From 2f74b709b637cad7a9c18a2d90b0747823f2ff51 Mon Sep 17 00:00:00 2001
From: Sam Gross
Date: Thu, 25 Jul 2024 04:16:53 -0400
Subject: [PATCH 01/47] gh-122187: Avoid TSan reported race in
`run_udp_echo_server` (#122189)
TSan doesn't fully recognize the synchronization via I/O, so ensure that
socket name is retrieved earlier and use a different socket for sending
the "STOP" message.
---
Lib/test/test_asyncio/utils.py | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/Lib/test/test_asyncio/utils.py b/Lib/test/test_asyncio/utils.py
index dbb8d27c176950..35893ab3118e1e 100644
--- a/Lib/test/test_asyncio/utils.py
+++ b/Lib/test/test_asyncio/utils.py
@@ -301,12 +301,17 @@ def run_udp_echo_server(*, host='127.0.0.1', port=0):
family, type, proto, _, sockaddr = addr_info[0]
sock = socket.socket(family, type, proto)
sock.bind((host, port))
+ sockname = sock.getsockname()
thread = threading.Thread(target=lambda: echo_datagrams(sock))
thread.start()
try:
- yield sock.getsockname()
+ yield sockname
finally:
- sock.sendto(b'STOP', sock.getsockname())
+ # gh-122187: use a separate socket to send the stop message to avoid
+ # TSan reported race on the same socket.
+ sock2 = socket.socket(family, type, proto)
+ sock2.sendto(b'STOP', sockname)
+ sock2.close()
thread.join()
From ca0f7c447c83503bd760dc2eb6d1ea4b3558f8e9 Mon Sep 17 00:00:00 2001
From: Nate Ohlson
Date: Thu, 25 Jul 2024 04:35:23 -0400
Subject: [PATCH 02/47] gh-112301: Make fortify source option check -Werror
(gh-122141)
---
configure | 14 +++++++-------
configure.ac | 2 +-
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/configure b/configure
index 7b3dfa71a2a192..52988f77f6d926 100755
--- a/configure
+++ b/configure
@@ -9788,13 +9788,13 @@ if test "$enable_slower_safety" = "yes"
then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -D_FORTIFY_SOURCE=3" >&5
printf %s "checking whether C compiler accepts -D_FORTIFY_SOURCE=3... " >&6; }
-if test ${ax_cv_check_cflags___D_FORTIFY_SOURCE_3+y}
+if test ${ax_cv_check_cflags__Werror__D_FORTIFY_SOURCE_3+y}
then :
printf %s "(cached) " >&6
else $as_nop
ax_check_save_flags=$CFLAGS
- CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=3"
+ CFLAGS="$CFLAGS -Werror -D_FORTIFY_SOURCE=3"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -9808,16 +9808,16 @@ main (void)
_ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
- ax_cv_check_cflags___D_FORTIFY_SOURCE_3=yes
+ ax_cv_check_cflags__Werror__D_FORTIFY_SOURCE_3=yes
else $as_nop
- ax_cv_check_cflags___D_FORTIFY_SOURCE_3=no
+ ax_cv_check_cflags__Werror__D_FORTIFY_SOURCE_3=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
CFLAGS=$ax_check_save_flags
fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___D_FORTIFY_SOURCE_3" >&5
-printf "%s\n" "$ax_cv_check_cflags___D_FORTIFY_SOURCE_3" >&6; }
-if test "x$ax_cv_check_cflags___D_FORTIFY_SOURCE_3" = xyes
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__D_FORTIFY_SOURCE_3" >&5
+printf "%s\n" "$ax_cv_check_cflags__Werror__D_FORTIFY_SOURCE_3" >&6; }
+if test "x$ax_cv_check_cflags__Werror__D_FORTIFY_SOURCE_3" = xyes
then :
BASECFLAGS="$BASECFLAGS -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3"
else $as_nop
diff --git a/configure.ac b/configure.ac
index 1275c199a7cf1c..5bde6803cd5a7b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2519,7 +2519,7 @@ AC_MSG_RESULT([$enable_slower_safety])
if test "$enable_slower_safety" = "yes"
then
- AX_CHECK_COMPILE_FLAG([-D_FORTIFY_SOURCE=3], [BASECFLAGS="$BASECFLAGS -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3"], [AC_MSG_WARN([-D_FORTIFY_SOURCE=3 not supported])])
+ AX_CHECK_COMPILE_FLAG([-D_FORTIFY_SOURCE=3], [BASECFLAGS="$BASECFLAGS -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3"], [AC_MSG_WARN([-D_FORTIFY_SOURCE=3 not supported])], [-Werror])
fi
case $GCC in
From dc07f65a53baf60d9857186294d3d7ba92d5606d Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka
Date: Thu, 25 Jul 2024 11:45:19 +0300
Subject: [PATCH 03/47] gh-82951: Fix serializing by name in pickle protocols <
4 (GH-122149)
Serializing objects with complex __qualname__ (such as unbound methods and
nested classes) by name no longer involves serializing parent objects by value
in pickle protocols < 4.
---
Lib/pickle.py | 40 ++++++++++----
Lib/test/pickletester.py | 12 +++++
...4-07-23-09-14-44.gh-issue-82951.-F5p5A.rst | 3 ++
Modules/_pickle.c | 53 +++++++++++++------
4 files changed, 82 insertions(+), 26 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2024-07-23-09-14-44.gh-issue-82951.-F5p5A.rst
diff --git a/Lib/pickle.py b/Lib/pickle.py
index 115bd893ca1a38..2d764980cdf7b2 100644
--- a/Lib/pickle.py
+++ b/Lib/pickle.py
@@ -1110,11 +1110,35 @@ def save_global(self, obj, name=None):
self.save(module_name)
self.save(name)
write(STACK_GLOBAL)
- elif parent is not module:
- self.save_reduce(getattr, (parent, lastname))
- elif self.proto >= 3:
- write(GLOBAL + bytes(module_name, "utf-8") + b'\n' +
- bytes(name, "utf-8") + b'\n')
+ elif '.' in name:
+ # In protocol < 4, objects with multi-part __qualname__
+ # are represented as
+ # getattr(getattr(..., attrname1), attrname2).
+ dotted_path = name.split('.')
+ name = dotted_path.pop(0)
+ save = self.save
+ for attrname in dotted_path:
+ save(getattr)
+ if self.proto < 2:
+ write(MARK)
+ self._save_toplevel_by_name(module_name, name)
+ for attrname in dotted_path:
+ save(attrname)
+ if self.proto < 2:
+ write(TUPLE)
+ else:
+ write(TUPLE2)
+ write(REDUCE)
+ else:
+ self._save_toplevel_by_name(module_name, name)
+
+ self.memoize(obj)
+
+ def _save_toplevel_by_name(self, module_name, name):
+ if self.proto >= 3:
+ # Non-ASCII identifiers are supported only with protocols >= 3.
+ self.write(GLOBAL + bytes(module_name, "utf-8") + b'\n' +
+ bytes(name, "utf-8") + b'\n')
else:
if self.fix_imports:
r_name_mapping = _compat_pickle.REVERSE_NAME_MAPPING
@@ -1124,15 +1148,13 @@ def save_global(self, obj, name=None):
elif module_name in r_import_mapping:
module_name = r_import_mapping[module_name]
try:
- write(GLOBAL + bytes(module_name, "ascii") + b'\n' +
- bytes(name, "ascii") + b'\n')
+ self.write(GLOBAL + bytes(module_name, "ascii") + b'\n' +
+ bytes(name, "ascii") + b'\n')
except UnicodeEncodeError:
raise PicklingError(
"can't pickle global identifier '%s.%s' using "
"pickle protocol %i" % (module, name, self.proto)) from None
- self.memoize(obj)
-
def save_type(self, obj):
if obj is type(None):
return self.save_reduce(type, (None,), obj=obj)
diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py
index 9922591ce7114a..13663220fc77ea 100644
--- a/Lib/test/pickletester.py
+++ b/Lib/test/pickletester.py
@@ -2818,6 +2818,18 @@ class Recursive:
self.assertIs(unpickled, Recursive)
del Recursive.mod # break reference loop
+ def test_recursive_nested_names2(self):
+ global Recursive
+ class Recursive:
+ pass
+ Recursive.ref = Recursive
+ Recursive.__qualname__ = 'Recursive.ref'
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ with self.subTest(proto=proto):
+ unpickled = self.loads(self.dumps(Recursive, proto))
+ self.assertIs(unpickled, Recursive)
+ del Recursive.ref # break reference loop
+
def test_py_methods(self):
global PyMethodsTest
class PyMethodsTest:
diff --git a/Misc/NEWS.d/next/Library/2024-07-23-09-14-44.gh-issue-82951.-F5p5A.rst b/Misc/NEWS.d/next/Library/2024-07-23-09-14-44.gh-issue-82951.-F5p5A.rst
new file mode 100644
index 00000000000000..b3f07889119c9f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-07-23-09-14-44.gh-issue-82951.-F5p5A.rst
@@ -0,0 +1,3 @@
+Serializing objects with complex ``__qualname__`` (such as unbound methods
+and nested classes) by name no longer involves serializing parent objects by
+value in pickle protocols < 4.
diff --git a/Modules/_pickle.c b/Modules/_pickle.c
index 7eebe922c93ca1..861363b68c20c5 100644
--- a/Modules/_pickle.c
+++ b/Modules/_pickle.c
@@ -3592,7 +3592,6 @@ save_global(PickleState *st, PicklerObject *self, PyObject *obj,
PyObject *module = NULL;
PyObject *parent = NULL;
PyObject *dotted_path = NULL;
- PyObject *lastname = NULL;
PyObject *cls;
int status = 0;
@@ -3633,10 +3632,7 @@ save_global(PickleState *st, PicklerObject *self, PyObject *obj,
obj, module_name);
goto error;
}
- lastname = Py_NewRef(PyList_GET_ITEM(dotted_path,
- PyList_GET_SIZE(dotted_path) - 1));
cls = get_deep_attribute(module, dotted_path, &parent);
- Py_CLEAR(dotted_path);
if (cls == NULL) {
PyErr_Format(st->PicklingError,
"Can't pickle %R: attribute lookup %S on %S failed",
@@ -3724,7 +3720,10 @@ save_global(PickleState *st, PicklerObject *self, PyObject *obj,
else {
gen_global:
if (parent == module) {
- Py_SETREF(global_name, Py_NewRef(lastname));
+ Py_SETREF(global_name,
+ Py_NewRef(PyList_GET_ITEM(dotted_path,
+ PyList_GET_SIZE(dotted_path) - 1)));
+ Py_CLEAR(dotted_path);
}
if (self->proto >= 4) {
const char stack_global_op = STACK_GLOBAL;
@@ -3737,20 +3736,30 @@ save_global(PickleState *st, PicklerObject *self, PyObject *obj,
if (_Pickler_Write(self, &stack_global_op, 1) < 0)
goto error;
}
- else if (parent != module) {
- PyObject *reduce_value = Py_BuildValue("(O(OO))",
- st->getattr, parent, lastname);
- if (reduce_value == NULL)
- goto error;
- status = save_reduce(st, self, reduce_value, NULL);
- Py_DECREF(reduce_value);
- if (status < 0)
- goto error;
- }
else {
/* Generate a normal global opcode if we are using a pickle
protocol < 4, or if the object is not registered in the
- extension registry. */
+ extension registry.
+
+ Objects with multi-part __qualname__ are represented as
+ getattr(getattr(..., attrname1), attrname2). */
+ const char mark_op = MARK;
+ const char tupletwo_op = (self->proto < 2) ? TUPLE : TUPLE2;
+ const char reduce_op = REDUCE;
+ Py_ssize_t i;
+ if (dotted_path) {
+ if (PyList_GET_SIZE(dotted_path) > 1) {
+ Py_SETREF(global_name, Py_NewRef(PyList_GET_ITEM(dotted_path, 0)));
+ }
+ for (i = 1; i < PyList_GET_SIZE(dotted_path); i++) {
+ if (save(st, self, st->getattr, 0) < 0 ||
+ (self->proto < 2 && _Pickler_Write(self, &mark_op, 1) < 0))
+ {
+ goto error;
+ }
+ }
+ }
+
PyObject *encoded;
PyObject *(*unicode_encoder)(PyObject *);
@@ -3812,6 +3821,17 @@ save_global(PickleState *st, PicklerObject *self, PyObject *obj,
Py_DECREF(encoded);
if (_Pickler_Write(self, "\n", 1) < 0)
goto error;
+
+ if (dotted_path) {
+ for (i = 1; i < PyList_GET_SIZE(dotted_path); i++) {
+ if (save(st, self, PyList_GET_ITEM(dotted_path, i), 0) < 0 ||
+ _Pickler_Write(self, &tupletwo_op, 1) < 0 ||
+ _Pickler_Write(self, &reduce_op, 1) < 0)
+ {
+ goto error;
+ }
+ }
+ }
}
/* Memoize the object. */
if (memo_put(st, self, obj) < 0)
@@ -3827,7 +3847,6 @@ save_global(PickleState *st, PicklerObject *self, PyObject *obj,
Py_XDECREF(module);
Py_XDECREF(parent);
Py_XDECREF(dotted_path);
- Py_XDECREF(lastname);
return status;
}
From 6c09b8de5c67406113e8d082e05c9587e35a852a Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka
Date: Thu, 25 Jul 2024 14:04:22 +0300
Subject: [PATCH 04/47] gh-122270: Fix typos in the Py_DEBUG macro name
(GH-122271)
---
Parser/pegen.c | 2 +-
Tools/peg_generator/peg_extension/peg_extension.c | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/Parser/pegen.c b/Parser/pegen.c
index 6efb5477c7b80f..ac428be0958bdf 100644
--- a/Parser/pegen.c
+++ b/Parser/pegen.c
@@ -341,7 +341,7 @@ _PyPegen_is_memoized(Parser *p, int type, void *pres)
for (Memo *m = t->memo; m != NULL; m = m->next) {
if (m->type == type) {
-#if defined(PY_DEBUG)
+#if defined(Py_DEBUG)
if (0 <= type && type < NSTATISTICS) {
long count = m->mark - p->mark;
// A memoized negative result counts for one.
diff --git a/Tools/peg_generator/peg_extension/peg_extension.c b/Tools/peg_generator/peg_extension/peg_extension.c
index b081240ffff017..1587d53d59472e 100644
--- a/Tools/peg_generator/peg_extension/peg_extension.c
+++ b/Tools/peg_generator/peg_extension/peg_extension.c
@@ -108,7 +108,7 @@ parse_string(PyObject *self, PyObject *args, PyObject *kwds)
static PyObject *
clear_memo_stats(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored))
{
-#if defined(PY_DEBUG)
+#if defined(Py_DEBUG)
_PyPegen_clear_memo_statistics();
#endif
Py_RETURN_NONE;
@@ -117,7 +117,7 @@ clear_memo_stats(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored))
static PyObject *
get_memo_stats(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored))
{
-#if defined(PY_DEBUG)
+#if defined(Py_DEBUG)
return _PyPegen_get_memo_statistics();
#else
Py_RETURN_NONE;
@@ -128,7 +128,7 @@ get_memo_stats(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored))
static PyObject *
dump_memo_stats(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored))
{
-#if defined(PY_DEBUG)
+#if defined(Py_DEBUG)
PyObject *list = _PyPegen_get_memo_statistics();
if (list == NULL) {
return NULL;
From 070f1e2e5b9b31ee3e7a1af2e30d7e3a66040b17 Mon Sep 17 00:00:00 2001
From: AN Long
Date: Thu, 25 Jul 2024 19:56:04 +0800
Subject: [PATCH 05/47] gh-121913: Use str(exc) instead of exc.strerror in
`asyncio.base_events` (#122269)
---
Lib/asyncio/base_events.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py
index f0e690b61a73dd..e4a39f4d345c79 100644
--- a/Lib/asyncio/base_events.py
+++ b/Lib/asyncio/base_events.py
@@ -1028,8 +1028,7 @@ async def _connect_sock(self, exceptions, addr_info, local_addr_infos=None):
except OSError as exc:
msg = (
f'error while attempting to bind on '
- f'address {laddr!r}: '
- f'{exc.strerror.lower()}'
+ f'address {laddr!r}: {str(exc).lower()}'
)
exc = OSError(exc.errno, msg)
my_exceptions.append(exc)
@@ -1599,7 +1598,7 @@ async def create_server(
except OSError as err:
msg = ('error while attempting '
'to bind on address %r: %s'
- % (sa, err.strerror.lower()))
+ % (sa, str(err).lower()))
if err.errno == errno.EADDRNOTAVAIL:
# Assume the family is not enabled (bpo-30945)
sockets.pop()
From 3998554bb05f5ce18e8a66492d23d094a2299442 Mon Sep 17 00:00:00 2001
From: AN Long
Date: Thu, 25 Jul 2024 20:03:39 +0800
Subject: [PATCH 06/47] gh-121275: Fix test_logging and test_smtplib with
Python build withoud IPv6 support (#121276)
Fix test_logging and test_smtplib with Python build withoud IPv6 support
---
Lib/test/support/smtpd.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Lib/test/support/smtpd.py b/Lib/test/support/smtpd.py
index c2e17cad422861..6537679db9ad24 100755
--- a/Lib/test/support/smtpd.py
+++ b/Lib/test/support/smtpd.py
@@ -633,7 +633,8 @@ def __init__(self, localaddr, remoteaddr,
" be set to True at the same time")
asyncore.dispatcher.__init__(self, map=map)
try:
- gai_results = socket.getaddrinfo(*localaddr,
+ family = 0 if socket.has_ipv6 else socket.AF_INET
+ gai_results = socket.getaddrinfo(*localaddr, family=family,
type=socket.SOCK_STREAM)
self.create_socket(gai_results[0][0], gai_results[0][1])
# try to re-use a server port if possible
From 9bb2e4623f504c44655436eae181d802f544fff9 Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka
Date: Thu, 25 Jul 2024 17:31:57 +0300
Subject: [PATCH 07/47] gh-116322: Fix typo in the #ifdef check (#122268)
---
Objects/moduleobject.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c
index 73ad9711b6b0fc..efc74dafb5fc73 100644
--- a/Objects/moduleobject.c
+++ b/Objects/moduleobject.c
@@ -251,7 +251,7 @@ _PyModule_CreateInitialized(PyModuleDef* module, int module_api_version)
}
}
m->md_def = module;
-#ifdef Py_GIL_DISABLE
+#ifdef Py_GIL_DISABLED
m->md_gil = Py_MOD_GIL_USED;
#endif
return (PyObject*)m;
From 2e14a52cced9834ed5f7e0665a08055de554360f Mon Sep 17 00:00:00 2001
From: Mark Shannon
Date: Thu, 25 Jul 2024 16:24:29 +0100
Subject: [PATCH 08/47] GH-122160: Remove BUILD_CONST_KEY_MAP opcode.
(GH-122164)
---
Doc/library/dis.rst | 9 -
Include/internal/pycore_opcode_metadata.h | 9 +-
Include/internal/pycore_uop_ids.h | 1 -
Include/internal/pycore_uop_metadata.h | 4 -
Include/opcode_ids.h | 149 +++++-----
Lib/_opcode_metadata.py | 149 +++++-----
Lib/importlib/_bootstrap_external.py | 3 +-
Lib/test/test_dis.py | 274 +++++++++---------
...-07-23-11-57-36.gh-issue-122160.HSnrAP.rst | 1 +
Programs/test_frozenmain.h | 22 +-
Python/bytecodes.c | 19 --
Python/compile.c | 63 +---
Python/executor_cases.c.h | 34 ---
Python/generated_cases.c.h | 36 ---
Python/opcode_targets.h | 2 +-
Python/optimizer_cases.c.h | 9 -
16 files changed, 306 insertions(+), 478 deletions(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-07-23-11-57-36.gh-issue-122160.HSnrAP.rst
diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index 56712e294bbe54..26b13c87181000 100644
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -1109,15 +1109,6 @@ iterations of the loop.
empty dictionary pre-sized to hold *count* items.
-.. opcode:: BUILD_CONST_KEY_MAP (count)
-
- The version of :opcode:`BUILD_MAP` specialized for constant keys. Pops the
- top element on the stack which contains a tuple of keys, then starting from
- ``STACK[-2]``, pops *count* values to form values in the built dictionary.
-
- .. versionadded:: 3.6
-
-
.. opcode:: BUILD_STRING (count)
Concatenates *count* strings from the stack and pushes the resulting string
diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h
index 40e582a5e94c3b..2b6e9bca51c9df 100644
--- a/Include/internal/pycore_opcode_metadata.h
+++ b/Include/internal/pycore_opcode_metadata.h
@@ -65,8 +65,6 @@ int _PyOpcode_num_popped(int opcode, int oparg) {
return 2;
case BINARY_SUBSCR_TUPLE_INT:
return 2;
- case BUILD_CONST_KEY_MAP:
- return 1 + oparg;
case BUILD_LIST:
return oparg;
case BUILD_MAP:
@@ -512,8 +510,6 @@ int _PyOpcode_num_pushed(int opcode, int oparg) {
return 1;
case BINARY_SUBSCR_TUPLE_INT:
return 1;
- case BUILD_CONST_KEY_MAP:
- return 1;
case BUILD_LIST:
return 1;
case BUILD_MAP:
@@ -1004,7 +1000,6 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[264] = {
[BINARY_SUBSCR_LIST_INT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG },
[BINARY_SUBSCR_STR_INT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG },
[BINARY_SUBSCR_TUPLE_INT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG },
- [BUILD_CONST_KEY_MAP] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[BUILD_LIST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG },
[BUILD_MAP] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[BUILD_SET] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
@@ -1233,7 +1228,6 @@ _PyOpcode_macro_expansion[256] = {
[BINARY_SUBSCR_LIST_INT] = { .nuops = 1, .uops = { { _BINARY_SUBSCR_LIST_INT, 0, 0 } } },
[BINARY_SUBSCR_STR_INT] = { .nuops = 1, .uops = { { _BINARY_SUBSCR_STR_INT, 0, 0 } } },
[BINARY_SUBSCR_TUPLE_INT] = { .nuops = 1, .uops = { { _BINARY_SUBSCR_TUPLE_INT, 0, 0 } } },
- [BUILD_CONST_KEY_MAP] = { .nuops = 1, .uops = { { _BUILD_CONST_KEY_MAP, 0, 0 } } },
[BUILD_LIST] = { .nuops = 1, .uops = { { _BUILD_LIST, 0, 0 } } },
[BUILD_MAP] = { .nuops = 1, .uops = { { _BUILD_MAP, 0, 0 } } },
[BUILD_SET] = { .nuops = 1, .uops = { { _BUILD_SET, 0, 0 } } },
@@ -1409,7 +1403,6 @@ const char *_PyOpcode_OpName[264] = {
[BINARY_SUBSCR_LIST_INT] = "BINARY_SUBSCR_LIST_INT",
[BINARY_SUBSCR_STR_INT] = "BINARY_SUBSCR_STR_INT",
[BINARY_SUBSCR_TUPLE_INT] = "BINARY_SUBSCR_TUPLE_INT",
- [BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP",
[BUILD_LIST] = "BUILD_LIST",
[BUILD_MAP] = "BUILD_MAP",
[BUILD_SET] = "BUILD_SET",
@@ -1659,7 +1652,6 @@ const uint8_t _PyOpcode_Deopt[256] = {
[BINARY_SUBSCR_LIST_INT] = BINARY_SUBSCR,
[BINARY_SUBSCR_STR_INT] = BINARY_SUBSCR,
[BINARY_SUBSCR_TUPLE_INT] = BINARY_SUBSCR,
- [BUILD_CONST_KEY_MAP] = BUILD_CONST_KEY_MAP,
[BUILD_LIST] = BUILD_LIST,
[BUILD_MAP] = BUILD_MAP,
[BUILD_SET] = BUILD_SET,
@@ -1859,6 +1851,7 @@ const uint8_t _PyOpcode_Deopt[256] = {
#endif // NEED_OPCODE_METADATA
#define EXTRA_CASES \
+ case 117: \
case 118: \
case 119: \
case 120: \
diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h
index aa7ee7775faeba..fc67da697cb06c 100644
--- a/Include/internal/pycore_uop_ids.h
+++ b/Include/internal/pycore_uop_ids.h
@@ -26,7 +26,6 @@ extern "C" {
#define _BINARY_SUBSCR_LIST_INT BINARY_SUBSCR_LIST_INT
#define _BINARY_SUBSCR_STR_INT BINARY_SUBSCR_STR_INT
#define _BINARY_SUBSCR_TUPLE_INT BINARY_SUBSCR_TUPLE_INT
-#define _BUILD_CONST_KEY_MAP BUILD_CONST_KEY_MAP
#define _BUILD_LIST BUILD_LIST
#define _BUILD_MAP BUILD_MAP
#define _BUILD_SET BUILD_SET
diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h
index 4c18f66d7420af..e86bae1d72d1a9 100644
--- a/Include/internal/pycore_uop_metadata.h
+++ b/Include/internal/pycore_uop_metadata.h
@@ -129,7 +129,6 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
[_BUILD_SET] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
[_BUILD_MAP] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
[_SETUP_ANNOTATIONS] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
- [_BUILD_CONST_KEY_MAP] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
[_DICT_UPDATE] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
[_DICT_MERGE] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
[_MAP_ADD] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
@@ -289,7 +288,6 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
[_BINARY_SUBSCR_LIST_INT] = "_BINARY_SUBSCR_LIST_INT",
[_BINARY_SUBSCR_STR_INT] = "_BINARY_SUBSCR_STR_INT",
[_BINARY_SUBSCR_TUPLE_INT] = "_BINARY_SUBSCR_TUPLE_INT",
- [_BUILD_CONST_KEY_MAP] = "_BUILD_CONST_KEY_MAP",
[_BUILD_LIST] = "_BUILD_LIST",
[_BUILD_MAP] = "_BUILD_MAP",
[_BUILD_SET] = "_BUILD_SET",
@@ -746,8 +744,6 @@ int _PyUop_num_popped(int opcode, int oparg)
return oparg*2;
case _SETUP_ANNOTATIONS:
return 0;
- case _BUILD_CONST_KEY_MAP:
- return 1 + oparg;
case _DICT_UPDATE:
return 2 + (oparg - 1);
case _DICT_MERGE:
diff --git a/Include/opcode_ids.h b/Include/opcode_ids.h
index 2ae6e5c1ba51ec..dd9b1ec1674949 100644
--- a/Include/opcode_ids.h
+++ b/Include/opcode_ids.h
@@ -53,81 +53,80 @@ extern "C" {
#define UNARY_NOT 40
#define WITH_EXCEPT_START 41
#define BINARY_OP 42
-#define BUILD_CONST_KEY_MAP 43
-#define BUILD_LIST 44
-#define BUILD_MAP 45
-#define BUILD_SET 46
-#define BUILD_SLICE 47
-#define BUILD_STRING 48
-#define BUILD_TUPLE 49
-#define CALL 50
-#define CALL_FUNCTION_EX 51
-#define CALL_INTRINSIC_1 52
-#define CALL_INTRINSIC_2 53
-#define CALL_KW 54
-#define COMPARE_OP 55
-#define CONTAINS_OP 56
-#define CONVERT_VALUE 57
-#define COPY 58
-#define COPY_FREE_VARS 59
-#define DELETE_ATTR 60
-#define DELETE_DEREF 61
-#define DELETE_FAST 62
-#define DELETE_GLOBAL 63
-#define DELETE_NAME 64
-#define DICT_MERGE 65
-#define DICT_UPDATE 66
-#define ENTER_EXECUTOR 67
-#define EXTENDED_ARG 68
-#define FOR_ITER 69
-#define GET_AWAITABLE 70
-#define IMPORT_FROM 71
-#define IMPORT_NAME 72
-#define IS_OP 73
-#define JUMP_BACKWARD 74
-#define JUMP_BACKWARD_NO_INTERRUPT 75
-#define JUMP_FORWARD 76
-#define LIST_APPEND 77
-#define LIST_EXTEND 78
-#define LOAD_ATTR 79
-#define LOAD_COMMON_CONSTANT 80
-#define LOAD_CONST 81
-#define LOAD_DEREF 82
-#define LOAD_FAST 83
-#define LOAD_FAST_AND_CLEAR 84
-#define LOAD_FAST_CHECK 85
-#define LOAD_FAST_LOAD_FAST 86
-#define LOAD_FROM_DICT_OR_DEREF 87
-#define LOAD_FROM_DICT_OR_GLOBALS 88
-#define LOAD_GLOBAL 89
-#define LOAD_NAME 90
-#define LOAD_SPECIAL 91
-#define LOAD_SUPER_ATTR 92
-#define MAKE_CELL 93
-#define MAP_ADD 94
-#define MATCH_CLASS 95
-#define POP_JUMP_IF_FALSE 96
-#define POP_JUMP_IF_NONE 97
-#define POP_JUMP_IF_NOT_NONE 98
-#define POP_JUMP_IF_TRUE 99
-#define RAISE_VARARGS 100
-#define RERAISE 101
-#define RETURN_CONST 102
-#define SEND 103
-#define SET_ADD 104
-#define SET_FUNCTION_ATTRIBUTE 105
-#define SET_UPDATE 106
-#define STORE_ATTR 107
-#define STORE_DEREF 108
-#define STORE_FAST 109
-#define STORE_FAST_LOAD_FAST 110
-#define STORE_FAST_STORE_FAST 111
-#define STORE_GLOBAL 112
-#define STORE_NAME 113
-#define SWAP 114
-#define UNPACK_EX 115
-#define UNPACK_SEQUENCE 116
-#define YIELD_VALUE 117
+#define BUILD_LIST 43
+#define BUILD_MAP 44
+#define BUILD_SET 45
+#define BUILD_SLICE 46
+#define BUILD_STRING 47
+#define BUILD_TUPLE 48
+#define CALL 49
+#define CALL_FUNCTION_EX 50
+#define CALL_INTRINSIC_1 51
+#define CALL_INTRINSIC_2 52
+#define CALL_KW 53
+#define COMPARE_OP 54
+#define CONTAINS_OP 55
+#define CONVERT_VALUE 56
+#define COPY 57
+#define COPY_FREE_VARS 58
+#define DELETE_ATTR 59
+#define DELETE_DEREF 60
+#define DELETE_FAST 61
+#define DELETE_GLOBAL 62
+#define DELETE_NAME 63
+#define DICT_MERGE 64
+#define DICT_UPDATE 65
+#define ENTER_EXECUTOR 66
+#define EXTENDED_ARG 67
+#define FOR_ITER 68
+#define GET_AWAITABLE 69
+#define IMPORT_FROM 70
+#define IMPORT_NAME 71
+#define IS_OP 72
+#define JUMP_BACKWARD 73
+#define JUMP_BACKWARD_NO_INTERRUPT 74
+#define JUMP_FORWARD 75
+#define LIST_APPEND 76
+#define LIST_EXTEND 77
+#define LOAD_ATTR 78
+#define LOAD_COMMON_CONSTANT 79
+#define LOAD_CONST 80
+#define LOAD_DEREF 81
+#define LOAD_FAST 82
+#define LOAD_FAST_AND_CLEAR 83
+#define LOAD_FAST_CHECK 84
+#define LOAD_FAST_LOAD_FAST 85
+#define LOAD_FROM_DICT_OR_DEREF 86
+#define LOAD_FROM_DICT_OR_GLOBALS 87
+#define LOAD_GLOBAL 88
+#define LOAD_NAME 89
+#define LOAD_SPECIAL 90
+#define LOAD_SUPER_ATTR 91
+#define MAKE_CELL 92
+#define MAP_ADD 93
+#define MATCH_CLASS 94
+#define POP_JUMP_IF_FALSE 95
+#define POP_JUMP_IF_NONE 96
+#define POP_JUMP_IF_NOT_NONE 97
+#define POP_JUMP_IF_TRUE 98
+#define RAISE_VARARGS 99
+#define RERAISE 100
+#define RETURN_CONST 101
+#define SEND 102
+#define SET_ADD 103
+#define SET_FUNCTION_ATTRIBUTE 104
+#define SET_UPDATE 105
+#define STORE_ATTR 106
+#define STORE_DEREF 107
+#define STORE_FAST 108
+#define STORE_FAST_LOAD_FAST 109
+#define STORE_FAST_STORE_FAST 110
+#define STORE_GLOBAL 111
+#define STORE_NAME 112
+#define SWAP 113
+#define UNPACK_EX 114
+#define UNPACK_SEQUENCE 115
+#define YIELD_VALUE 116
#define RESUME 149
#define BINARY_OP_ADD_FLOAT 150
#define BINARY_OP_ADD_INT 151
diff --git a/Lib/_opcode_metadata.py b/Lib/_opcode_metadata.py
index 2d0a5ba46b3e10..4b6057f4119421 100644
--- a/Lib/_opcode_metadata.py
+++ b/Lib/_opcode_metadata.py
@@ -231,81 +231,80 @@
'UNARY_NOT': 40,
'WITH_EXCEPT_START': 41,
'BINARY_OP': 42,
- 'BUILD_CONST_KEY_MAP': 43,
- 'BUILD_LIST': 44,
- 'BUILD_MAP': 45,
- 'BUILD_SET': 46,
- 'BUILD_SLICE': 47,
- 'BUILD_STRING': 48,
- 'BUILD_TUPLE': 49,
- 'CALL': 50,
- 'CALL_FUNCTION_EX': 51,
- 'CALL_INTRINSIC_1': 52,
- 'CALL_INTRINSIC_2': 53,
- 'CALL_KW': 54,
- 'COMPARE_OP': 55,
- 'CONTAINS_OP': 56,
- 'CONVERT_VALUE': 57,
- 'COPY': 58,
- 'COPY_FREE_VARS': 59,
- 'DELETE_ATTR': 60,
- 'DELETE_DEREF': 61,
- 'DELETE_FAST': 62,
- 'DELETE_GLOBAL': 63,
- 'DELETE_NAME': 64,
- 'DICT_MERGE': 65,
- 'DICT_UPDATE': 66,
- 'ENTER_EXECUTOR': 67,
- 'EXTENDED_ARG': 68,
- 'FOR_ITER': 69,
- 'GET_AWAITABLE': 70,
- 'IMPORT_FROM': 71,
- 'IMPORT_NAME': 72,
- 'IS_OP': 73,
- 'JUMP_BACKWARD': 74,
- 'JUMP_BACKWARD_NO_INTERRUPT': 75,
- 'JUMP_FORWARD': 76,
- 'LIST_APPEND': 77,
- 'LIST_EXTEND': 78,
- 'LOAD_ATTR': 79,
- 'LOAD_COMMON_CONSTANT': 80,
- 'LOAD_CONST': 81,
- 'LOAD_DEREF': 82,
- 'LOAD_FAST': 83,
- 'LOAD_FAST_AND_CLEAR': 84,
- 'LOAD_FAST_CHECK': 85,
- 'LOAD_FAST_LOAD_FAST': 86,
- 'LOAD_FROM_DICT_OR_DEREF': 87,
- 'LOAD_FROM_DICT_OR_GLOBALS': 88,
- 'LOAD_GLOBAL': 89,
- 'LOAD_NAME': 90,
- 'LOAD_SPECIAL': 91,
- 'LOAD_SUPER_ATTR': 92,
- 'MAKE_CELL': 93,
- 'MAP_ADD': 94,
- 'MATCH_CLASS': 95,
- 'POP_JUMP_IF_FALSE': 96,
- 'POP_JUMP_IF_NONE': 97,
- 'POP_JUMP_IF_NOT_NONE': 98,
- 'POP_JUMP_IF_TRUE': 99,
- 'RAISE_VARARGS': 100,
- 'RERAISE': 101,
- 'RETURN_CONST': 102,
- 'SEND': 103,
- 'SET_ADD': 104,
- 'SET_FUNCTION_ATTRIBUTE': 105,
- 'SET_UPDATE': 106,
- 'STORE_ATTR': 107,
- 'STORE_DEREF': 108,
- 'STORE_FAST': 109,
- 'STORE_FAST_LOAD_FAST': 110,
- 'STORE_FAST_STORE_FAST': 111,
- 'STORE_GLOBAL': 112,
- 'STORE_NAME': 113,
- 'SWAP': 114,
- 'UNPACK_EX': 115,
- 'UNPACK_SEQUENCE': 116,
- 'YIELD_VALUE': 117,
+ 'BUILD_LIST': 43,
+ 'BUILD_MAP': 44,
+ 'BUILD_SET': 45,
+ 'BUILD_SLICE': 46,
+ 'BUILD_STRING': 47,
+ 'BUILD_TUPLE': 48,
+ 'CALL': 49,
+ 'CALL_FUNCTION_EX': 50,
+ 'CALL_INTRINSIC_1': 51,
+ 'CALL_INTRINSIC_2': 52,
+ 'CALL_KW': 53,
+ 'COMPARE_OP': 54,
+ 'CONTAINS_OP': 55,
+ 'CONVERT_VALUE': 56,
+ 'COPY': 57,
+ 'COPY_FREE_VARS': 58,
+ 'DELETE_ATTR': 59,
+ 'DELETE_DEREF': 60,
+ 'DELETE_FAST': 61,
+ 'DELETE_GLOBAL': 62,
+ 'DELETE_NAME': 63,
+ 'DICT_MERGE': 64,
+ 'DICT_UPDATE': 65,
+ 'ENTER_EXECUTOR': 66,
+ 'EXTENDED_ARG': 67,
+ 'FOR_ITER': 68,
+ 'GET_AWAITABLE': 69,
+ 'IMPORT_FROM': 70,
+ 'IMPORT_NAME': 71,
+ 'IS_OP': 72,
+ 'JUMP_BACKWARD': 73,
+ 'JUMP_BACKWARD_NO_INTERRUPT': 74,
+ 'JUMP_FORWARD': 75,
+ 'LIST_APPEND': 76,
+ 'LIST_EXTEND': 77,
+ 'LOAD_ATTR': 78,
+ 'LOAD_COMMON_CONSTANT': 79,
+ 'LOAD_CONST': 80,
+ 'LOAD_DEREF': 81,
+ 'LOAD_FAST': 82,
+ 'LOAD_FAST_AND_CLEAR': 83,
+ 'LOAD_FAST_CHECK': 84,
+ 'LOAD_FAST_LOAD_FAST': 85,
+ 'LOAD_FROM_DICT_OR_DEREF': 86,
+ 'LOAD_FROM_DICT_OR_GLOBALS': 87,
+ 'LOAD_GLOBAL': 88,
+ 'LOAD_NAME': 89,
+ 'LOAD_SPECIAL': 90,
+ 'LOAD_SUPER_ATTR': 91,
+ 'MAKE_CELL': 92,
+ 'MAP_ADD': 93,
+ 'MATCH_CLASS': 94,
+ 'POP_JUMP_IF_FALSE': 95,
+ 'POP_JUMP_IF_NONE': 96,
+ 'POP_JUMP_IF_NOT_NONE': 97,
+ 'POP_JUMP_IF_TRUE': 98,
+ 'RAISE_VARARGS': 99,
+ 'RERAISE': 100,
+ 'RETURN_CONST': 101,
+ 'SEND': 102,
+ 'SET_ADD': 103,
+ 'SET_FUNCTION_ATTRIBUTE': 104,
+ 'SET_UPDATE': 105,
+ 'STORE_ATTR': 106,
+ 'STORE_DEREF': 107,
+ 'STORE_FAST': 108,
+ 'STORE_FAST_LOAD_FAST': 109,
+ 'STORE_FAST_STORE_FAST': 110,
+ 'STORE_GLOBAL': 111,
+ 'STORE_NAME': 112,
+ 'SWAP': 113,
+ 'UNPACK_EX': 114,
+ 'UNPACK_SEQUENCE': 115,
+ 'YIELD_VALUE': 116,
'INSTRUMENTED_RESUME': 236,
'INSTRUMENTED_END_FOR': 237,
'INSTRUMENTED_END_SEND': 238,
diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py
index bf14d57b2503ea..2bb44b290e4a84 100644
--- a/Lib/importlib/_bootstrap_external.py
+++ b/Lib/importlib/_bootstrap_external.py
@@ -475,6 +475,7 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.14a1 3600 (Add LOAD_COMMON_CONSTANT)
# Python 3.14a1 3601 (Fix miscompilation of private names in generic classes)
# Python 3.14a1 3602 (Add LOAD_SPECIAL. Remove BEFORE_WITH and BEFORE_ASYNC_WITH)
+# Python 3.14a1 3603 (Remove BUILD_CONST_KEY_MAP)
# Python 3.15 will start with 3650
@@ -491,7 +492,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.
-MAGIC_NUMBER = (3602).to_bytes(2, 'little') + b'\r\n'
+MAGIC_NUMBER = (3603).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index ab1c48f9b25361..c8defde7b99ec7 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -1605,204 +1605,204 @@ def _prepare_test_cases():
Instruction = dis.Instruction
expected_opinfo_outer = [
- Instruction(opname='MAKE_CELL', opcode=93, arg=0, argval='a', argrepr='a', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None),
- Instruction(opname='MAKE_CELL', opcode=93, arg=1, argval='b', argrepr='b', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
+ Instruction(opname='MAKE_CELL', opcode=92, arg=0, argval='a', argrepr='a', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None),
+ Instruction(opname='MAKE_CELL', opcode=92, arg=1, argval='b', argrepr='b', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=4, start_offset=4, starts_line=True, line_number=1, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=81, arg=5, argval=(3, 4), argrepr='(3, 4)', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='a', argrepr='a', offset=8, start_offset=8, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_FAST', opcode=83, arg=1, argval='b', argrepr='b', offset=10, start_offset=10, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
- Instruction(opname='BUILD_TUPLE', opcode=49, arg=2, argval=2, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=81, arg=1, argval=code_object_f, argrepr=repr(code_object_f), offset=14, start_offset=14, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=5, argval=(3, 4), argrepr='(3, 4)', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='a', argrepr='a', offset=8, start_offset=8, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_FAST', opcode=82, arg=1, argval='b', argrepr='b', offset=10, start_offset=10, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
+ Instruction(opname='BUILD_TUPLE', opcode=48, arg=2, argval=2, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=1, argval=code_object_f, argrepr=repr(code_object_f), offset=14, start_offset=14, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
Instruction(opname='MAKE_FUNCTION', opcode=23, arg=None, argval=None, argrepr='', offset=16, start_offset=16, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
- Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=105, arg=8, argval=8, argrepr='closure', offset=18, start_offset=18, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
- Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=105, arg=1, argval=1, argrepr='defaults', offset=20, start_offset=20, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
- Instruction(opname='STORE_FAST', opcode=109, arg=2, argval='f', argrepr='f', offset=22, start_offset=22, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_GLOBAL', opcode=89, arg=1, argval='print', argrepr='print + NULL', offset=24, start_offset=24, starts_line=True, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_DEREF', opcode=82, arg=0, argval='a', argrepr='a', offset=34, start_offset=34, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_DEREF', opcode=82, arg=1, argval='b', argrepr='b', offset=36, start_offset=36, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=81, arg=2, argval='', argrepr="''", offset=38, start_offset=38, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=1, argrepr='1', offset=40, start_offset=40, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
- Instruction(opname='BUILD_LIST', opcode=44, arg=0, argval=0, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
- Instruction(opname='BUILD_MAP', opcode=45, arg=0, argval=0, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=81, arg=4, argval='Hello world!', argrepr="'Hello world!'", offset=46, start_offset=46, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
- Instruction(opname='CALL', opcode=50, arg=7, argval=7, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
+ Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=104, arg=8, argval=8, argrepr='closure', offset=18, start_offset=18, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
+ Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=104, arg=1, argval=1, argrepr='defaults', offset=20, start_offset=20, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
+ Instruction(opname='STORE_FAST', opcode=108, arg=2, argval='f', argrepr='f', offset=22, start_offset=22, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_GLOBAL', opcode=88, arg=1, argval='print', argrepr='print + NULL', offset=24, start_offset=24, starts_line=True, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_DEREF', opcode=81, arg=0, argval='a', argrepr='a', offset=34, start_offset=34, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_DEREF', opcode=81, arg=1, argval='b', argrepr='b', offset=36, start_offset=36, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=2, argval='', argrepr="''", offset=38, start_offset=38, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=1, argrepr='1', offset=40, start_offset=40, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
+ Instruction(opname='BUILD_LIST', opcode=43, arg=0, argval=0, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
+ Instruction(opname='BUILD_MAP', opcode=44, arg=0, argval=0, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=4, argval='Hello world!', argrepr="'Hello world!'", offset=46, start_offset=46, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
+ Instruction(opname='CALL', opcode=49, arg=7, argval=7, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_FAST', opcode=83, arg=2, argval='f', argrepr='f', offset=58, start_offset=58, starts_line=True, line_number=8, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_FAST', opcode=82, arg=2, argval='f', argrepr='f', offset=58, start_offset=58, starts_line=True, line_number=8, label=None, positions=None, cache_info=None),
Instruction(opname='RETURN_VALUE', opcode=33, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=8, label=None, positions=None, cache_info=None),
]
expected_opinfo_f = [
- Instruction(opname='COPY_FREE_VARS', opcode=59, arg=2, argval=2, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None),
- Instruction(opname='MAKE_CELL', opcode=93, arg=0, argval='c', argrepr='c', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
- Instruction(opname='MAKE_CELL', opcode=93, arg=1, argval='d', argrepr='d', offset=4, start_offset=4, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
+ Instruction(opname='COPY_FREE_VARS', opcode=58, arg=2, argval=2, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None),
+ Instruction(opname='MAKE_CELL', opcode=92, arg=0, argval='c', argrepr='c', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
+ Instruction(opname='MAKE_CELL', opcode=92, arg=1, argval='d', argrepr='d', offset=4, start_offset=4, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=81, arg=2, argval=(5, 6), argrepr='(5, 6)', offset=8, start_offset=8, starts_line=True, line_number=3, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_FAST', opcode=83, arg=3, argval='a', argrepr='a', offset=10, start_offset=10, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_FAST', opcode=83, arg=4, argval='b', argrepr='b', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='c', argrepr='c', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_FAST', opcode=83, arg=1, argval='d', argrepr='d', offset=16, start_offset=16, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
- Instruction(opname='BUILD_TUPLE', opcode=49, arg=4, argval=4, argrepr='', offset=18, start_offset=18, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=81, arg=1, argval=code_object_inner, argrepr=repr(code_object_inner), offset=20, start_offset=20, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=2, argval=(5, 6), argrepr='(5, 6)', offset=8, start_offset=8, starts_line=True, line_number=3, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_FAST', opcode=82, arg=3, argval='a', argrepr='a', offset=10, start_offset=10, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_FAST', opcode=82, arg=4, argval='b', argrepr='b', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='c', argrepr='c', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_FAST', opcode=82, arg=1, argval='d', argrepr='d', offset=16, start_offset=16, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
+ Instruction(opname='BUILD_TUPLE', opcode=48, arg=4, argval=4, argrepr='', offset=18, start_offset=18, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=1, argval=code_object_inner, argrepr=repr(code_object_inner), offset=20, start_offset=20, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
Instruction(opname='MAKE_FUNCTION', opcode=23, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
- Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=105, arg=8, argval=8, argrepr='closure', offset=24, start_offset=24, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
- Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=105, arg=1, argval=1, argrepr='defaults', offset=26, start_offset=26, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
- Instruction(opname='STORE_FAST', opcode=109, arg=2, argval='inner', argrepr='inner', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_GLOBAL', opcode=89, arg=1, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_DEREF', opcode=82, arg=3, argval='a', argrepr='a', offset=40, start_offset=40, starts_line=False, line_number=5, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_DEREF', opcode=82, arg=4, argval='b', argrepr='b', offset=42, start_offset=42, starts_line=False, line_number=5, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_DEREF', opcode=82, arg=0, argval='c', argrepr='c', offset=44, start_offset=44, starts_line=False, line_number=5, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_DEREF', opcode=82, arg=1, argval='d', argrepr='d', offset=46, start_offset=46, starts_line=False, line_number=5, label=None, positions=None, cache_info=None),
- Instruction(opname='CALL', opcode=50, arg=4, argval=4, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
+ Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=104, arg=8, argval=8, argrepr='closure', offset=24, start_offset=24, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
+ Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=104, arg=1, argval=1, argrepr='defaults', offset=26, start_offset=26, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
+ Instruction(opname='STORE_FAST', opcode=108, arg=2, argval='inner', argrepr='inner', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_GLOBAL', opcode=88, arg=1, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_DEREF', opcode=81, arg=3, argval='a', argrepr='a', offset=40, start_offset=40, starts_line=False, line_number=5, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_DEREF', opcode=81, arg=4, argval='b', argrepr='b', offset=42, start_offset=42, starts_line=False, line_number=5, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_DEREF', opcode=81, arg=0, argval='c', argrepr='c', offset=44, start_offset=44, starts_line=False, line_number=5, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_DEREF', opcode=81, arg=1, argval='d', argrepr='d', offset=46, start_offset=46, starts_line=False, line_number=5, label=None, positions=None, cache_info=None),
+ Instruction(opname='CALL', opcode=49, arg=4, argval=4, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_FAST', opcode=83, arg=2, argval='inner', argrepr='inner', offset=58, start_offset=58, starts_line=True, line_number=6, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_FAST', opcode=82, arg=2, argval='inner', argrepr='inner', offset=58, start_offset=58, starts_line=True, line_number=6, label=None, positions=None, cache_info=None),
Instruction(opname='RETURN_VALUE', opcode=33, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=6, label=None, positions=None, cache_info=None),
]
expected_opinfo_inner = [
- Instruction(opname='COPY_FREE_VARS', opcode=59, arg=4, argval=4, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None),
+ Instruction(opname='COPY_FREE_VARS', opcode=58, arg=4, argval=4, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None),
Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=2, start_offset=2, starts_line=True, line_number=3, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_GLOBAL', opcode=89, arg=1, argval='print', argrepr='print + NULL', offset=4, start_offset=4, starts_line=True, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_DEREF', opcode=82, arg=2, argval='a', argrepr='a', offset=14, start_offset=14, starts_line=False, line_number=4, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_DEREF', opcode=82, arg=3, argval='b', argrepr='b', offset=16, start_offset=16, starts_line=False, line_number=4, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_DEREF', opcode=82, arg=4, argval='c', argrepr='c', offset=18, start_offset=18, starts_line=False, line_number=4, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_DEREF', opcode=82, arg=5, argval='d', argrepr='d', offset=20, start_offset=20, starts_line=False, line_number=4, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_FAST_LOAD_FAST', opcode=86, arg=1, argval=('e', 'f'), argrepr='e, f', offset=22, start_offset=22, starts_line=False, line_number=4, label=None, positions=None, cache_info=None),
- Instruction(opname='CALL', opcode=50, arg=6, argval=6, argrepr='', offset=24, start_offset=24, starts_line=False, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
+ Instruction(opname='LOAD_GLOBAL', opcode=88, arg=1, argval='print', argrepr='print + NULL', offset=4, start_offset=4, starts_line=True, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_DEREF', opcode=81, arg=2, argval='a', argrepr='a', offset=14, start_offset=14, starts_line=False, line_number=4, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_DEREF', opcode=81, arg=3, argval='b', argrepr='b', offset=16, start_offset=16, starts_line=False, line_number=4, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_DEREF', opcode=81, arg=4, argval='c', argrepr='c', offset=18, start_offset=18, starts_line=False, line_number=4, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_DEREF', opcode=81, arg=5, argval='d', argrepr='d', offset=20, start_offset=20, starts_line=False, line_number=4, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_FAST_LOAD_FAST', opcode=85, arg=1, argval=('e', 'f'), argrepr='e, f', offset=22, start_offset=22, starts_line=False, line_number=4, label=None, positions=None, cache_info=None),
+ Instruction(opname='CALL', opcode=49, arg=6, argval=6, argrepr='', offset=24, start_offset=24, starts_line=False, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=32, start_offset=32, starts_line=False, line_number=4, label=None, positions=None, cache_info=None),
- Instruction(opname='RETURN_CONST', opcode=102, arg=0, argval=None, argrepr='None', offset=34, start_offset=34, starts_line=False, line_number=4, label=None, positions=None, cache_info=None),
+ Instruction(opname='RETURN_CONST', opcode=101, arg=0, argval=None, argrepr='None', offset=34, start_offset=34, starts_line=False, line_number=4, label=None, positions=None, cache_info=None),
]
expected_opinfo_jumpy = [
Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=1, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_GLOBAL', opcode=89, arg=1, argval='range', argrepr='range + NULL', offset=2, start_offset=2, starts_line=True, line_number=3, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_CONST', opcode=81, arg=1, argval=10, argrepr='10', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
- Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
+ Instruction(opname='LOAD_GLOBAL', opcode=88, arg=1, argval='range', argrepr='range + NULL', offset=2, start_offset=2, starts_line=True, line_number=3, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=1, argval=10, argrepr='10', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
+ Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='GET_ITER', opcode=16, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
- Instruction(opname='FOR_ITER', opcode=69, arg=30, argval=88, argrepr='to L4', offset=24, start_offset=24, starts_line=False, line_number=3, label=1, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
- Instruction(opname='STORE_FAST', opcode=109, arg=0, argval='i', argrepr='i', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=40, start_offset=40, starts_line=False, line_number=4, label=None, positions=None, cache_info=None),
- Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
+ Instruction(opname='FOR_ITER', opcode=68, arg=30, argval=88, argrepr='to L4', offset=24, start_offset=24, starts_line=False, line_number=3, label=1, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
+ Instruction(opname='STORE_FAST', opcode=108, arg=0, argval='i', argrepr='i', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=40, start_offset=40, starts_line=False, line_number=4, label=None, positions=None, cache_info=None),
+ Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=50, start_offset=50, starts_line=False, line_number=4, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=52, start_offset=52, starts_line=True, line_number=5, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=81, arg=2, argval=4, argrepr='4', offset=54, start_offset=54, starts_line=False, line_number=5, label=None, positions=None, cache_info=None),
- Instruction(opname='COMPARE_OP', opcode=55, arg=18, argval='<', argrepr='bool(<)', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
- Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=2, argval=68, argrepr='to L2', offset=60, start_offset=60, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
- Instruction(opname='JUMP_BACKWARD', opcode=74, arg=22, argval=24, argrepr='to L1', offset=64, start_offset=64, starts_line=True, line_number=6, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=68, start_offset=68, starts_line=True, line_number=7, label=2, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=6, argrepr='6', offset=70, start_offset=70, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
- Instruction(opname='COMPARE_OP', opcode=55, arg=148, argval='>', argrepr='bool(>)', offset=72, start_offset=72, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
- Instruction(opname='POP_JUMP_IF_TRUE', opcode=99, arg=2, argval=84, argrepr='to L3', offset=76, start_offset=76, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
- Instruction(opname='JUMP_BACKWARD', opcode=74, arg=30, argval=24, argrepr='to L1', offset=80, start_offset=80, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=52, start_offset=52, starts_line=True, line_number=5, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=2, argval=4, argrepr='4', offset=54, start_offset=54, starts_line=False, line_number=5, label=None, positions=None, cache_info=None),
+ Instruction(opname='COMPARE_OP', opcode=54, arg=18, argval='<', argrepr='bool(<)', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
+ Instruction(opname='POP_JUMP_IF_FALSE', opcode=95, arg=2, argval=68, argrepr='to L2', offset=60, start_offset=60, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
+ Instruction(opname='JUMP_BACKWARD', opcode=73, arg=22, argval=24, argrepr='to L1', offset=64, start_offset=64, starts_line=True, line_number=6, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=68, start_offset=68, starts_line=True, line_number=7, label=2, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=6, argrepr='6', offset=70, start_offset=70, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
+ Instruction(opname='COMPARE_OP', opcode=54, arg=148, argval='>', argrepr='bool(>)', offset=72, start_offset=72, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
+ Instruction(opname='POP_JUMP_IF_TRUE', opcode=98, arg=2, argval=84, argrepr='to L3', offset=76, start_offset=76, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
+ Instruction(opname='JUMP_BACKWARD', opcode=73, arg=30, argval=24, argrepr='to L1', offset=80, start_offset=80, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=84, start_offset=84, starts_line=True, line_number=8, label=3, positions=None, cache_info=None),
- Instruction(opname='JUMP_FORWARD', opcode=76, arg=13, argval=114, argrepr='to L5', offset=86, start_offset=86, starts_line=False, line_number=8, label=None, positions=None, cache_info=None),
+ Instruction(opname='JUMP_FORWARD', opcode=75, arg=13, argval=114, argrepr='to L5', offset=86, start_offset=86, starts_line=False, line_number=8, label=None, positions=None, cache_info=None),
Instruction(opname='END_FOR', opcode=9, arg=None, argval=None, argrepr='', offset=88, start_offset=88, starts_line=True, line_number=3, label=4, positions=None, cache_info=None),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=90, start_offset=90, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=92, start_offset=92, starts_line=True, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_CONST', opcode=81, arg=4, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=102, start_offset=102, starts_line=False, line_number=10, label=None, positions=None, cache_info=None),
- Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=104, start_offset=104, starts_line=False, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
+ Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=92, start_offset=92, starts_line=True, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=4, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=102, start_offset=102, starts_line=False, line_number=10, label=None, positions=None, cache_info=None),
+ Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=104, start_offset=104, starts_line=False, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=112, start_offset=112, starts_line=False, line_number=10, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_FAST_CHECK', opcode=85, arg=0, argval='i', argrepr='i', offset=114, start_offset=114, starts_line=True, line_number=11, label=5, positions=None, cache_info=None),
+ Instruction(opname='LOAD_FAST_CHECK', opcode=84, arg=0, argval='i', argrepr='i', offset=114, start_offset=114, starts_line=True, line_number=11, label=5, positions=None, cache_info=None),
Instruction(opname='TO_BOOL', opcode=37, arg=None, argval=None, argrepr='', offset=116, start_offset=116, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]),
- Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=40, argval=208, argrepr='to L9', offset=124, start_offset=124, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=128, start_offset=128, starts_line=True, line_number=12, label=6, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=138, start_offset=138, starts_line=False, line_number=12, label=None, positions=None, cache_info=None),
- Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=140, start_offset=140, starts_line=False, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
+ Instruction(opname='POP_JUMP_IF_FALSE', opcode=95, arg=40, argval=208, argrepr='to L9', offset=124, start_offset=124, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=128, start_offset=128, starts_line=True, line_number=12, label=6, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=138, start_offset=138, starts_line=False, line_number=12, label=None, positions=None, cache_info=None),
+ Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=140, start_offset=140, starts_line=False, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=148, start_offset=148, starts_line=False, line_number=12, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=150, start_offset=150, starts_line=True, line_number=13, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=81, arg=5, argval=1, argrepr='1', offset=152, start_offset=152, starts_line=False, line_number=13, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=150, start_offset=150, starts_line=True, line_number=13, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=5, argval=1, argrepr='1', offset=152, start_offset=152, starts_line=False, line_number=13, label=None, positions=None, cache_info=None),
Instruction(opname='BINARY_OP', opcode=42, arg=23, argval=23, argrepr='-=', offset=154, start_offset=154, starts_line=False, line_number=13, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
- Instruction(opname='STORE_FAST', opcode=109, arg=0, argval='i', argrepr='i', offset=158, start_offset=158, starts_line=False, line_number=13, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=160, start_offset=160, starts_line=True, line_number=14, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=6, argrepr='6', offset=162, start_offset=162, starts_line=False, line_number=14, label=None, positions=None, cache_info=None),
- Instruction(opname='COMPARE_OP', opcode=55, arg=148, argval='>', argrepr='bool(>)', offset=164, start_offset=164, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
- Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=2, argval=176, argrepr='to L7', offset=168, start_offset=168, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
- Instruction(opname='JUMP_BACKWARD', opcode=74, arg=31, argval=114, argrepr='to L5', offset=172, start_offset=172, starts_line=True, line_number=15, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=176, start_offset=176, starts_line=True, line_number=16, label=7, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=81, arg=2, argval=4, argrepr='4', offset=178, start_offset=178, starts_line=False, line_number=16, label=None, positions=None, cache_info=None),
- Instruction(opname='COMPARE_OP', opcode=55, arg=18, argval='<', argrepr='bool(<)', offset=180, start_offset=180, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
- Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=1, argval=190, argrepr='to L8', offset=184, start_offset=184, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
- Instruction(opname='JUMP_FORWARD', opcode=76, arg=20, argval=230, argrepr='to L10', offset=188, start_offset=188, starts_line=True, line_number=17, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=190, start_offset=190, starts_line=True, line_number=11, label=8, positions=None, cache_info=None),
+ Instruction(opname='STORE_FAST', opcode=108, arg=0, argval='i', argrepr='i', offset=158, start_offset=158, starts_line=False, line_number=13, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=160, start_offset=160, starts_line=True, line_number=14, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=6, argrepr='6', offset=162, start_offset=162, starts_line=False, line_number=14, label=None, positions=None, cache_info=None),
+ Instruction(opname='COMPARE_OP', opcode=54, arg=148, argval='>', argrepr='bool(>)', offset=164, start_offset=164, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
+ Instruction(opname='POP_JUMP_IF_FALSE', opcode=95, arg=2, argval=176, argrepr='to L7', offset=168, start_offset=168, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
+ Instruction(opname='JUMP_BACKWARD', opcode=73, arg=31, argval=114, argrepr='to L5', offset=172, start_offset=172, starts_line=True, line_number=15, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=176, start_offset=176, starts_line=True, line_number=16, label=7, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=2, argval=4, argrepr='4', offset=178, start_offset=178, starts_line=False, line_number=16, label=None, positions=None, cache_info=None),
+ Instruction(opname='COMPARE_OP', opcode=54, arg=18, argval='<', argrepr='bool(<)', offset=180, start_offset=180, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
+ Instruction(opname='POP_JUMP_IF_FALSE', opcode=95, arg=1, argval=190, argrepr='to L8', offset=184, start_offset=184, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
+ Instruction(opname='JUMP_FORWARD', opcode=75, arg=20, argval=230, argrepr='to L10', offset=188, start_offset=188, starts_line=True, line_number=17, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=190, start_offset=190, starts_line=True, line_number=11, label=8, positions=None, cache_info=None),
Instruction(opname='TO_BOOL', opcode=37, arg=None, argval=None, argrepr='', offset=192, start_offset=192, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]),
- Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=2, argval=208, argrepr='to L9', offset=200, start_offset=200, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
- Instruction(opname='JUMP_BACKWARD', opcode=74, arg=40, argval=128, argrepr='to L6', offset=204, start_offset=204, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=208, start_offset=208, starts_line=True, line_number=19, label=9, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_CONST', opcode=81, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=218, start_offset=218, starts_line=False, line_number=19, label=None, positions=None, cache_info=None),
- Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=220, start_offset=220, starts_line=False, line_number=19, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
+ Instruction(opname='POP_JUMP_IF_FALSE', opcode=95, arg=2, argval=208, argrepr='to L9', offset=200, start_offset=200, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
+ Instruction(opname='JUMP_BACKWARD', opcode=73, arg=40, argval=128, argrepr='to L6', offset=204, start_offset=204, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=208, start_offset=208, starts_line=True, line_number=19, label=9, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=218, start_offset=218, starts_line=False, line_number=19, label=None, positions=None, cache_info=None),
+ Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=220, start_offset=220, starts_line=False, line_number=19, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=228, start_offset=228, starts_line=False, line_number=19, label=None, positions=None, cache_info=None),
Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=230, start_offset=230, starts_line=True, line_number=20, label=10, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=81, arg=5, argval=1, argrepr='1', offset=232, start_offset=232, starts_line=True, line_number=21, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=81, arg=7, argval=0, argrepr='0', offset=234, start_offset=234, starts_line=False, line_number=21, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=5, argval=1, argrepr='1', offset=232, start_offset=232, starts_line=True, line_number=21, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=7, argval=0, argrepr='0', offset=234, start_offset=234, starts_line=False, line_number=21, label=None, positions=None, cache_info=None),
Instruction(opname='BINARY_OP', opcode=42, arg=11, argval=11, argrepr='/', offset=236, start_offset=236, starts_line=False, line_number=21, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=240, start_offset=240, starts_line=False, line_number=21, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=242, start_offset=242, starts_line=True, line_number=25, label=None, positions=None, cache_info=None),
- Instruction(opname='COPY', opcode=58, arg=1, argval=1, argrepr='', offset=244, start_offset=244, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_SPECIAL', opcode=91, arg=1, argval=1, argrepr='__exit__', offset=246, start_offset=246, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
- Instruction(opname='SWAP', opcode=114, arg=2, argval=2, argrepr='', offset=248, start_offset=248, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
- Instruction(opname='SWAP', opcode=114, arg=3, argval=3, argrepr='', offset=250, start_offset=250, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_SPECIAL', opcode=91, arg=0, argval=0, argrepr='__enter__', offset=252, start_offset=252, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
- Instruction(opname='CALL', opcode=50, arg=0, argval=0, argrepr='', offset=254, start_offset=254, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
- Instruction(opname='STORE_FAST', opcode=109, arg=1, argval='dodgy', argrepr='dodgy', offset=262, start_offset=262, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=264, start_offset=264, starts_line=True, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_CONST', opcode=81, arg=8, argval='Never reach this', argrepr="'Never reach this'", offset=274, start_offset=274, starts_line=False, line_number=26, label=None, positions=None, cache_info=None),
- Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=276, start_offset=276, starts_line=False, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
+ Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=242, start_offset=242, starts_line=True, line_number=25, label=None, positions=None, cache_info=None),
+ Instruction(opname='COPY', opcode=57, arg=1, argval=1, argrepr='', offset=244, start_offset=244, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_SPECIAL', opcode=90, arg=1, argval=1, argrepr='__exit__', offset=246, start_offset=246, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
+ Instruction(opname='SWAP', opcode=113, arg=2, argval=2, argrepr='', offset=248, start_offset=248, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
+ Instruction(opname='SWAP', opcode=113, arg=3, argval=3, argrepr='', offset=250, start_offset=250, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_SPECIAL', opcode=90, arg=0, argval=0, argrepr='__enter__', offset=252, start_offset=252, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
+ Instruction(opname='CALL', opcode=49, arg=0, argval=0, argrepr='', offset=254, start_offset=254, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
+ Instruction(opname='STORE_FAST', opcode=108, arg=1, argval='dodgy', argrepr='dodgy', offset=262, start_offset=262, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=264, start_offset=264, starts_line=True, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=8, argval='Never reach this', argrepr="'Never reach this'", offset=274, start_offset=274, starts_line=False, line_number=26, label=None, positions=None, cache_info=None),
+ Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=276, start_offset=276, starts_line=False, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=284, start_offset=284, starts_line=False, line_number=26, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=81, arg=0, argval=None, argrepr='None', offset=286, start_offset=286, starts_line=True, line_number=25, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=81, arg=0, argval=None, argrepr='None', offset=288, start_offset=288, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=81, arg=0, argval=None, argrepr='None', offset=290, start_offset=290, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
- Instruction(opname='CALL', opcode=50, arg=3, argval=3, argrepr='', offset=292, start_offset=292, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval=None, argrepr='None', offset=286, start_offset=286, starts_line=True, line_number=25, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval=None, argrepr='None', offset=288, start_offset=288, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval=None, argrepr='None', offset=290, start_offset=290, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
+ Instruction(opname='CALL', opcode=49, arg=3, argval=3, argrepr='', offset=292, start_offset=292, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=300, start_offset=300, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=302, start_offset=302, starts_line=True, line_number=28, label=11, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_CONST', opcode=81, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=312, start_offset=312, starts_line=False, line_number=28, label=None, positions=None, cache_info=None),
- Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=314, start_offset=314, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
+ Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=302, start_offset=302, starts_line=True, line_number=28, label=11, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=312, start_offset=312, starts_line=False, line_number=28, label=None, positions=None, cache_info=None),
+ Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=314, start_offset=314, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=322, start_offset=322, starts_line=False, line_number=28, label=None, positions=None, cache_info=None),
- Instruction(opname='RETURN_CONST', opcode=102, arg=0, argval=None, argrepr='None', offset=324, start_offset=324, starts_line=False, line_number=28, label=None, positions=None, cache_info=None),
+ Instruction(opname='RETURN_CONST', opcode=101, arg=0, argval=None, argrepr='None', offset=324, start_offset=324, starts_line=False, line_number=28, label=None, positions=None, cache_info=None),
Instruction(opname='PUSH_EXC_INFO', opcode=30, arg=None, argval=None, argrepr='', offset=326, start_offset=326, starts_line=True, line_number=25, label=None, positions=None, cache_info=None),
Instruction(opname='WITH_EXCEPT_START', opcode=41, arg=None, argval=None, argrepr='', offset=328, start_offset=328, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
Instruction(opname='TO_BOOL', opcode=37, arg=None, argval=None, argrepr='', offset=330, start_offset=330, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]),
- Instruction(opname='POP_JUMP_IF_TRUE', opcode=99, arg=1, argval=344, argrepr='to L12', offset=338, start_offset=338, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
- Instruction(opname='RERAISE', opcode=101, arg=2, argval=2, argrepr='', offset=342, start_offset=342, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
+ Instruction(opname='POP_JUMP_IF_TRUE', opcode=98, arg=1, argval=344, argrepr='to L12', offset=338, start_offset=338, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
+ Instruction(opname='RERAISE', opcode=100, arg=2, argval=2, argrepr='', offset=342, start_offset=342, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=344, start_offset=344, starts_line=False, line_number=25, label=12, positions=None, cache_info=None),
Instruction(opname='POP_EXCEPT', opcode=28, arg=None, argval=None, argrepr='', offset=346, start_offset=346, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=348, start_offset=348, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=350, start_offset=350, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=352, start_offset=352, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
- Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=75, arg=27, argval=302, argrepr='to L11', offset=354, start_offset=354, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
- Instruction(opname='COPY', opcode=58, arg=3, argval=3, argrepr='', offset=356, start_offset=356, starts_line=True, line_number=None, label=None, positions=None, cache_info=None),
+ Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=74, arg=27, argval=302, argrepr='to L11', offset=354, start_offset=354, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
+ Instruction(opname='COPY', opcode=57, arg=3, argval=3, argrepr='', offset=356, start_offset=356, starts_line=True, line_number=None, label=None, positions=None, cache_info=None),
Instruction(opname='POP_EXCEPT', opcode=28, arg=None, argval=None, argrepr='', offset=358, start_offset=358, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
- Instruction(opname='RERAISE', opcode=101, arg=1, argval=1, argrepr='', offset=360, start_offset=360, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
+ Instruction(opname='RERAISE', opcode=100, arg=1, argval=1, argrepr='', offset=360, start_offset=360, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
Instruction(opname='PUSH_EXC_INFO', opcode=30, arg=None, argval=None, argrepr='', offset=362, start_offset=362, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_GLOBAL', opcode=89, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=364, start_offset=364, starts_line=True, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_GLOBAL', opcode=88, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=364, start_offset=364, starts_line=True, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
Instruction(opname='CHECK_EXC_MATCH', opcode=5, arg=None, argval=None, argrepr='', offset=374, start_offset=374, starts_line=False, line_number=22, label=None, positions=None, cache_info=None),
- Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=14, argval=408, argrepr='to L13', offset=376, start_offset=376, starts_line=False, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
+ Instruction(opname='POP_JUMP_IF_FALSE', opcode=95, arg=14, argval=408, argrepr='to L13', offset=376, start_offset=376, starts_line=False, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=380, start_offset=380, starts_line=False, line_number=22, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=382, start_offset=382, starts_line=True, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_CONST', opcode=81, arg=9, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=392, start_offset=392, starts_line=False, line_number=23, label=None, positions=None, cache_info=None),
- Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=394, start_offset=394, starts_line=False, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
+ Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=382, start_offset=382, starts_line=True, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=9, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=392, start_offset=392, starts_line=False, line_number=23, label=None, positions=None, cache_info=None),
+ Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=394, start_offset=394, starts_line=False, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=402, start_offset=402, starts_line=False, line_number=23, label=None, positions=None, cache_info=None),
Instruction(opname='POP_EXCEPT', opcode=28, arg=None, argval=None, argrepr='', offset=404, start_offset=404, starts_line=False, line_number=23, label=None, positions=None, cache_info=None),
- Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=75, arg=53, argval=302, argrepr='to L11', offset=406, start_offset=406, starts_line=False, line_number=23, label=None, positions=None, cache_info=None),
- Instruction(opname='RERAISE', opcode=101, arg=0, argval=0, argrepr='', offset=408, start_offset=408, starts_line=True, line_number=22, label=13, positions=None, cache_info=None),
- Instruction(opname='COPY', opcode=58, arg=3, argval=3, argrepr='', offset=410, start_offset=410, starts_line=True, line_number=None, label=None, positions=None, cache_info=None),
+ Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=74, arg=53, argval=302, argrepr='to L11', offset=406, start_offset=406, starts_line=False, line_number=23, label=None, positions=None, cache_info=None),
+ Instruction(opname='RERAISE', opcode=100, arg=0, argval=0, argrepr='', offset=408, start_offset=408, starts_line=True, line_number=22, label=13, positions=None, cache_info=None),
+ Instruction(opname='COPY', opcode=57, arg=3, argval=3, argrepr='', offset=410, start_offset=410, starts_line=True, line_number=None, label=None, positions=None, cache_info=None),
Instruction(opname='POP_EXCEPT', opcode=28, arg=None, argval=None, argrepr='', offset=412, start_offset=412, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
- Instruction(opname='RERAISE', opcode=101, arg=1, argval=1, argrepr='', offset=414, start_offset=414, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
+ Instruction(opname='RERAISE', opcode=100, arg=1, argval=1, argrepr='', offset=414, start_offset=414, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
Instruction(opname='PUSH_EXC_INFO', opcode=30, arg=None, argval=None, argrepr='', offset=416, start_offset=416, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=418, start_offset=418, starts_line=True, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_CONST', opcode=81, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=428, start_offset=428, starts_line=False, line_number=28, label=None, positions=None, cache_info=None),
- Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=430, start_offset=430, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
+ Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=418, start_offset=418, starts_line=True, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
+ Instruction(opname='LOAD_CONST', opcode=80, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=428, start_offset=428, starts_line=False, line_number=28, label=None, positions=None, cache_info=None),
+ Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=430, start_offset=430, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=438, start_offset=438, starts_line=False, line_number=28, label=None, positions=None, cache_info=None),
- Instruction(opname='RERAISE', opcode=101, arg=0, argval=0, argrepr='', offset=440, start_offset=440, starts_line=False, line_number=28, label=None, positions=None, cache_info=None),
- Instruction(opname='COPY', opcode=58, arg=3, argval=3, argrepr='', offset=442, start_offset=442, starts_line=True, line_number=None, label=None, positions=None, cache_info=None),
+ Instruction(opname='RERAISE', opcode=100, arg=0, argval=0, argrepr='', offset=440, start_offset=440, starts_line=False, line_number=28, label=None, positions=None, cache_info=None),
+ Instruction(opname='COPY', opcode=57, arg=3, argval=3, argrepr='', offset=442, start_offset=442, starts_line=True, line_number=None, label=None, positions=None, cache_info=None),
Instruction(opname='POP_EXCEPT', opcode=28, arg=None, argval=None, argrepr='', offset=444, start_offset=444, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
- Instruction(opname='RERAISE', opcode=101, arg=1, argval=1, argrepr='', offset=446, start_offset=446, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
+ Instruction(opname='RERAISE', opcode=100, arg=1, argval=1, argrepr='', offset=446, start_offset=446, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
]
# One last piece of inspect fodder to check the default line number handling
def simple(): pass
expected_opinfo_simple = [
Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=simple.__code__.co_firstlineno, label=None, positions=None),
- Instruction(opname='RETURN_CONST', opcode=102, arg=0, argval=None, argrepr='None', offset=2, start_offset=2, starts_line=False, line_number=simple.__code__.co_firstlineno, label=None),
+ Instruction(opname='RETURN_CONST', opcode=101, arg=0, argval=None, argrepr='None', offset=2, start_offset=2, starts_line=False, line_number=simple.__code__.co_firstlineno, label=None),
]
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-07-23-11-57-36.gh-issue-122160.HSnrAP.rst b/Misc/NEWS.d/next/Core and Builtins/2024-07-23-11-57-36.gh-issue-122160.HSnrAP.rst
new file mode 100644
index 00000000000000..78153fc1abdaeb
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2024-07-23-11-57-36.gh-issue-122160.HSnrAP.rst
@@ -0,0 +1 @@
+Remove the ``BUILD_CONST_KEY_MAP`` opcode. Use :opcode:`BUILD_MAP` instead.
diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h
index b2a7196bd6081c..22354c9bbf8a35 100644
--- a/Programs/test_frozenmain.h
+++ b/Programs/test_frozenmain.h
@@ -1,17 +1,17 @@
// Auto-generated by Programs/freeze_test_frozenmain.py
unsigned char M_test_frozenmain[] = {
227,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,
- 0,0,0,0,0,243,166,0,0,0,149,0,81,0,81,1,
- 72,0,113,0,81,0,81,1,72,1,113,1,90,2,31,0,
- 81,2,50,1,0,0,0,0,0,0,29,0,90,2,31,0,
- 81,3,90,0,79,6,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,50,2,0,0,0,0,0,0,
- 29,0,90,1,79,8,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,31,0,50,0,0,0,0,0,
- 0,0,81,4,2,0,0,0,113,5,81,5,16,0,69,20,
- 0,0,113,6,90,2,31,0,81,6,90,6,12,0,81,7,
- 90,5,90,6,2,0,0,0,12,0,48,4,50,1,0,0,
- 0,0,0,0,29,0,74,22,0,0,9,0,29,0,102,1,
+ 0,0,0,0,0,243,166,0,0,0,149,0,80,0,80,1,
+ 71,0,112,0,80,0,80,1,71,1,112,1,89,2,31,0,
+ 80,2,49,1,0,0,0,0,0,0,29,0,89,2,31,0,
+ 80,3,89,0,78,6,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,49,2,0,0,0,0,0,0,
+ 29,0,89,1,78,8,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,31,0,49,0,0,0,0,0,
+ 0,0,80,4,2,0,0,0,112,5,80,5,16,0,68,20,
+ 0,0,112,6,89,2,31,0,80,6,89,6,12,0,80,7,
+ 89,5,89,6,2,0,0,0,12,0,47,4,49,1,0,0,
+ 0,0,0,0,29,0,73,22,0,0,9,0,29,0,101,1,
41,8,233,0,0,0,0,78,122,18,70,114,111,122,101,110,
32,72,101,108,108,111,32,87,111,114,108,100,122,8,115,121,
115,46,97,114,103,118,218,6,99,111,110,102,105,103,41,5,
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 9dd7cf37beecf0..be6b4436694774 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -1882,25 +1882,6 @@ dummy_func(
}
}
- inst(BUILD_CONST_KEY_MAP, (values[oparg], keys -- map)) {
- PyObject *keys_o = PyStackRef_AsPyObjectBorrow(keys);
-
- assert(PyTuple_CheckExact(keys_o));
- assert(PyTuple_GET_SIZE(keys_o) == (Py_ssize_t)oparg);
- STACKREFS_TO_PYOBJECTS(values, oparg, values_o);
- if (CONVERSION_FAILED(values_o)) {
- DECREF_INPUTS();
- ERROR_IF(true, error);
- }
- PyObject *map_o = _PyDict_FromItems(
- &PyTuple_GET_ITEM(keys_o, 0), 1,
- values_o, 1, oparg);
- STACKREFS_TO_PYOBJECTS_CLEANUP(values_o);
- DECREF_INPUTS();
- ERROR_IF(map_o == NULL, error);
- map = PyStackRef_FromPyObjectSteal(map_o);
- }
-
inst(DICT_UPDATE, (dict, unused[oparg - 1], update -- dict, unused[oparg - 1])) {
PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict);
PyObject *update_o = PyStackRef_AsPyObjectBorrow(update);
diff --git a/Python/compile.c b/Python/compile.c
index 87a75487a9aaa5..9707759c99c943 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -1759,42 +1759,24 @@ compiler_kwonlydefaults(struct compiler *c, location loc,
*/
int i;
PyObject *keys = NULL;
-
+ int default_count = 0;
for (i = 0; i < asdl_seq_LEN(kwonlyargs); i++) {
arg_ty arg = asdl_seq_GET(kwonlyargs, i);
expr_ty default_ = asdl_seq_GET(kw_defaults, i);
if (default_) {
+ default_count++;
PyObject *mangled = compiler_maybe_mangle(c, arg->arg);
if (!mangled) {
goto error;
}
- if (keys == NULL) {
- keys = PyList_New(1);
- if (keys == NULL) {
- Py_DECREF(mangled);
- return ERROR;
- }
- PyList_SET_ITEM(keys, 0, mangled);
- }
- else {
- int res = PyList_Append(keys, mangled);
- Py_DECREF(mangled);
- if (res == -1) {
- goto error;
- }
- }
+ ADDOP_LOAD_CONST_NEW(c, loc, mangled);
if (compiler_visit_expr(c, default_) < 0) {
goto error;
}
}
}
- if (keys != NULL) {
- Py_ssize_t default_count = PyList_GET_SIZE(keys);
- PyObject *keys_tuple = PyList_AsTuple(keys);
- Py_DECREF(keys);
- ADDOP_LOAD_CONST_NEW(c, loc, keys_tuple);
- ADDOP_I(c, loc, BUILD_CONST_KEY_MAP, default_count);
- assert(default_count > 0);
+ if (default_count) {
+ ADDOP_I(c, loc, BUILD_MAP, default_count);
return 1;
}
else {
@@ -4454,25 +4436,8 @@ static int
compiler_subdict(struct compiler *c, expr_ty e, Py_ssize_t begin, Py_ssize_t end)
{
Py_ssize_t i, n = end - begin;
- PyObject *keys, *key;
int big = n*2 > STACK_USE_GUIDELINE;
location loc = LOC(e);
- if (n > 1 && !big && are_all_items_const(e->v.Dict.keys, begin, end)) {
- for (i = begin; i < end; i++) {
- VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Dict.values, i));
- }
- keys = PyTuple_New(n);
- if (keys == NULL) {
- return SUCCESS;
- }
- for (i = begin; i < end; i++) {
- key = ((expr_ty)asdl_seq_GET(e->v.Dict.keys, i))->v.Constant.value;
- PyTuple_SET_ITEM(keys, i - begin, Py_NewRef(key));
- }
- ADDOP_LOAD_CONST_NEW(c, loc, keys);
- ADDOP_I(c, loc, BUILD_CONST_KEY_MAP, n);
- return SUCCESS;
- }
if (big) {
ADDOP_I(c, loc, BUILD_MAP, 0);
}
@@ -5032,26 +4997,8 @@ compiler_subkwargs(struct compiler *c, location loc,
{
Py_ssize_t i, n = end - begin;
keyword_ty kw;
- PyObject *keys, *key;
assert(n > 0);
int big = n*2 > STACK_USE_GUIDELINE;
- if (n > 1 && !big) {
- for (i = begin; i < end; i++) {
- kw = asdl_seq_GET(keywords, i);
- VISIT(c, expr, kw->value);
- }
- keys = PyTuple_New(n);
- if (keys == NULL) {
- return ERROR;
- }
- for (i = begin; i < end; i++) {
- key = ((keyword_ty) asdl_seq_GET(keywords, i))->arg;
- PyTuple_SET_ITEM(keys, i - begin, Py_NewRef(key));
- }
- ADDOP_LOAD_CONST_NEW(c, loc, keys);
- ADDOP_I(c, loc, BUILD_CONST_KEY_MAP, n);
- return SUCCESS;
- }
if (big) {
ADDOP_I(c, NO_LOCATION, BUILD_MAP, 0);
}
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index 2a4428e4a52cf0..abcc7cf4d69473 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -2028,40 +2028,6 @@
break;
}
- case _BUILD_CONST_KEY_MAP: {
- _PyStackRef keys;
- _PyStackRef *values;
- _PyStackRef map;
- oparg = CURRENT_OPARG();
- keys = stack_pointer[-1];
- values = &stack_pointer[-1 - oparg];
- PyObject *keys_o = PyStackRef_AsPyObjectBorrow(keys);
- assert(PyTuple_CheckExact(keys_o));
- assert(PyTuple_GET_SIZE(keys_o) == (Py_ssize_t)oparg);
- STACKREFS_TO_PYOBJECTS(values, oparg, values_o);
- if (CONVERSION_FAILED(values_o)) {
- for (int _i = oparg; --_i >= 0;) {
- PyStackRef_CLOSE(values[_i]);
- }
- PyStackRef_CLOSE(keys);
- if (true) JUMP_TO_ERROR();
- }
- PyObject *map_o = _PyDict_FromItems(
- &PyTuple_GET_ITEM(keys_o, 0), 1,
- values_o, 1, oparg);
- STACKREFS_TO_PYOBJECTS_CLEANUP(values_o);
- for (int _i = oparg; --_i >= 0;) {
- PyStackRef_CLOSE(values[_i]);
- }
- PyStackRef_CLOSE(keys);
- if (map_o == NULL) JUMP_TO_ERROR();
- map = PyStackRef_FromPyObjectSteal(map_o);
- stack_pointer[-1 - oparg] = map;
- stack_pointer += -oparg;
- assert(WITHIN_STACK_BOUNDS());
- break;
- }
-
case _DICT_UPDATE: {
_PyStackRef update;
_PyStackRef dict;
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 585e6825a346d8..195fe4c5a04798 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -589,42 +589,6 @@
DISPATCH();
}
- TARGET(BUILD_CONST_KEY_MAP) {
- frame->instr_ptr = next_instr;
- next_instr += 1;
- INSTRUCTION_STATS(BUILD_CONST_KEY_MAP);
- _PyStackRef *values;
- _PyStackRef keys;
- _PyStackRef map;
- keys = stack_pointer[-1];
- values = &stack_pointer[-1 - oparg];
- PyObject *keys_o = PyStackRef_AsPyObjectBorrow(keys);
- assert(PyTuple_CheckExact(keys_o));
- assert(PyTuple_GET_SIZE(keys_o) == (Py_ssize_t)oparg);
- STACKREFS_TO_PYOBJECTS(values, oparg, values_o);
- if (CONVERSION_FAILED(values_o)) {
- for (int _i = oparg; --_i >= 0;) {
- PyStackRef_CLOSE(values[_i]);
- }
- PyStackRef_CLOSE(keys);
- if (true) { stack_pointer += -1 - oparg; goto error; }
- }
- PyObject *map_o = _PyDict_FromItems(
- &PyTuple_GET_ITEM(keys_o, 0), 1,
- values_o, 1, oparg);
- STACKREFS_TO_PYOBJECTS_CLEANUP(values_o);
- for (int _i = oparg; --_i >= 0;) {
- PyStackRef_CLOSE(values[_i]);
- }
- PyStackRef_CLOSE(keys);
- if (map_o == NULL) { stack_pointer += -1 - oparg; goto error; }
- map = PyStackRef_FromPyObjectSteal(map_o);
- stack_pointer[-1 - oparg] = map;
- stack_pointer += -oparg;
- assert(WITHIN_STACK_BOUNDS());
- DISPATCH();
- }
-
TARGET(BUILD_LIST) {
frame->instr_ptr = next_instr;
next_instr += 1;
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index 6097b249c0ad0b..74544a1dff25c6 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -42,7 +42,6 @@ static void *opcode_targets[256] = {
&&TARGET_UNARY_NOT,
&&TARGET_WITH_EXCEPT_START,
&&TARGET_BINARY_OP,
- &&TARGET_BUILD_CONST_KEY_MAP,
&&TARGET_BUILD_LIST,
&&TARGET_BUILD_MAP,
&&TARGET_BUILD_SET,
@@ -148,6 +147,7 @@ static void *opcode_targets[256] = {
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
+ &&_unknown_opcode,
&&TARGET_RESUME,
&&TARGET_BINARY_OP_ADD_FLOAT,
&&TARGET_BINARY_OP_ADD_INT,
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 60cfb214835bdd..8077badce78a39 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -943,15 +943,6 @@
break;
}
- case _BUILD_CONST_KEY_MAP: {
- _Py_UopsSymbol *map;
- map = sym_new_not_null(ctx);
- stack_pointer[-1 - oparg] = map;
- stack_pointer += -oparg;
- assert(WITHIN_STACK_BOUNDS());
- break;
- }
-
case _DICT_UPDATE: {
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
From aef95eb107fef9355c66461612aedd31265f8c21 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Thu, 25 Jul 2024 17:31:30 +0200
Subject: [PATCH 09/47] gh-121489: Export private _PyBytes_Join() again
(#122267)
---
Include/cpython/bytesobject.h | 4 ++++
Include/internal/pycore_bytesobject.h | 4 ----
.../next/C API/2024-07-21-17-40-07.gh-issue-121489.SUMFCr.rst | 1 +
Modules/_io/bufferedio.c | 1 -
4 files changed, 5 insertions(+), 5 deletions(-)
create mode 100644 Misc/NEWS.d/next/C API/2024-07-21-17-40-07.gh-issue-121489.SUMFCr.rst
diff --git a/Include/cpython/bytesobject.h b/Include/cpython/bytesobject.h
index 816823716e9a6f..41537210b748a1 100644
--- a/Include/cpython/bytesobject.h
+++ b/Include/cpython/bytesobject.h
@@ -31,3 +31,7 @@ static inline Py_ssize_t PyBytes_GET_SIZE(PyObject *op) {
return Py_SIZE(self);
}
#define PyBytes_GET_SIZE(self) PyBytes_GET_SIZE(_PyObject_CAST(self))
+
+/* _PyBytes_Join(sep, x) is like sep.join(x). sep must be PyBytesObject*,
+ x must be an iterable object. */
+PyAPI_FUNC(PyObject*) _PyBytes_Join(PyObject *sep, PyObject *x);
diff --git a/Include/internal/pycore_bytesobject.h b/Include/internal/pycore_bytesobject.h
index 94d421a9eb742a..300e7f4896a39e 100644
--- a/Include/internal/pycore_bytesobject.h
+++ b/Include/internal/pycore_bytesobject.h
@@ -23,10 +23,6 @@ extern PyObject* _PyBytes_FromHex(
PyAPI_FUNC(PyObject*) _PyBytes_DecodeEscape(const char *, Py_ssize_t,
const char *, const char **);
-/* _PyBytes_Join(sep, x) is like sep.join(x). sep must be PyBytesObject*,
- x must be an iterable object. */
-extern PyObject* _PyBytes_Join(PyObject *sep, PyObject *x);
-
// Substring Search.
//
diff --git a/Misc/NEWS.d/next/C API/2024-07-21-17-40-07.gh-issue-121489.SUMFCr.rst b/Misc/NEWS.d/next/C API/2024-07-21-17-40-07.gh-issue-121489.SUMFCr.rst
new file mode 100644
index 00000000000000..8c18a49c05d547
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2024-07-21-17-40-07.gh-issue-121489.SUMFCr.rst
@@ -0,0 +1 @@
+Export private :c:func:`!_PyBytes_Join` again.
diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c
index aa52711941d374..e45323c93a17ef 100644
--- a/Modules/_io/bufferedio.c
+++ b/Modules/_io/bufferedio.c
@@ -8,7 +8,6 @@
*/
#include "Python.h"
-#include "pycore_bytesobject.h" // _PyBytes_Join()
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
#include "pycore_pyerrors.h" // _Py_FatalErrorFormat()
From 5e686ff57d6bc2fd8c675bd2c59a064be6da2839 Mon Sep 17 00:00:00 2001
From: Mark Shannon
Date: Thu, 25 Jul 2024 18:32:43 +0100
Subject: [PATCH 10/47] GH-122034: Add StackRef variants of type checks to
reduce the number of PyStackRef_AsPyObjectBorrow calls (GH-122037)
---
Include/internal/pycore_stackref.h | 33 ++++++++++++++++++++++++++++++
Python/bytecodes.c | 20 +++++++++---------
Python/executor_cases.c.h | 10 ++++-----
Python/generated_cases.c.h | 24 +++++++++++-----------
4 files changed, 60 insertions(+), 27 deletions(-)
diff --git a/Include/internal/pycore_stackref.h b/Include/internal/pycore_stackref.h
index 8d3d559814bfd9..1b35a3e3269257 100644
--- a/Include/internal/pycore_stackref.h
+++ b/Include/internal/pycore_stackref.h
@@ -11,6 +11,7 @@ extern "C" {
#include "pycore_object_deferred.h"
#include
+#include
/*
This file introduces a new API for handling references on the stack, called
@@ -237,6 +238,38 @@ _PyObjectStack_FromStackRefStack(PyObject **dst, const _PyStackRef *src, size_t
}
}
+// StackRef type checks
+
+static inline bool
+PyStackRef_GenCheck(_PyStackRef stackref)
+{
+ return PyGen_Check(PyStackRef_AsPyObjectBorrow(stackref));
+}
+
+static inline bool
+PyStackRef_BoolCheck(_PyStackRef stackref)
+{
+ return PyBool_Check(PyStackRef_AsPyObjectBorrow(stackref));
+}
+
+static inline bool
+PyStackRef_LongCheck(_PyStackRef stackref)
+{
+ return PyLong_Check(PyStackRef_AsPyObjectBorrow(stackref));
+}
+
+static inline bool
+PyStackRef_ExceptionInstanceCheck(_PyStackRef stackref)
+{
+ return PyExceptionInstance_Check(PyStackRef_AsPyObjectBorrow(stackref));
+}
+
+
+static inline bool
+PyStackRef_FunctionCheck(_PyStackRef stackref)
+{
+ return PyFunction_Check(PyStackRef_AsPyObjectBorrow(stackref));
+}
#ifdef __cplusplus
}
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index be6b4436694774..b161fc0ede1dff 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -286,7 +286,7 @@ dummy_func(
tier1 inst(INSTRUMENTED_END_FOR, (receiver, value -- receiver)) {
/* Need to create a fake StopIteration error here,
* to conform to PEP 380 */
- if (PyGen_Check(PyStackRef_AsPyObjectBorrow(receiver))) {
+ if (PyStackRef_GenCheck(receiver)) {
if (monitor_stop_iteration(tstate, frame, this_instr, PyStackRef_AsPyObjectBorrow(value))) {
ERROR_NO_POP();
}
@@ -317,7 +317,7 @@ dummy_func(
}
pure inst(UNARY_NOT, (value -- res)) {
- assert(PyBool_Check(PyStackRef_AsPyObjectBorrow(value)));
+ assert(PyStackRef_BoolCheck(value));
res = PyStackRef_Is(value, PyStackRef_False)
? PyStackRef_True : PyStackRef_False;
}
@@ -353,7 +353,7 @@ dummy_func(
macro(TO_BOOL) = _SPECIALIZE_TO_BOOL + unused/2 + _TO_BOOL;
inst(TO_BOOL_BOOL, (unused/1, unused/2, value -- value)) {
- EXIT_IF(!PyBool_Check(PyStackRef_AsPyObjectBorrow(value)));
+ EXIT_IF(!PyStackRef_BoolCheck(value));
STAT_INC(TO_BOOL, hit);
}
@@ -2688,7 +2688,7 @@ dummy_func(
}
replaced op(_POP_JUMP_IF_FALSE, (cond -- )) {
- assert(PyBool_Check(PyStackRef_AsPyObjectBorrow(cond)));
+ assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_Is(cond, PyStackRef_False);
#if ENABLE_SPECIALIZATION
this_instr[1].cache = (this_instr[1].cache << 1) | flag;
@@ -2697,7 +2697,7 @@ dummy_func(
}
replaced op(_POP_JUMP_IF_TRUE, (cond -- )) {
- assert(PyBool_Check(PyStackRef_AsPyObjectBorrow(cond)));
+ assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_Is(cond, PyStackRef_True);
#if ENABLE_SPECIALIZATION
this_instr[1].cache = (this_instr[1].cache << 1) | flag;
@@ -3121,7 +3121,7 @@ dummy_func(
else {
Py_DECREF(tb);
}
- assert(PyLong_Check(PyStackRef_AsPyObjectBorrow(lasti)));
+ assert(PyStackRef_LongCheck(lasti));
(void)lasti; // Shut up compiler warning if asserts are off
PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb};
int has_self = !PyStackRef_IsNull(exit_self);
@@ -3163,7 +3163,7 @@ dummy_func(
else {
prev_exc = PyStackRef_None;
}
- assert(PyExceptionInstance_Check(PyStackRef_AsPyObjectBorrow(new_exc)));
+ assert(PyStackRef_ExceptionInstanceCheck(new_exc));
exc_info->exc_value = PyStackRef_AsPyObjectNew(new_exc);
}
@@ -3459,7 +3459,7 @@ dummy_func(
assert(Py_TYPE(callable_o) == &PyMethod_Type);
self = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self);
method = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func);
- assert(PyFunction_Check(PyStackRef_AsPyObjectBorrow(method)));
+ assert(PyStackRef_FunctionCheck(method));
PyStackRef_CLOSE(callable);
}
@@ -4467,7 +4467,7 @@ dummy_func(
inst(INSTRUMENTED_POP_JUMP_IF_TRUE, (unused/1 -- )) {
_PyStackRef cond = POP();
- assert(PyBool_Check(PyStackRef_AsPyObjectBorrow(cond)));
+ assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_Is(cond, PyStackRef_True);
int offset = flag * oparg;
#if ENABLE_SPECIALIZATION
@@ -4478,7 +4478,7 @@ dummy_func(
inst(INSTRUMENTED_POP_JUMP_IF_FALSE, (unused/1 -- )) {
_PyStackRef cond = POP();
- assert(PyBool_Check(PyStackRef_AsPyObjectBorrow(cond)));
+ assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_Is(cond, PyStackRef_False);
int offset = flag * oparg;
#if ENABLE_SPECIALIZATION
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index abcc7cf4d69473..87c9255ef7997d 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -324,7 +324,7 @@
_PyStackRef value;
_PyStackRef res;
value = stack_pointer[-1];
- assert(PyBool_Check(PyStackRef_AsPyObjectBorrow(value)));
+ assert(PyStackRef_BoolCheck(value));
res = PyStackRef_Is(value, PyStackRef_False)
? PyStackRef_True : PyStackRef_False;
stack_pointer[-1] = res;
@@ -346,7 +346,7 @@
case _TO_BOOL_BOOL: {
_PyStackRef value;
value = stack_pointer[-1];
- if (!PyBool_Check(PyStackRef_AsPyObjectBorrow(value))) {
+ if (!PyStackRef_BoolCheck(value)) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
@@ -3344,7 +3344,7 @@
else {
Py_DECREF(tb);
}
- assert(PyLong_Check(PyStackRef_AsPyObjectBorrow(lasti)));
+ assert(PyStackRef_LongCheck(lasti));
(void)lasti; // Shut up compiler warning if asserts are off
PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb};
int has_self = !PyStackRef_IsNull(exit_self);
@@ -3368,7 +3368,7 @@
else {
prev_exc = PyStackRef_None;
}
- assert(PyExceptionInstance_Check(PyStackRef_AsPyObjectBorrow(new_exc)));
+ assert(PyStackRef_ExceptionInstanceCheck(new_exc));
exc_info->exc_value = PyStackRef_AsPyObjectNew(new_exc);
stack_pointer[-1] = prev_exc;
stack_pointer[0] = new_exc;
@@ -3614,7 +3614,7 @@
assert(Py_TYPE(callable_o) == &PyMethod_Type);
self = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self);
method = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func);
- assert(PyFunction_Check(PyStackRef_AsPyObjectBorrow(method)));
+ assert(PyStackRef_FunctionCheck(method));
PyStackRef_CLOSE(callable);
stack_pointer[-2 - oparg] = method;
stack_pointer[-1 - oparg] = self;
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 195fe4c5a04798..f15a829ea3ebfa 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -1078,7 +1078,7 @@
assert(Py_TYPE(callable_o) == &PyMethod_Type);
self = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self);
method = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func);
- assert(PyFunction_Check(PyStackRef_AsPyObjectBorrow(method)));
+ assert(PyStackRef_FunctionCheck(method));
PyStackRef_CLOSE(callable);
}
// flush
@@ -3544,7 +3544,7 @@
receiver = stack_pointer[-2];
/* Need to create a fake StopIteration error here,
* to conform to PEP 380 */
- if (PyGen_Check(PyStackRef_AsPyObjectBorrow(receiver))) {
+ if (PyStackRef_GenCheck(receiver)) {
if (monitor_stop_iteration(tstate, frame, this_instr, PyStackRef_AsPyObjectBorrow(value))) {
goto error;
}
@@ -3667,7 +3667,7 @@
INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_FALSE);
/* Skip 1 cache entry */
_PyStackRef cond = POP();
- assert(PyBool_Check(PyStackRef_AsPyObjectBorrow(cond)));
+ assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_Is(cond, PyStackRef_False);
int offset = flag * oparg;
#if ENABLE_SPECIALIZATION
@@ -3730,7 +3730,7 @@
INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_TRUE);
/* Skip 1 cache entry */
_PyStackRef cond = POP();
- assert(PyBool_Check(PyStackRef_AsPyObjectBorrow(cond)));
+ assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_Is(cond, PyStackRef_True);
int offset = flag * oparg;
#if ENABLE_SPECIALIZATION
@@ -5329,7 +5329,7 @@
_PyStackRef cond;
/* Skip 1 cache entry */
cond = stack_pointer[-1];
- assert(PyBool_Check(PyStackRef_AsPyObjectBorrow(cond)));
+ assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_Is(cond, PyStackRef_False);
#if ENABLE_SPECIALIZATION
this_instr[1].cache = (this_instr[1].cache << 1) | flag;
@@ -5363,7 +5363,7 @@
// _POP_JUMP_IF_TRUE
cond = b;
{
- assert(PyBool_Check(PyStackRef_AsPyObjectBorrow(cond)));
+ assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_Is(cond, PyStackRef_True);
#if ENABLE_SPECIALIZATION
this_instr[1].cache = (this_instr[1].cache << 1) | flag;
@@ -5398,7 +5398,7 @@
// _POP_JUMP_IF_FALSE
cond = b;
{
- assert(PyBool_Check(PyStackRef_AsPyObjectBorrow(cond)));
+ assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_Is(cond, PyStackRef_False);
#if ENABLE_SPECIALIZATION
this_instr[1].cache = (this_instr[1].cache << 1) | flag;
@@ -5418,7 +5418,7 @@
_PyStackRef cond;
/* Skip 1 cache entry */
cond = stack_pointer[-1];
- assert(PyBool_Check(PyStackRef_AsPyObjectBorrow(cond)));
+ assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_Is(cond, PyStackRef_True);
#if ENABLE_SPECIALIZATION
this_instr[1].cache = (this_instr[1].cache << 1) | flag;
@@ -5455,7 +5455,7 @@
else {
prev_exc = PyStackRef_None;
}
- assert(PyExceptionInstance_Check(PyStackRef_AsPyObjectBorrow(new_exc)));
+ assert(PyStackRef_ExceptionInstanceCheck(new_exc));
exc_info->exc_value = PyStackRef_AsPyObjectNew(new_exc);
stack_pointer[-1] = prev_exc;
stack_pointer[0] = new_exc;
@@ -6418,7 +6418,7 @@
/* Skip 1 cache entry */
/* Skip 2 cache entries */
value = stack_pointer[-1];
- DEOPT_IF(!PyBool_Check(PyStackRef_AsPyObjectBorrow(value)), TO_BOOL);
+ DEOPT_IF(!PyStackRef_BoolCheck(value), TO_BOOL);
STAT_INC(TO_BOOL, hit);
DISPATCH();
}
@@ -6548,7 +6548,7 @@
_PyStackRef value;
_PyStackRef res;
value = stack_pointer[-1];
- assert(PyBool_Check(PyStackRef_AsPyObjectBorrow(value)));
+ assert(PyStackRef_BoolCheck(value));
res = PyStackRef_Is(value, PyStackRef_False)
? PyStackRef_True : PyStackRef_False;
stack_pointer[-1] = res;
@@ -6715,7 +6715,7 @@
else {
Py_DECREF(tb);
}
- assert(PyLong_Check(PyStackRef_AsPyObjectBorrow(lasti)));
+ assert(PyStackRef_LongCheck(lasti));
(void)lasti; // Shut up compiler warning if asserts are off
PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb};
int has_self = !PyStackRef_IsNull(exit_self);
From 5f6001130f8ada871193377954cfcfee01ef93b6 Mon Sep 17 00:00:00 2001
From: Brandt Bucher
Date: Thu, 25 Jul 2024 10:45:28 -0700
Subject: [PATCH 11/47] GH-118093: Add tier two support for LOAD_ATTR_PROPERTY
(GH-122283)
---
Include/internal/pycore_opcode_metadata.h | 5 +-
Include/internal/pycore_uop_ids.h | 98 +++++++++++------------
Include/internal/pycore_uop_metadata.h | 4 +
Python/bytecodes.c | 30 ++++---
Python/executor_cases.c.h | 34 +++++++-
Python/generated_cases.c.h | 79 ++++++++++++------
Python/optimizer.c | 5 +-
Python/optimizer_cases.c.h | 7 +-
Python/specialize.c | 5 --
9 files changed, 168 insertions(+), 99 deletions(-)
diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h
index 2b6e9bca51c9df..2fb6b2c4ed8205 100644
--- a/Include/internal/pycore_opcode_metadata.h
+++ b/Include/internal/pycore_opcode_metadata.h
@@ -733,7 +733,7 @@ int _PyOpcode_num_pushed(int opcode, int oparg) {
case LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES:
return 1;
case LOAD_ATTR_PROPERTY:
- return 1;
+ return 0;
case LOAD_ATTR_SLOT:
return 1 + (oparg & 1);
case LOAD_ATTR_WITH_HINT:
@@ -1109,7 +1109,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[264] = {
[LOAD_ATTR_MODULE] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG },
[LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_EXIT_FLAG },
[LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG },
- [LOAD_ATTR_PROPERTY] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG },
+ [LOAD_ATTR_PROPERTY] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG },
[LOAD_ATTR_SLOT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG },
[LOAD_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG },
[LOAD_BUILD_CLASS] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
@@ -1305,6 +1305,7 @@ _PyOpcode_macro_expansion[256] = {
[LOAD_ATTR_MODULE] = { .nuops = 2, .uops = { { _CHECK_ATTR_MODULE, 2, 1 }, { _LOAD_ATTR_MODULE, 1, 3 } } },
[LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_NONDESCRIPTOR_NO_DICT, 4, 5 } } },
[LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, 0, 0 }, { _GUARD_KEYS_VERSION, 2, 3 }, { _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES, 4, 5 } } },
+ [LOAD_ATTR_PROPERTY] = { .nuops = 5, .uops = { { _CHECK_PEP_523, 0, 0 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_PROPERTY_FRAME, 4, 5 }, { _SAVE_RETURN_OFFSET, 7, 9 }, { _PUSH_FRAME, 0, 0 } } },
[LOAD_ATTR_SLOT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_SLOT, 1, 3 } } },
[LOAD_ATTR_WITH_HINT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_ATTR_WITH_HINT, 0, 0 }, { _LOAD_ATTR_WITH_HINT, 1, 3 } } },
[LOAD_BUILD_CLASS] = { .nuops = 1, .uops = { { _LOAD_BUILD_CLASS, 0, 0 } } },
diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h
index fc67da697cb06c..88c835ca8ed10c 100644
--- a/Include/internal/pycore_uop_ids.h
+++ b/Include/internal/pycore_uop_ids.h
@@ -182,36 +182,36 @@ extern "C" {
#define _LOAD_ATTR_MODULE 402
#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 403
#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 404
-#define _LOAD_ATTR_PROPERTY LOAD_ATTR_PROPERTY
-#define _LOAD_ATTR_SLOT 405
-#define _LOAD_ATTR_SLOT_0 406
-#define _LOAD_ATTR_SLOT_1 407
-#define _LOAD_ATTR_WITH_HINT 408
+#define _LOAD_ATTR_PROPERTY_FRAME 405
+#define _LOAD_ATTR_SLOT 406
+#define _LOAD_ATTR_SLOT_0 407
+#define _LOAD_ATTR_SLOT_1 408
+#define _LOAD_ATTR_WITH_HINT 409
#define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS
#define _LOAD_COMMON_CONSTANT LOAD_COMMON_CONSTANT
#define _LOAD_CONST LOAD_CONST
-#define _LOAD_CONST_INLINE 409
-#define _LOAD_CONST_INLINE_BORROW 410
-#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 411
-#define _LOAD_CONST_INLINE_WITH_NULL 412
+#define _LOAD_CONST_INLINE 410
+#define _LOAD_CONST_INLINE_BORROW 411
+#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 412
+#define _LOAD_CONST_INLINE_WITH_NULL 413
#define _LOAD_DEREF LOAD_DEREF
-#define _LOAD_FAST 413
-#define _LOAD_FAST_0 414
-#define _LOAD_FAST_1 415
-#define _LOAD_FAST_2 416
-#define _LOAD_FAST_3 417
-#define _LOAD_FAST_4 418
-#define _LOAD_FAST_5 419
-#define _LOAD_FAST_6 420
-#define _LOAD_FAST_7 421
+#define _LOAD_FAST 414
+#define _LOAD_FAST_0 415
+#define _LOAD_FAST_1 416
+#define _LOAD_FAST_2 417
+#define _LOAD_FAST_3 418
+#define _LOAD_FAST_4 419
+#define _LOAD_FAST_5 420
+#define _LOAD_FAST_6 421
+#define _LOAD_FAST_7 422
#define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR
#define _LOAD_FAST_CHECK LOAD_FAST_CHECK
#define _LOAD_FAST_LOAD_FAST LOAD_FAST_LOAD_FAST
#define _LOAD_FROM_DICT_OR_DEREF LOAD_FROM_DICT_OR_DEREF
#define _LOAD_FROM_DICT_OR_GLOBALS LOAD_FROM_DICT_OR_GLOBALS
-#define _LOAD_GLOBAL 422
-#define _LOAD_GLOBAL_BUILTINS 423
-#define _LOAD_GLOBAL_MODULE 424
+#define _LOAD_GLOBAL 423
+#define _LOAD_GLOBAL_BUILTINS 424
+#define _LOAD_GLOBAL_MODULE 425
#define _LOAD_LOCALS LOAD_LOCALS
#define _LOAD_NAME LOAD_NAME
#define _LOAD_SPECIAL LOAD_SPECIAL
@@ -226,51 +226,51 @@ extern "C" {
#define _MATCH_SEQUENCE MATCH_SEQUENCE
#define _NOP NOP
#define _POP_EXCEPT POP_EXCEPT
-#define _POP_JUMP_IF_FALSE 425
-#define _POP_JUMP_IF_TRUE 426
+#define _POP_JUMP_IF_FALSE 426
+#define _POP_JUMP_IF_TRUE 427
#define _POP_TOP POP_TOP
-#define _POP_TOP_LOAD_CONST_INLINE_BORROW 427
+#define _POP_TOP_LOAD_CONST_INLINE_BORROW 428
#define _PUSH_EXC_INFO PUSH_EXC_INFO
-#define _PUSH_FRAME 428
+#define _PUSH_FRAME 429
#define _PUSH_NULL PUSH_NULL
-#define _PY_FRAME_GENERAL 429
-#define _REPLACE_WITH_TRUE 430
+#define _PY_FRAME_GENERAL 430
+#define _REPLACE_WITH_TRUE 431
#define _RESUME_CHECK RESUME_CHECK
#define _RETURN_GENERATOR RETURN_GENERATOR
#define _RETURN_VALUE RETURN_VALUE
-#define _SAVE_RETURN_OFFSET 431
-#define _SEND 432
-#define _SEND_GEN_FRAME 433
+#define _SAVE_RETURN_OFFSET 432
+#define _SEND 433
+#define _SEND_GEN_FRAME 434
#define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS
#define _SET_ADD SET_ADD
#define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE
#define _SET_UPDATE SET_UPDATE
-#define _START_EXECUTOR 434
-#define _STORE_ATTR 435
-#define _STORE_ATTR_INSTANCE_VALUE 436
-#define _STORE_ATTR_SLOT 437
-#define _STORE_ATTR_WITH_HINT 438
+#define _START_EXECUTOR 435
+#define _STORE_ATTR 436
+#define _STORE_ATTR_INSTANCE_VALUE 437
+#define _STORE_ATTR_SLOT 438
+#define _STORE_ATTR_WITH_HINT 439
#define _STORE_DEREF STORE_DEREF
-#define _STORE_FAST 439
-#define _STORE_FAST_0 440
-#define _STORE_FAST_1 441
-#define _STORE_FAST_2 442
-#define _STORE_FAST_3 443
-#define _STORE_FAST_4 444
-#define _STORE_FAST_5 445
-#define _STORE_FAST_6 446
-#define _STORE_FAST_7 447
+#define _STORE_FAST 440
+#define _STORE_FAST_0 441
+#define _STORE_FAST_1 442
+#define _STORE_FAST_2 443
+#define _STORE_FAST_3 444
+#define _STORE_FAST_4 445
+#define _STORE_FAST_5 446
+#define _STORE_FAST_6 447
+#define _STORE_FAST_7 448
#define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST
#define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST
#define _STORE_GLOBAL STORE_GLOBAL
#define _STORE_NAME STORE_NAME
#define _STORE_SLICE STORE_SLICE
-#define _STORE_SUBSCR 448
+#define _STORE_SUBSCR 449
#define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT
#define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT
#define _SWAP SWAP
-#define _TIER2_RESUME_CHECK 449
-#define _TO_BOOL 450
+#define _TIER2_RESUME_CHECK 450
+#define _TO_BOOL 451
#define _TO_BOOL_BOOL TO_BOOL_BOOL
#define _TO_BOOL_INT TO_BOOL_INT
#define _TO_BOOL_LIST TO_BOOL_LIST
@@ -280,13 +280,13 @@ extern "C" {
#define _UNARY_NEGATIVE UNARY_NEGATIVE
#define _UNARY_NOT UNARY_NOT
#define _UNPACK_EX UNPACK_EX
-#define _UNPACK_SEQUENCE 451
+#define _UNPACK_SEQUENCE 452
#define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST
#define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE
#define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE
#define _WITH_EXCEPT_START WITH_EXCEPT_START
#define _YIELD_VALUE YIELD_VALUE
-#define MAX_UOP_ID 451
+#define MAX_UOP_ID 452
#ifdef __cplusplus
}
diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h
index e86bae1d72d1a9..14befe59f04a1e 100644
--- a/Include/internal/pycore_uop_metadata.h
+++ b/Include/internal/pycore_uop_metadata.h
@@ -151,6 +151,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
[_LOAD_ATTR_CLASS_0] = 0,
[_LOAD_ATTR_CLASS_1] = 0,
[_LOAD_ATTR_CLASS] = HAS_ARG_FLAG | HAS_OPARG_AND_1_FLAG,
+ [_LOAD_ATTR_PROPERTY_FRAME] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG,
[_GUARD_DORV_NO_DICT] = HAS_DEOPT_FLAG,
[_STORE_ATTR_INSTANCE_VALUE] = 0,
[_STORE_ATTR_WITH_HINT] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG,
@@ -420,6 +421,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
[_LOAD_ATTR_MODULE] = "_LOAD_ATTR_MODULE",
[_LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = "_LOAD_ATTR_NONDESCRIPTOR_NO_DICT",
[_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = "_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES",
+ [_LOAD_ATTR_PROPERTY_FRAME] = "_LOAD_ATTR_PROPERTY_FRAME",
[_LOAD_ATTR_SLOT] = "_LOAD_ATTR_SLOT",
[_LOAD_ATTR_SLOT_0] = "_LOAD_ATTR_SLOT_0",
[_LOAD_ATTR_SLOT_1] = "_LOAD_ATTR_SLOT_1",
@@ -788,6 +790,8 @@ int _PyUop_num_popped(int opcode, int oparg)
return 1;
case _LOAD_ATTR_CLASS:
return 1;
+ case _LOAD_ATTR_PROPERTY_FRAME:
+ return 1;
case _GUARD_DORV_NO_DICT:
return 1;
case _STORE_ATTR_INSTANCE_VALUE:
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index b161fc0ede1dff..d356fc9bfdddba 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -2243,32 +2243,30 @@ dummy_func(
unused/2 +
_LOAD_ATTR_CLASS;
- inst(LOAD_ATTR_PROPERTY, (unused/1, type_version/2, func_version/2, fget/4, owner -- unused, unused if (0))) {
- PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
-
+ op(_LOAD_ATTR_PROPERTY_FRAME, (fget/4, owner -- new_frame: _PyInterpreterFrame *)) {
assert((oparg & 1) == 0);
- DEOPT_IF(tstate->interp->eval_frame);
-
- PyTypeObject *cls = Py_TYPE(owner_o);
- assert(type_version != 0);
- DEOPT_IF(cls->tp_version_tag != type_version);
assert(Py_IS_TYPE(fget, &PyFunction_Type));
PyFunctionObject *f = (PyFunctionObject *)fget;
- assert(func_version != 0);
- DEOPT_IF(f->func_version != func_version);
PyCodeObject *code = (PyCodeObject *)f->func_code;
- assert(code->co_argcount == 1);
+ DEOPT_IF((code->co_flags & (CO_VARKEYWORDS | CO_VARARGS | CO_OPTIMIZED)) != CO_OPTIMIZED);
+ DEOPT_IF(code->co_kwonlyargcount);
+ DEOPT_IF(code->co_argcount != 1);
DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize));
STAT_INC(LOAD_ATTR, hit);
Py_INCREF(fget);
- _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 1);
- // Manipulate stack directly because we exit with DISPATCH_INLINED().
- STACK_SHRINK(1);
+ new_frame = _PyFrame_PushUnchecked(tstate, f, 1);
new_frame->localsplus[0] = owner;
- frame->return_offset = (uint16_t)(next_instr - this_instr);
- DISPATCH_INLINED(new_frame);
}
+ macro(LOAD_ATTR_PROPERTY) =
+ unused/1 +
+ _CHECK_PEP_523 +
+ _GUARD_TYPE_VERSION +
+ unused/2 +
+ _LOAD_ATTR_PROPERTY_FRAME +
+ _SAVE_RETURN_OFFSET +
+ _PUSH_FRAME;
+
inst(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN, (unused/1, type_version/2, func_version/2, getattribute/4, owner -- unused, unused if (0))) {
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index 87c9255ef7997d..b8343f9ffd5f80 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -2507,7 +2507,39 @@
/* _LOAD_ATTR_CLASS is split on (oparg & 1) */
- /* _LOAD_ATTR_PROPERTY is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */
+ case _LOAD_ATTR_PROPERTY_FRAME: {
+ _PyStackRef owner;
+ _PyInterpreterFrame *new_frame;
+ oparg = CURRENT_OPARG();
+ owner = stack_pointer[-1];
+ PyObject *fget = (PyObject *)CURRENT_OPERAND();
+ assert((oparg & 1) == 0);
+ assert(Py_IS_TYPE(fget, &PyFunction_Type));
+ PyFunctionObject *f = (PyFunctionObject *)fget;
+ PyCodeObject *code = (PyCodeObject *)f->func_code;
+ if ((code->co_flags & (CO_VARKEYWORDS | CO_VARARGS | CO_OPTIMIZED)) != CO_OPTIMIZED) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
+ if (code->co_kwonlyargcount) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
+ if (code->co_argcount != 1) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
+ if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
+ STAT_INC(LOAD_ATTR, hit);
+ Py_INCREF(fget);
+ new_frame = _PyFrame_PushUnchecked(tstate, f, 1);
+ new_frame->localsplus[0] = owner;
+ stack_pointer[-1].bits = (uintptr_t)new_frame;
+ break;
+ }
/* _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index f15a829ea3ebfa..6f996f91921cd3 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -4429,32 +4429,63 @@
INSTRUCTION_STATS(LOAD_ATTR_PROPERTY);
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
_PyStackRef owner;
+ _PyInterpreterFrame *new_frame;
/* Skip 1 cache entry */
+ // _CHECK_PEP_523
+ {
+ DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR);
+ }
+ // _GUARD_TYPE_VERSION
owner = stack_pointer[-1];
- uint32_t type_version = read_u32(&this_instr[2].cache);
- uint32_t func_version = read_u32(&this_instr[4].cache);
- PyObject *fget = read_obj(&this_instr[6].cache);
- PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
- assert((oparg & 1) == 0);
- DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR);
- PyTypeObject *cls = Py_TYPE(owner_o);
- assert(type_version != 0);
- DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR);
- assert(Py_IS_TYPE(fget, &PyFunction_Type));
- PyFunctionObject *f = (PyFunctionObject *)fget;
- assert(func_version != 0);
- DEOPT_IF(f->func_version != func_version, LOAD_ATTR);
- PyCodeObject *code = (PyCodeObject *)f->func_code;
- assert(code->co_argcount == 1);
- DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR);
- STAT_INC(LOAD_ATTR, hit);
- Py_INCREF(fget);
- _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 1);
- // Manipulate stack directly because we exit with DISPATCH_INLINED().
- STACK_SHRINK(1);
- new_frame->localsplus[0] = owner;
- frame->return_offset = (uint16_t)(next_instr - this_instr);
- DISPATCH_INLINED(new_frame);
+ {
+ uint32_t type_version = read_u32(&this_instr[2].cache);
+ PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner));
+ assert(type_version != 0);
+ DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR);
+ }
+ /* Skip 2 cache entries */
+ // _LOAD_ATTR_PROPERTY_FRAME
+ {
+ PyObject *fget = read_obj(&this_instr[6].cache);
+ assert((oparg & 1) == 0);
+ assert(Py_IS_TYPE(fget, &PyFunction_Type));
+ PyFunctionObject *f = (PyFunctionObject *)fget;
+ PyCodeObject *code = (PyCodeObject *)f->func_code;
+ DEOPT_IF((code->co_flags & (CO_VARKEYWORDS | CO_VARARGS | CO_OPTIMIZED)) != CO_OPTIMIZED, LOAD_ATTR);
+ DEOPT_IF(code->co_kwonlyargcount, LOAD_ATTR);
+ DEOPT_IF(code->co_argcount != 1, LOAD_ATTR);
+ DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR);
+ STAT_INC(LOAD_ATTR, hit);
+ Py_INCREF(fget);
+ new_frame = _PyFrame_PushUnchecked(tstate, f, 1);
+ new_frame->localsplus[0] = owner;
+ }
+ // _SAVE_RETURN_OFFSET
+ {
+ #if TIER_ONE
+ frame->return_offset = (uint16_t)(next_instr - this_instr);
+ #endif
+ #if TIER_TWO
+ frame->return_offset = oparg;
+ #endif
+ }
+ // _PUSH_FRAME
+ {
+ // Write it out explicitly because it's subtly different.
+ // Eventually this should be the only occurrence of this code.
+ assert(tstate->interp->eval_frame == NULL);
+ stack_pointer += -1;
+ assert(WITHIN_STACK_BOUNDS());
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ new_frame->previous = frame;
+ CALL_STAT_INC(inlined_py_calls);
+ frame = tstate->current_frame = new_frame;
+ tstate->py_recursion_remaining--;
+ LOAD_SP();
+ LOAD_IP(0);
+ LLTRACE_RESUME_FRAME();
+ }
+ DISPATCH();
}
TARGET(LOAD_ATTR_SLOT) {
diff --git a/Python/optimizer.c b/Python/optimizer.c
index 73316b3587f221..e08c1dc9936a3d 100644
--- a/Python/optimizer.c
+++ b/Python/optimizer.c
@@ -797,7 +797,10 @@ translate_bytecode_to_trace(
if (uop == _PUSH_FRAME) {
assert(i + 1 == nuops);
- if (opcode == FOR_ITER_GEN || opcode == SEND_GEN) {
+ if (opcode == FOR_ITER_GEN ||
+ opcode == LOAD_ATTR_PROPERTY ||
+ opcode == SEND_GEN)
+ {
DPRINTF(2, "Bailing due to dynamic target\n");
ADD_TO_TRACE(uop, oparg, 0, target);
ADD_TO_TRACE(_DYNAMIC_EXIT, 0, 0, 0);
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 8077badce78a39..8c2b1ac7926cec 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -1160,7 +1160,12 @@
break;
}
- /* _LOAD_ATTR_PROPERTY is not a viable micro-op for tier 2 */
+ case _LOAD_ATTR_PROPERTY_FRAME: {
+ _PyInterpreterFrame *new_frame;
+ new_frame = sym_new_not_null(ctx);
+ stack_pointer[-1] = (_Py_UopsSymbol *)new_frame;
+ break;
+ }
/* _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN is not a viable micro-op for tier 2 */
diff --git a/Python/specialize.c b/Python/specialize.c
index 3af0deabb9b40a..c354a9079019ac 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -963,15 +963,10 @@ _Py_Specialize_LoadAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *nam
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_METHOD);
goto fail;
}
- uint32_t version = function_get_version(fget, LOAD_ATTR);
- if (version == 0) {
- goto fail;
- }
if (_PyInterpreterState_GET()->eval_frame) {
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER);
goto fail;
}
- write_u32(lm_cache->keys_version, version);
assert(type->tp_version_tag != 0);
write_u32(lm_cache->type_version, type->tp_version_tag);
/* borrowed */
From 1d607fe759ef22177b50d734ae029df3903c99e0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sviatoslav=20Sydorenko=20=28=D0=A1=D0=B2=D1=8F=D1=82=D0=BE?=
=?UTF-8?q?=D1=81=D0=BB=D0=B0=D0=B2=20=D0=A1=D0=B8=D0=B4=D0=BE=D1=80=D0=B5?=
=?UTF-8?q?=D0=BD=D0=BA=D0=BE=29?=
Date: Thu, 25 Jul 2024 22:27:26 +0200
Subject: [PATCH 12/47] Move macOS matrix to the calling workflow (#121809)
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
---
.github/workflows/build.yml | 45 +++++++++++++++++-----------
.github/workflows/reusable-macos.yml | 22 ++++----------
2 files changed, 33 insertions(+), 34 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 5c894abda71a87..613578ae176ad9 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -249,27 +249,38 @@ jobs:
arch: ${{ matrix.arch }}
build_macos:
- name: 'macOS'
- needs: check_source
- if: needs.check_source.outputs.run_tests == 'true'
- uses: ./.github/workflows/reusable-macos.yml
- with:
- config_hash: ${{ needs.check_source.outputs.config_hash }}
- # Cirrus and macos-14 are M1, macos-13 is default GHA Intel.
- # Cirrus used for upstream, macos-14 for forks.
- os-matrix: '["ghcr.io/cirruslabs/macos-runner:sonoma", "macos-14", "macos-13"]'
-
- build_macos_free_threading:
- name: 'macOS (free-threading)'
+ name: >-
+ macOS
+ ${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }}
needs: check_source
if: needs.check_source.outputs.run_tests == 'true'
+ strategy:
+ fail-fast: false
+ matrix:
+ # Cirrus and macos-14 are M1, macos-13 is default GHA Intel.
+ # macOS 13 only runs tests against the GIL-enabled CPython.
+ # Cirrus used for upstream, macos-14 for forks.
+ os:
+ - ghcr.io/cirruslabs/macos-runner:sonoma
+ - macos-14
+ - macos-13
+ is-fork: # only used for the exclusion trick
+ - ${{ github.repository_owner != 'python' }}
+ free-threading:
+ - false
+ - true
+ exclude:
+ - os: ghcr.io/cirruslabs/macos-runner:sonoma
+ is-fork: true
+ - os: macos-14
+ is-fork: false
+ - os: macos-13
+ free-threading: true
uses: ./.github/workflows/reusable-macos.yml
with:
config_hash: ${{ needs.check_source.outputs.config_hash }}
- free-threading: true
- # Cirrus and macos-14 are M1.
- # Cirrus used for upstream, macos-14 for forks.
- os-matrix: '["ghcr.io/cirruslabs/macos-runner:sonoma", "macos-14"]'
+ free-threading: ${{ matrix.free-threading }}
+ os: ${{ matrix.os }}
build_ubuntu:
name: >-
@@ -596,7 +607,6 @@ jobs:
- check-docs
- check_generated_files
- build_macos
- - build_macos_free_threading
- build_ubuntu
- build_ubuntu_ssltests
- build_wasi
@@ -632,7 +642,6 @@ jobs:
&& '
check_generated_files,
build_macos,
- build_macos_free_threading,
build_ubuntu,
build_ubuntu_ssltests,
build_wasi,
diff --git a/.github/workflows/reusable-macos.yml b/.github/workflows/reusable-macos.yml
index 0f189960dbea61..64ef2c91329d81 100644
--- a/.github/workflows/reusable-macos.yml
+++ b/.github/workflows/reusable-macos.yml
@@ -8,13 +8,14 @@ on:
required: false
type: boolean
default: false
- os-matrix:
- required: false
+ os:
+ description: OS to run the job
+ required: true
type: string
jobs:
build_macos:
- name: build and test (${{ matrix.os }})
+ name: build and test (${{ inputs.os }})
timeout-minutes: 60
env:
HOMEBREW_NO_ANALYTICS: 1
@@ -23,18 +24,7 @@ jobs:
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
PYTHONSTRICTEXTENSIONBUILD: 1
TERM: linux
- strategy:
- fail-fast: false
- matrix:
- os: ${{fromJson(inputs.os-matrix)}}
- is-fork:
- - ${{ github.repository_owner != 'python' }}
- exclude:
- - os: "ghcr.io/cirruslabs/macos-runner:sonoma"
- is-fork: true
- - os: "macos-14"
- is-fork: false
- runs-on: ${{ matrix.os }}
+ runs-on: ${{ inputs.os }}
steps:
- uses: actions/checkout@v4
- name: Runner image version
@@ -43,7 +33,7 @@ jobs:
uses: actions/cache@v4
with:
path: config.cache
- key: ${{ github.job }}-${{ matrix.os }}-${{ env.IMAGE_VERSION }}-${{ inputs.config_hash }}
+ key: ${{ github.job }}-${{ inputs.os }}-${{ env.IMAGE_VERSION }}-${{ inputs.config_hash }}
- name: Install Homebrew dependencies
run: brew install pkg-config openssl@3.0 xz gdbm tcl-tk
- name: Configure CPython
From d9efa45d7457b0dfea467bb1c2d22c69056ffc73 Mon Sep 17 00:00:00 2001
From: Brandt Bucher
Date: Thu, 25 Jul 2024 14:45:07 -0700
Subject: [PATCH 13/47] GH-118093: Add tier two support for
BINARY_OP_INPLACE_ADD_UNICODE (GH-122253)
---
Include/internal/pycore_opcode_metadata.h | 1 +
Include/internal/pycore_uop_ids.h | 297 +++++++++++-----------
Include/internal/pycore_uop_metadata.h | 4 +
Python/bytecodes.c | 15 +-
Python/executor_cases.c.h | 49 ++++
Python/generated_cases.c.h | 13 +-
Python/optimizer.c | 9 +
Python/optimizer_cases.c.h | 6 +
8 files changed, 241 insertions(+), 153 deletions(-)
diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h
index 2fb6b2c4ed8205..9c7ad926f9a980 100644
--- a/Include/internal/pycore_opcode_metadata.h
+++ b/Include/internal/pycore_opcode_metadata.h
@@ -1218,6 +1218,7 @@ _PyOpcode_macro_expansion[256] = {
[BINARY_OP_ADD_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_ADD_FLOAT, 0, 0 } } },
[BINARY_OP_ADD_INT] = { .nuops = 2, .uops = { { _GUARD_BOTH_INT, 0, 0 }, { _BINARY_OP_ADD_INT, 0, 0 } } },
[BINARY_OP_ADD_UNICODE] = { .nuops = 2, .uops = { { _GUARD_BOTH_UNICODE, 0, 0 }, { _BINARY_OP_ADD_UNICODE, 0, 0 } } },
+ [BINARY_OP_INPLACE_ADD_UNICODE] = { .nuops = 2, .uops = { { _GUARD_BOTH_UNICODE, 0, 0 }, { _BINARY_OP_INPLACE_ADD_UNICODE, 0, 0 } } },
[BINARY_OP_MULTIPLY_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_MULTIPLY_FLOAT, 0, 0 } } },
[BINARY_OP_MULTIPLY_INT] = { .nuops = 2, .uops = { { _GUARD_BOTH_INT, 0, 0 }, { _BINARY_OP_MULTIPLY_INT, 0, 0 } } },
[BINARY_OP_SUBTRACT_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_SUBTRACT_FLOAT, 0, 0 } } },
diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h
index 88c835ca8ed10c..9ae82ca3c3dafa 100644
--- a/Include/internal/pycore_uop_ids.h
+++ b/Include/internal/pycore_uop_ids.h
@@ -15,12 +15,13 @@ extern "C" {
#define _BINARY_OP_ADD_FLOAT 303
#define _BINARY_OP_ADD_INT 304
#define _BINARY_OP_ADD_UNICODE 305
-#define _BINARY_OP_MULTIPLY_FLOAT 306
-#define _BINARY_OP_MULTIPLY_INT 307
-#define _BINARY_OP_SUBTRACT_FLOAT 308
-#define _BINARY_OP_SUBTRACT_INT 309
+#define _BINARY_OP_INPLACE_ADD_UNICODE 306
+#define _BINARY_OP_MULTIPLY_FLOAT 307
+#define _BINARY_OP_MULTIPLY_INT 308
+#define _BINARY_OP_SUBTRACT_FLOAT 309
+#define _BINARY_OP_SUBTRACT_INT 310
#define _BINARY_SLICE BINARY_SLICE
-#define _BINARY_SUBSCR 310
+#define _BINARY_SUBSCR 311
#define _BINARY_SUBSCR_DICT BINARY_SUBSCR_DICT
#define _BINARY_SUBSCR_GETITEM BINARY_SUBSCR_GETITEM
#define _BINARY_SUBSCR_LIST_INT BINARY_SUBSCR_LIST_INT
@@ -32,12 +33,12 @@ extern "C" {
#define _BUILD_SLICE BUILD_SLICE
#define _BUILD_STRING BUILD_STRING
#define _BUILD_TUPLE BUILD_TUPLE
-#define _CALL 311
+#define _CALL 312
#define _CALL_ALLOC_AND_ENTER_INIT CALL_ALLOC_AND_ENTER_INIT
-#define _CALL_BUILTIN_CLASS 312
-#define _CALL_BUILTIN_FAST 313
-#define _CALL_BUILTIN_FAST_WITH_KEYWORDS 314
-#define _CALL_BUILTIN_O 315
+#define _CALL_BUILTIN_CLASS 313
+#define _CALL_BUILTIN_FAST 314
+#define _CALL_BUILTIN_FAST_WITH_KEYWORDS 315
+#define _CALL_BUILTIN_O 316
#define _CALL_FUNCTION_EX CALL_FUNCTION_EX
#define _CALL_INTRINSIC_1 CALL_INTRINSIC_1
#define _CALL_INTRINSIC_2 CALL_INTRINSIC_2
@@ -45,38 +46,38 @@ extern "C" {
#define _CALL_KW CALL_KW
#define _CALL_LEN CALL_LEN
#define _CALL_LIST_APPEND CALL_LIST_APPEND
-#define _CALL_METHOD_DESCRIPTOR_FAST 316
-#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 317
-#define _CALL_METHOD_DESCRIPTOR_NOARGS 318
-#define _CALL_METHOD_DESCRIPTOR_O 319
-#define _CALL_NON_PY_GENERAL 320
-#define _CALL_STR_1 321
-#define _CALL_TUPLE_1 322
+#define _CALL_METHOD_DESCRIPTOR_FAST 317
+#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 318
+#define _CALL_METHOD_DESCRIPTOR_NOARGS 319
+#define _CALL_METHOD_DESCRIPTOR_O 320
+#define _CALL_NON_PY_GENERAL 321
+#define _CALL_STR_1 322
+#define _CALL_TUPLE_1 323
#define _CALL_TYPE_1 CALL_TYPE_1
-#define _CHECK_ATTR_CLASS 323
-#define _CHECK_ATTR_METHOD_LAZY_DICT 324
-#define _CHECK_ATTR_MODULE 325
-#define _CHECK_ATTR_WITH_HINT 326
-#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 327
+#define _CHECK_ATTR_CLASS 324
+#define _CHECK_ATTR_METHOD_LAZY_DICT 325
+#define _CHECK_ATTR_MODULE 326
+#define _CHECK_ATTR_WITH_HINT 327
+#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 328
#define _CHECK_EG_MATCH CHECK_EG_MATCH
#define _CHECK_EXC_MATCH CHECK_EXC_MATCH
-#define _CHECK_FUNCTION 328
-#define _CHECK_FUNCTION_EXACT_ARGS 329
-#define _CHECK_FUNCTION_VERSION 330
-#define _CHECK_IS_NOT_PY_CALLABLE 331
-#define _CHECK_MANAGED_OBJECT_HAS_VALUES 332
-#define _CHECK_METHOD_VERSION 333
-#define _CHECK_PEP_523 334
-#define _CHECK_PERIODIC 335
-#define _CHECK_STACK_SPACE 336
-#define _CHECK_STACK_SPACE_OPERAND 337
-#define _CHECK_VALIDITY 338
-#define _CHECK_VALIDITY_AND_SET_IP 339
-#define _COMPARE_OP 340
-#define _COMPARE_OP_FLOAT 341
-#define _COMPARE_OP_INT 342
-#define _COMPARE_OP_STR 343
-#define _CONTAINS_OP 344
+#define _CHECK_FUNCTION 329
+#define _CHECK_FUNCTION_EXACT_ARGS 330
+#define _CHECK_FUNCTION_VERSION 331
+#define _CHECK_IS_NOT_PY_CALLABLE 332
+#define _CHECK_MANAGED_OBJECT_HAS_VALUES 333
+#define _CHECK_METHOD_VERSION 334
+#define _CHECK_PEP_523 335
+#define _CHECK_PERIODIC 336
+#define _CHECK_STACK_SPACE 337
+#define _CHECK_STACK_SPACE_OPERAND 338
+#define _CHECK_VALIDITY 339
+#define _CHECK_VALIDITY_AND_SET_IP 340
+#define _COMPARE_OP 341
+#define _COMPARE_OP_FLOAT 342
+#define _COMPARE_OP_INT 343
+#define _COMPARE_OP_STR 344
+#define _CONTAINS_OP 345
#define _CONTAINS_OP_DICT CONTAINS_OP_DICT
#define _CONTAINS_OP_SET CONTAINS_OP_SET
#define _CONVERT_VALUE CONVERT_VALUE
@@ -88,55 +89,55 @@ extern "C" {
#define _DELETE_GLOBAL DELETE_GLOBAL
#define _DELETE_NAME DELETE_NAME
#define _DELETE_SUBSCR DELETE_SUBSCR
-#define _DEOPT 345
+#define _DEOPT 346
#define _DICT_MERGE DICT_MERGE
#define _DICT_UPDATE DICT_UPDATE
-#define _DYNAMIC_EXIT 346
+#define _DYNAMIC_EXIT 347
#define _END_SEND END_SEND
-#define _ERROR_POP_N 347
+#define _ERROR_POP_N 348
#define _EXIT_INIT_CHECK EXIT_INIT_CHECK
-#define _EXPAND_METHOD 348
-#define _FATAL_ERROR 349
+#define _EXPAND_METHOD 349
+#define _FATAL_ERROR 350
#define _FORMAT_SIMPLE FORMAT_SIMPLE
#define _FORMAT_WITH_SPEC FORMAT_WITH_SPEC
-#define _FOR_ITER 350
-#define _FOR_ITER_GEN_FRAME 351
-#define _FOR_ITER_TIER_TWO 352
+#define _FOR_ITER 351
+#define _FOR_ITER_GEN_FRAME 352
+#define _FOR_ITER_TIER_TWO 353
#define _GET_AITER GET_AITER
#define _GET_ANEXT GET_ANEXT
#define _GET_AWAITABLE GET_AWAITABLE
#define _GET_ITER GET_ITER
#define _GET_LEN GET_LEN
#define _GET_YIELD_FROM_ITER GET_YIELD_FROM_ITER
-#define _GUARD_BOTH_FLOAT 353
-#define _GUARD_BOTH_INT 354
-#define _GUARD_BOTH_UNICODE 355
-#define _GUARD_BUILTINS_VERSION 356
-#define _GUARD_DORV_NO_DICT 357
-#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 358
-#define _GUARD_GLOBALS_VERSION 359
-#define _GUARD_IS_FALSE_POP 360
-#define _GUARD_IS_NONE_POP 361
-#define _GUARD_IS_NOT_NONE_POP 362
-#define _GUARD_IS_TRUE_POP 363
-#define _GUARD_KEYS_VERSION 364
-#define _GUARD_NOS_FLOAT 365
-#define _GUARD_NOS_INT 366
-#define _GUARD_NOT_EXHAUSTED_LIST 367
-#define _GUARD_NOT_EXHAUSTED_RANGE 368
-#define _GUARD_NOT_EXHAUSTED_TUPLE 369
-#define _GUARD_TOS_FLOAT 370
-#define _GUARD_TOS_INT 371
-#define _GUARD_TYPE_VERSION 372
+#define _GUARD_BOTH_FLOAT 354
+#define _GUARD_BOTH_INT 355
+#define _GUARD_BOTH_UNICODE 356
+#define _GUARD_BUILTINS_VERSION 357
+#define _GUARD_DORV_NO_DICT 358
+#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 359
+#define _GUARD_GLOBALS_VERSION 360
+#define _GUARD_IS_FALSE_POP 361
+#define _GUARD_IS_NONE_POP 362
+#define _GUARD_IS_NOT_NONE_POP 363
+#define _GUARD_IS_TRUE_POP 364
+#define _GUARD_KEYS_VERSION 365
+#define _GUARD_NOS_FLOAT 366
+#define _GUARD_NOS_INT 367
+#define _GUARD_NOT_EXHAUSTED_LIST 368
+#define _GUARD_NOT_EXHAUSTED_RANGE 369
+#define _GUARD_NOT_EXHAUSTED_TUPLE 370
+#define _GUARD_TOS_FLOAT 371
+#define _GUARD_TOS_INT 372
+#define _GUARD_TYPE_VERSION 373
#define _IMPORT_FROM IMPORT_FROM
#define _IMPORT_NAME IMPORT_NAME
-#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 373
-#define _INIT_CALL_PY_EXACT_ARGS 374
-#define _INIT_CALL_PY_EXACT_ARGS_0 375
-#define _INIT_CALL_PY_EXACT_ARGS_1 376
-#define _INIT_CALL_PY_EXACT_ARGS_2 377
-#define _INIT_CALL_PY_EXACT_ARGS_3 378
-#define _INIT_CALL_PY_EXACT_ARGS_4 379
+#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 374
+#define _INIT_CALL_PY_EXACT_ARGS 375
+#define _INIT_CALL_PY_EXACT_ARGS_0 376
+#define _INIT_CALL_PY_EXACT_ARGS_1 377
+#define _INIT_CALL_PY_EXACT_ARGS_2 378
+#define _INIT_CALL_PY_EXACT_ARGS_3 379
+#define _INIT_CALL_PY_EXACT_ARGS_4 380
#define _INSTRUMENTED_CALL INSTRUMENTED_CALL
#define _INSTRUMENTED_CALL_FUNCTION_EX INSTRUMENTED_CALL_FUNCTION_EX
#define _INSTRUMENTED_CALL_KW INSTRUMENTED_CALL_KW
@@ -153,65 +154,65 @@ extern "C" {
#define _INSTRUMENTED_RETURN_CONST INSTRUMENTED_RETURN_CONST
#define _INSTRUMENTED_RETURN_VALUE INSTRUMENTED_RETURN_VALUE
#define _INSTRUMENTED_YIELD_VALUE INSTRUMENTED_YIELD_VALUE
-#define _INTERNAL_INCREMENT_OPT_COUNTER 380
-#define _IS_NONE 381
+#define _INTERNAL_INCREMENT_OPT_COUNTER 381
+#define _IS_NONE 382
#define _IS_OP IS_OP
-#define _ITER_CHECK_LIST 382
-#define _ITER_CHECK_RANGE 383
-#define _ITER_CHECK_TUPLE 384
-#define _ITER_JUMP_LIST 385
-#define _ITER_JUMP_RANGE 386
-#define _ITER_JUMP_TUPLE 387
-#define _ITER_NEXT_LIST 388
-#define _ITER_NEXT_RANGE 389
-#define _ITER_NEXT_TUPLE 390
-#define _JUMP_TO_TOP 391
+#define _ITER_CHECK_LIST 383
+#define _ITER_CHECK_RANGE 384
+#define _ITER_CHECK_TUPLE 385
+#define _ITER_JUMP_LIST 386
+#define _ITER_JUMP_RANGE 387
+#define _ITER_JUMP_TUPLE 388
+#define _ITER_NEXT_LIST 389
+#define _ITER_NEXT_RANGE 390
+#define _ITER_NEXT_TUPLE 391
+#define _JUMP_TO_TOP 392
#define _LIST_APPEND LIST_APPEND
#define _LIST_EXTEND LIST_EXTEND
-#define _LOAD_ATTR 392
-#define _LOAD_ATTR_CLASS 393
-#define _LOAD_ATTR_CLASS_0 394
-#define _LOAD_ATTR_CLASS_1 395
+#define _LOAD_ATTR 393
+#define _LOAD_ATTR_CLASS 394
+#define _LOAD_ATTR_CLASS_0 395
+#define _LOAD_ATTR_CLASS_1 396
#define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN
-#define _LOAD_ATTR_INSTANCE_VALUE 396
-#define _LOAD_ATTR_INSTANCE_VALUE_0 397
-#define _LOAD_ATTR_INSTANCE_VALUE_1 398
-#define _LOAD_ATTR_METHOD_LAZY_DICT 399
-#define _LOAD_ATTR_METHOD_NO_DICT 400
-#define _LOAD_ATTR_METHOD_WITH_VALUES 401
-#define _LOAD_ATTR_MODULE 402
-#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 403
-#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 404
-#define _LOAD_ATTR_PROPERTY_FRAME 405
-#define _LOAD_ATTR_SLOT 406
-#define _LOAD_ATTR_SLOT_0 407
-#define _LOAD_ATTR_SLOT_1 408
-#define _LOAD_ATTR_WITH_HINT 409
+#define _LOAD_ATTR_INSTANCE_VALUE 397
+#define _LOAD_ATTR_INSTANCE_VALUE_0 398
+#define _LOAD_ATTR_INSTANCE_VALUE_1 399
+#define _LOAD_ATTR_METHOD_LAZY_DICT 400
+#define _LOAD_ATTR_METHOD_NO_DICT 401
+#define _LOAD_ATTR_METHOD_WITH_VALUES 402
+#define _LOAD_ATTR_MODULE 403
+#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 404
+#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 405
+#define _LOAD_ATTR_PROPERTY_FRAME 406
+#define _LOAD_ATTR_SLOT 407
+#define _LOAD_ATTR_SLOT_0 408
+#define _LOAD_ATTR_SLOT_1 409
+#define _LOAD_ATTR_WITH_HINT 410
#define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS
#define _LOAD_COMMON_CONSTANT LOAD_COMMON_CONSTANT
#define _LOAD_CONST LOAD_CONST
-#define _LOAD_CONST_INLINE 410
-#define _LOAD_CONST_INLINE_BORROW 411
-#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 412
-#define _LOAD_CONST_INLINE_WITH_NULL 413
+#define _LOAD_CONST_INLINE 411
+#define _LOAD_CONST_INLINE_BORROW 412
+#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 413
+#define _LOAD_CONST_INLINE_WITH_NULL 414
#define _LOAD_DEREF LOAD_DEREF
-#define _LOAD_FAST 414
-#define _LOAD_FAST_0 415
-#define _LOAD_FAST_1 416
-#define _LOAD_FAST_2 417
-#define _LOAD_FAST_3 418
-#define _LOAD_FAST_4 419
-#define _LOAD_FAST_5 420
-#define _LOAD_FAST_6 421
-#define _LOAD_FAST_7 422
+#define _LOAD_FAST 415
+#define _LOAD_FAST_0 416
+#define _LOAD_FAST_1 417
+#define _LOAD_FAST_2 418
+#define _LOAD_FAST_3 419
+#define _LOAD_FAST_4 420
+#define _LOAD_FAST_5 421
+#define _LOAD_FAST_6 422
+#define _LOAD_FAST_7 423
#define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR
#define _LOAD_FAST_CHECK LOAD_FAST_CHECK
#define _LOAD_FAST_LOAD_FAST LOAD_FAST_LOAD_FAST
#define _LOAD_FROM_DICT_OR_DEREF LOAD_FROM_DICT_OR_DEREF
#define _LOAD_FROM_DICT_OR_GLOBALS LOAD_FROM_DICT_OR_GLOBALS
-#define _LOAD_GLOBAL 423
-#define _LOAD_GLOBAL_BUILTINS 424
-#define _LOAD_GLOBAL_MODULE 425
+#define _LOAD_GLOBAL 424
+#define _LOAD_GLOBAL_BUILTINS 425
+#define _LOAD_GLOBAL_MODULE 426
#define _LOAD_LOCALS LOAD_LOCALS
#define _LOAD_NAME LOAD_NAME
#define _LOAD_SPECIAL LOAD_SPECIAL
@@ -226,51 +227,51 @@ extern "C" {
#define _MATCH_SEQUENCE MATCH_SEQUENCE
#define _NOP NOP
#define _POP_EXCEPT POP_EXCEPT
-#define _POP_JUMP_IF_FALSE 426
-#define _POP_JUMP_IF_TRUE 427
+#define _POP_JUMP_IF_FALSE 427
+#define _POP_JUMP_IF_TRUE 428
#define _POP_TOP POP_TOP
-#define _POP_TOP_LOAD_CONST_INLINE_BORROW 428
+#define _POP_TOP_LOAD_CONST_INLINE_BORROW 429
#define _PUSH_EXC_INFO PUSH_EXC_INFO
-#define _PUSH_FRAME 429
+#define _PUSH_FRAME 430
#define _PUSH_NULL PUSH_NULL
-#define _PY_FRAME_GENERAL 430
-#define _REPLACE_WITH_TRUE 431
+#define _PY_FRAME_GENERAL 431
+#define _REPLACE_WITH_TRUE 432
#define _RESUME_CHECK RESUME_CHECK
#define _RETURN_GENERATOR RETURN_GENERATOR
#define _RETURN_VALUE RETURN_VALUE
-#define _SAVE_RETURN_OFFSET 432
-#define _SEND 433
-#define _SEND_GEN_FRAME 434
+#define _SAVE_RETURN_OFFSET 433
+#define _SEND 434
+#define _SEND_GEN_FRAME 435
#define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS
#define _SET_ADD SET_ADD
#define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE
#define _SET_UPDATE SET_UPDATE
-#define _START_EXECUTOR 435
-#define _STORE_ATTR 436
-#define _STORE_ATTR_INSTANCE_VALUE 437
-#define _STORE_ATTR_SLOT 438
-#define _STORE_ATTR_WITH_HINT 439
+#define _START_EXECUTOR 436
+#define _STORE_ATTR 437
+#define _STORE_ATTR_INSTANCE_VALUE 438
+#define _STORE_ATTR_SLOT 439
+#define _STORE_ATTR_WITH_HINT 440
#define _STORE_DEREF STORE_DEREF
-#define _STORE_FAST 440
-#define _STORE_FAST_0 441
-#define _STORE_FAST_1 442
-#define _STORE_FAST_2 443
-#define _STORE_FAST_3 444
-#define _STORE_FAST_4 445
-#define _STORE_FAST_5 446
-#define _STORE_FAST_6 447
-#define _STORE_FAST_7 448
+#define _STORE_FAST 441
+#define _STORE_FAST_0 442
+#define _STORE_FAST_1 443
+#define _STORE_FAST_2 444
+#define _STORE_FAST_3 445
+#define _STORE_FAST_4 446
+#define _STORE_FAST_5 447
+#define _STORE_FAST_6 448
+#define _STORE_FAST_7 449
#define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST
#define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST
#define _STORE_GLOBAL STORE_GLOBAL
#define _STORE_NAME STORE_NAME
#define _STORE_SLICE STORE_SLICE
-#define _STORE_SUBSCR 449
+#define _STORE_SUBSCR 450
#define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT
#define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT
#define _SWAP SWAP
-#define _TIER2_RESUME_CHECK 450
-#define _TO_BOOL 451
+#define _TIER2_RESUME_CHECK 451
+#define _TO_BOOL 452
#define _TO_BOOL_BOOL TO_BOOL_BOOL
#define _TO_BOOL_INT TO_BOOL_INT
#define _TO_BOOL_LIST TO_BOOL_LIST
@@ -280,13 +281,13 @@ extern "C" {
#define _UNARY_NEGATIVE UNARY_NEGATIVE
#define _UNARY_NOT UNARY_NOT
#define _UNPACK_EX UNPACK_EX
-#define _UNPACK_SEQUENCE 452
+#define _UNPACK_SEQUENCE 453
#define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST
#define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE
#define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE
#define _WITH_EXCEPT_START WITH_EXCEPT_START
#define _YIELD_VALUE YIELD_VALUE
-#define MAX_UOP_ID 452
+#define MAX_UOP_ID 453
#ifdef __cplusplus
}
diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h
index 14befe59f04a1e..190c6fb2365cc4 100644
--- a/Include/internal/pycore_uop_metadata.h
+++ b/Include/internal/pycore_uop_metadata.h
@@ -72,6 +72,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
[_BINARY_OP_SUBTRACT_FLOAT] = HAS_PURE_FLAG,
[_GUARD_BOTH_UNICODE] = HAS_EXIT_FLAG,
[_BINARY_OP_ADD_UNICODE] = HAS_ERROR_FLAG | HAS_PURE_FLAG,
+ [_BINARY_OP_INPLACE_ADD_UNICODE] = HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
[_BINARY_SUBSCR] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
[_BINARY_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
[_STORE_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
@@ -279,6 +280,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
[_BINARY_OP_ADD_FLOAT] = "_BINARY_OP_ADD_FLOAT",
[_BINARY_OP_ADD_INT] = "_BINARY_OP_ADD_INT",
[_BINARY_OP_ADD_UNICODE] = "_BINARY_OP_ADD_UNICODE",
+ [_BINARY_OP_INPLACE_ADD_UNICODE] = "_BINARY_OP_INPLACE_ADD_UNICODE",
[_BINARY_OP_MULTIPLY_FLOAT] = "_BINARY_OP_MULTIPLY_FLOAT",
[_BINARY_OP_MULTIPLY_INT] = "_BINARY_OP_MULTIPLY_INT",
[_BINARY_OP_SUBTRACT_FLOAT] = "_BINARY_OP_SUBTRACT_FLOAT",
@@ -632,6 +634,8 @@ int _PyUop_num_popped(int opcode, int oparg)
return 2;
case _BINARY_OP_ADD_UNICODE:
return 2;
+ case _BINARY_OP_INPLACE_ADD_UNICODE:
+ return 2;
case _BINARY_SUBSCR:
return 2;
case _BINARY_SLICE:
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index d356fc9bfdddba..eb8b66f8d8b2e7 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -581,12 +581,18 @@ dummy_func(
// So the inputs are the same as for all BINARY_OP
// specializations, but there is no output.
// At the end we just skip over the STORE_FAST.
- tier1 op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right --)) {
+ op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right --)) {
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
+ int next_oparg;
+ #if TIER_ONE
assert(next_instr->op.code == STORE_FAST);
- _PyStackRef *target_local = &GETLOCAL(next_instr->op.arg);
+ next_oparg = next_instr->op.arg;
+ #else
+ next_oparg = CURRENT_OPERAND();
+ #endif
+ _PyStackRef *target_local = &GETLOCAL(next_oparg);
DEOPT_IF(!PyStackRef_Is(*target_local, left));
STAT_INC(BINARY_OP, hit);
/* Handle `left = left + right` or `left += right` for str.
@@ -607,9 +613,12 @@ dummy_func(
*target_local = PyStackRef_FromPyObjectSteal(temp);
_Py_DECREF_SPECIALIZED(right_o, _PyUnicode_ExactDealloc);
ERROR_IF(PyStackRef_IsNull(*target_local), error);
- // The STORE_FAST is already done.
+ #if TIER_ONE
+ // The STORE_FAST is already done. This is done here in tier one,
+ // and during trace projection in tier two:
assert(next_instr->op.code == STORE_FAST);
SKIP_OVER(1);
+ #endif
}
macro(BINARY_OP_INPLACE_ADD_UNICODE) =
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index b8343f9ffd5f80..3379f0be2272dc 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -693,6 +693,55 @@
break;
}
+ case _BINARY_OP_INPLACE_ADD_UNICODE: {
+ _PyStackRef right;
+ _PyStackRef left;
+ right = stack_pointer[-1];
+ left = stack_pointer[-2];
+ PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
+ PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
+ int next_oparg;
+ #if TIER_ONE
+ assert(next_instr->op.code == STORE_FAST);
+ next_oparg = next_instr->op.arg;
+ #else
+ next_oparg = CURRENT_OPERAND();
+ #endif
+ _PyStackRef *target_local = &GETLOCAL(next_oparg);
+ if (!PyStackRef_Is(*target_local, left)) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
+ STAT_INC(BINARY_OP, hit);
+ /* Handle `left = left + right` or `left += right` for str.
+ *
+ * When possible, extend `left` in place rather than
+ * allocating a new PyUnicodeObject. This attempts to avoid
+ * quadratic behavior when one neglects to use str.join().
+ *
+ * If `left` has only two references remaining (one from
+ * the stack, one in the locals), DECREFing `left` leaves
+ * only the locals reference, so PyUnicode_Append knows
+ * that the string is safe to mutate.
+ */
+ assert(Py_REFCNT(left_o) >= 2);
+ _Py_DECREF_NO_DEALLOC(left_o);
+ PyObject *temp = PyStackRef_AsPyObjectBorrow(*target_local);
+ PyUnicode_Append(&temp, right_o);
+ *target_local = PyStackRef_FromPyObjectSteal(temp);
+ _Py_DECREF_SPECIALIZED(right_o, _PyUnicode_ExactDealloc);
+ if (PyStackRef_IsNull(*target_local)) JUMP_TO_ERROR();
+ #if TIER_ONE
+ // The STORE_FAST is already done. This is done here in tier one,
+ // and during trace projection in tier two:
+ assert(next_instr->op.code == STORE_FAST);
+ SKIP_OVER(1);
+ #endif
+ stack_pointer += -2;
+ assert(WITHIN_STACK_BOUNDS());
+ break;
+ }
+
case _BINARY_SUBSCR: {
_PyStackRef sub;
_PyStackRef container;
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 6f996f91921cd3..c9907438ddc466 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -181,8 +181,14 @@
{
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
+ int next_oparg;
+ #if TIER_ONE
assert(next_instr->op.code == STORE_FAST);
- _PyStackRef *target_local = &GETLOCAL(next_instr->op.arg);
+ next_oparg = next_instr->op.arg;
+ #else
+ next_oparg = CURRENT_OPERAND();
+ #endif
+ _PyStackRef *target_local = &GETLOCAL(next_oparg);
DEOPT_IF(!PyStackRef_Is(*target_local, left), BINARY_OP);
STAT_INC(BINARY_OP, hit);
/* Handle `left = left + right` or `left += right` for str.
@@ -203,9 +209,12 @@
*target_local = PyStackRef_FromPyObjectSteal(temp);
_Py_DECREF_SPECIALIZED(right_o, _PyUnicode_ExactDealloc);
if (PyStackRef_IsNull(*target_local)) goto pop_2_error;
- // The STORE_FAST is already done.
+ #if TIER_ONE
+ // The STORE_FAST is already done. This is done here in tier one,
+ // and during trace projection in tier two:
assert(next_instr->op.code == STORE_FAST);
SKIP_OVER(1);
+ #endif
}
stack_pointer += -2;
assert(WITHIN_STACK_BOUNDS());
diff --git a/Python/optimizer.c b/Python/optimizer.c
index e08c1dc9936a3d..f0793b8c8f2088 100644
--- a/Python/optimizer.c
+++ b/Python/optimizer.c
@@ -875,6 +875,15 @@ translate_bytecode_to_trace(
goto done;
}
+ if (uop == _BINARY_OP_INPLACE_ADD_UNICODE) {
+ assert(i + 1 == nuops);
+ _Py_CODEUNIT *next_instr = instr + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]];
+ assert(next_instr->op.code == STORE_FAST);
+ operand = next_instr->op.arg;
+ // Skip the STORE_FAST:
+ instr++;
+ }
+
// All other instructions
ADD_TO_TRACE(uop, oparg, operand, target);
}
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 8c2b1ac7926cec..33af8552ba69e0 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -473,6 +473,12 @@
break;
}
+ case _BINARY_OP_INPLACE_ADD_UNICODE: {
+ stack_pointer += -2;
+ assert(WITHIN_STACK_BOUNDS());
+ break;
+ }
+
case _BINARY_SUBSCR: {
_Py_UopsSymbol *res;
res = sym_new_not_null(ctx);
From afb0aa6ed20bd8e982ecb307f12923cf8dbccd8c Mon Sep 17 00:00:00 2001
From: Mark Shannon
Date: Fri, 26 Jul 2024 12:24:12 +0100
Subject: [PATCH 14/47] GH-121131: Clean up and fix some instrumented
instructions. (GH-121132)
* Add support for 'prev_instr' to code generator and refactor some INSTRUMENTED instructions
---
Include/internal/pycore_opcode_metadata.h | 9 +-
Include/internal/pycore_uop_ids.h | 4 +-
Include/opcode_ids.h | 30 +--
Lib/_opcode_metadata.py | 30 +--
Lib/test/test_sys_settrace.py | 2 +-
Python/bytecodes.c | 115 +++++----
Python/ceval.c | 40 ----
Python/ceval_macros.h | 5 +-
Python/executor_cases.c.h | 8 +-
Python/generated_cases.c.h | 222 +++++++++++++-----
Python/opcode_targets.h | 6 +-
Python/optimizer_cases.c.h | 8 +-
Tools/cases_generator/analyzer.py | 9 +-
.../opcode_metadata_generator.py | 2 -
Tools/cases_generator/tier1_generator.py | 2 +
15 files changed, 277 insertions(+), 215 deletions(-)
diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h
index 9c7ad926f9a980..f5c439e81a6232 100644
--- a/Include/internal/pycore_opcode_metadata.h
+++ b/Include/internal/pycore_opcode_metadata.h
@@ -231,6 +231,8 @@ int _PyOpcode_num_popped(int opcode, int oparg) {
return 0;
case INSTRUMENTED_JUMP_FORWARD:
return 0;
+ case INSTRUMENTED_LINE:
+ return 0;
case INSTRUMENTED_LOAD_SUPER_ATTR:
return 3;
case INSTRUMENTED_POP_JUMP_IF_FALSE:
@@ -676,6 +678,8 @@ int _PyOpcode_num_pushed(int opcode, int oparg) {
return 0;
case INSTRUMENTED_JUMP_FORWARD:
return 0;
+ case INSTRUMENTED_LINE:
+ return 0;
case INSTRUMENTED_LOAD_SUPER_ATTR:
return 1 + (oparg & 1);
case INSTRUMENTED_POP_JUMP_IF_FALSE:
@@ -689,9 +693,9 @@ int _PyOpcode_num_pushed(int opcode, int oparg) {
case INSTRUMENTED_RESUME:
return 0;
case INSTRUMENTED_RETURN_CONST:
- return 0;
+ return 1;
case INSTRUMENTED_RETURN_VALUE:
- return 0;
+ return 1;
case INSTRUMENTED_YIELD_VALUE:
return 1;
case INTERPRETER_EXIT:
@@ -1083,6 +1087,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[264] = {
[INSTRUMENTED_INSTRUCTION] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[INSTRUMENTED_JUMP_BACKWARD] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG },
[INSTRUMENTED_JUMP_FORWARD] = { true, INSTR_FMT_IB, HAS_ARG_FLAG },
+ [INSTRUMENTED_LINE] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG },
[INSTRUMENTED_LOAD_SUPER_ATTR] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG },
[INSTRUMENTED_POP_JUMP_IF_FALSE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG },
[INSTRUMENTED_POP_JUMP_IF_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG },
diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h
index 9ae82ca3c3dafa..60de0573baf5f1 100644
--- a/Include/internal/pycore_uop_ids.h
+++ b/Include/internal/pycore_uop_ids.h
@@ -145,15 +145,13 @@ extern "C" {
#define _INSTRUMENTED_INSTRUCTION INSTRUMENTED_INSTRUCTION
#define _INSTRUMENTED_JUMP_BACKWARD INSTRUMENTED_JUMP_BACKWARD
#define _INSTRUMENTED_JUMP_FORWARD INSTRUMENTED_JUMP_FORWARD
+#define _INSTRUMENTED_LINE INSTRUMENTED_LINE
#define _INSTRUMENTED_LOAD_SUPER_ATTR INSTRUMENTED_LOAD_SUPER_ATTR
#define _INSTRUMENTED_POP_JUMP_IF_FALSE INSTRUMENTED_POP_JUMP_IF_FALSE
#define _INSTRUMENTED_POP_JUMP_IF_NONE INSTRUMENTED_POP_JUMP_IF_NONE
#define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE INSTRUMENTED_POP_JUMP_IF_NOT_NONE
#define _INSTRUMENTED_POP_JUMP_IF_TRUE INSTRUMENTED_POP_JUMP_IF_TRUE
#define _INSTRUMENTED_RESUME INSTRUMENTED_RESUME
-#define _INSTRUMENTED_RETURN_CONST INSTRUMENTED_RETURN_CONST
-#define _INSTRUMENTED_RETURN_VALUE INSTRUMENTED_RETURN_VALUE
-#define _INSTRUMENTED_YIELD_VALUE INSTRUMENTED_YIELD_VALUE
#define _INTERNAL_INCREMENT_OPT_COUNTER 381
#define _IS_NONE 382
#define _IS_OP IS_OP
diff --git a/Include/opcode_ids.h b/Include/opcode_ids.h
index dd9b1ec1674949..d14b48f4289285 100644
--- a/Include/opcode_ids.h
+++ b/Include/opcode_ids.h
@@ -204,21 +204,21 @@ extern "C" {
#define INSTRUMENTED_RESUME 236
#define INSTRUMENTED_END_FOR 237
#define INSTRUMENTED_END_SEND 238
-#define INSTRUMENTED_RETURN_VALUE 239
-#define INSTRUMENTED_RETURN_CONST 240
-#define INSTRUMENTED_YIELD_VALUE 241
-#define INSTRUMENTED_LOAD_SUPER_ATTR 242
-#define INSTRUMENTED_FOR_ITER 243
-#define INSTRUMENTED_CALL 244
-#define INSTRUMENTED_CALL_KW 245
-#define INSTRUMENTED_CALL_FUNCTION_EX 246
-#define INSTRUMENTED_INSTRUCTION 247
-#define INSTRUMENTED_JUMP_FORWARD 248
-#define INSTRUMENTED_JUMP_BACKWARD 249
-#define INSTRUMENTED_POP_JUMP_IF_TRUE 250
-#define INSTRUMENTED_POP_JUMP_IF_FALSE 251
-#define INSTRUMENTED_POP_JUMP_IF_NONE 252
-#define INSTRUMENTED_POP_JUMP_IF_NOT_NONE 253
+#define INSTRUMENTED_LOAD_SUPER_ATTR 239
+#define INSTRUMENTED_FOR_ITER 240
+#define INSTRUMENTED_CALL 241
+#define INSTRUMENTED_CALL_KW 242
+#define INSTRUMENTED_CALL_FUNCTION_EX 243
+#define INSTRUMENTED_INSTRUCTION 244
+#define INSTRUMENTED_JUMP_FORWARD 245
+#define INSTRUMENTED_JUMP_BACKWARD 246
+#define INSTRUMENTED_POP_JUMP_IF_TRUE 247
+#define INSTRUMENTED_POP_JUMP_IF_FALSE 248
+#define INSTRUMENTED_POP_JUMP_IF_NONE 249
+#define INSTRUMENTED_POP_JUMP_IF_NOT_NONE 250
+#define INSTRUMENTED_RETURN_VALUE 251
+#define INSTRUMENTED_RETURN_CONST 252
+#define INSTRUMENTED_YIELD_VALUE 253
#define INSTRUMENTED_LINE 254
#define JUMP 256
#define JUMP_NO_INTERRUPT 257
diff --git a/Lib/_opcode_metadata.py b/Lib/_opcode_metadata.py
index 4b6057f4119421..01c22a89846e97 100644
--- a/Lib/_opcode_metadata.py
+++ b/Lib/_opcode_metadata.py
@@ -308,21 +308,21 @@
'INSTRUMENTED_RESUME': 236,
'INSTRUMENTED_END_FOR': 237,
'INSTRUMENTED_END_SEND': 238,
- 'INSTRUMENTED_RETURN_VALUE': 239,
- 'INSTRUMENTED_RETURN_CONST': 240,
- 'INSTRUMENTED_YIELD_VALUE': 241,
- 'INSTRUMENTED_LOAD_SUPER_ATTR': 242,
- 'INSTRUMENTED_FOR_ITER': 243,
- 'INSTRUMENTED_CALL': 244,
- 'INSTRUMENTED_CALL_KW': 245,
- 'INSTRUMENTED_CALL_FUNCTION_EX': 246,
- 'INSTRUMENTED_INSTRUCTION': 247,
- 'INSTRUMENTED_JUMP_FORWARD': 248,
- 'INSTRUMENTED_JUMP_BACKWARD': 249,
- 'INSTRUMENTED_POP_JUMP_IF_TRUE': 250,
- 'INSTRUMENTED_POP_JUMP_IF_FALSE': 251,
- 'INSTRUMENTED_POP_JUMP_IF_NONE': 252,
- 'INSTRUMENTED_POP_JUMP_IF_NOT_NONE': 253,
+ 'INSTRUMENTED_LOAD_SUPER_ATTR': 239,
+ 'INSTRUMENTED_FOR_ITER': 240,
+ 'INSTRUMENTED_CALL': 241,
+ 'INSTRUMENTED_CALL_KW': 242,
+ 'INSTRUMENTED_CALL_FUNCTION_EX': 243,
+ 'INSTRUMENTED_INSTRUCTION': 244,
+ 'INSTRUMENTED_JUMP_FORWARD': 245,
+ 'INSTRUMENTED_JUMP_BACKWARD': 246,
+ 'INSTRUMENTED_POP_JUMP_IF_TRUE': 247,
+ 'INSTRUMENTED_POP_JUMP_IF_FALSE': 248,
+ 'INSTRUMENTED_POP_JUMP_IF_NONE': 249,
+ 'INSTRUMENTED_POP_JUMP_IF_NOT_NONE': 250,
+ 'INSTRUMENTED_RETURN_VALUE': 251,
+ 'INSTRUMENTED_RETURN_CONST': 252,
+ 'INSTRUMENTED_YIELD_VALUE': 253,
'JUMP': 256,
'JUMP_NO_INTERRUPT': 257,
'LOAD_CLOSURE': 258,
diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py
index c622fd9ce7c466..95cf0d1ec2d9ab 100644
--- a/Lib/test/test_sys_settrace.py
+++ b/Lib/test/test_sys_settrace.py
@@ -2857,7 +2857,7 @@ def test_no_jump_from_exception_event(output):
output.append(1)
1 / 0
- @jump_test(3, 2, [2, 5], event='return')
+ @jump_test(3, 2, [2, 2, 5], event='return')
def test_jump_from_yield(output):
def gen():
output.append(2)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index eb8b66f8d8b2e7..971397c955de09 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -945,48 +945,25 @@ dummy_func(
LLTRACE_RESUME_FRAME();
}
- inst(INSTRUMENTED_RETURN_VALUE, (retval --)) {
+ tier1 op(_RETURN_VALUE_EVENT, (val -- val)) {
int err = _Py_call_instrumentation_arg(
tstate, PY_MONITORING_EVENT_PY_RETURN,
- frame, this_instr, PyStackRef_AsPyObjectBorrow(retval));
+ frame, this_instr, PyStackRef_AsPyObjectBorrow(val));
if (err) ERROR_NO_POP();
- STACK_SHRINK(1);
- assert(EMPTY());
- _PyFrame_SetStackPointer(frame, stack_pointer);
- _Py_LeaveRecursiveCallPy(tstate);
- assert(frame != &entry_frame);
- // GH-99729: We need to unlink the frame *before* clearing it:
- _PyInterpreterFrame *dying = frame;
- frame = tstate->current_frame = dying->previous;
- _PyEval_FrameClearAndPop(tstate, dying);
- _PyFrame_StackPush(frame, retval);
- LOAD_IP(frame->return_offset);
- goto resume_frame;
}
+ macro(INSTRUMENTED_RETURN_VALUE) =
+ _RETURN_VALUE_EVENT +
+ RETURN_VALUE;
+
macro(RETURN_CONST) =
LOAD_CONST +
RETURN_VALUE;
- inst(INSTRUMENTED_RETURN_CONST, (--)) {
- PyObject *retval = GETITEM(FRAME_CO_CONSTS, oparg);
- int err = _Py_call_instrumentation_arg(
- tstate, PY_MONITORING_EVENT_PY_RETURN,
- frame, this_instr, retval);
- if (err) ERROR_NO_POP();
- Py_INCREF(retval);
- assert(EMPTY());
- _PyFrame_SetStackPointer(frame, stack_pointer);
- _Py_LeaveRecursiveCallPy(tstate);
- assert(frame != &entry_frame);
- // GH-99729: We need to unlink the frame *before* clearing it:
- _PyInterpreterFrame *dying = frame;
- frame = tstate->current_frame = dying->previous;
- _PyEval_FrameClearAndPop(tstate, dying);
- _PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(retval));
- LOAD_IP(frame->return_offset);
- goto resume_frame;
- }
+ macro(INSTRUMENTED_RETURN_CONST) =
+ LOAD_CONST +
+ _RETURN_VALUE_EVENT +
+ RETURN_VALUE;
inst(GET_AITER, (obj -- iter)) {
unaryfunc getter = NULL;
@@ -1183,31 +1160,6 @@ dummy_func(
_SEND_GEN_FRAME +
_PUSH_FRAME;
- inst(INSTRUMENTED_YIELD_VALUE, (retval -- unused)) {
- assert(frame != &entry_frame);
- frame->instr_ptr = next_instr;
- PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame);
- assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1);
- assert(oparg == 0 || oparg == 1);
- gen->gi_frame_state = FRAME_SUSPENDED + oparg;
- _PyFrame_SetStackPointer(frame, stack_pointer - 1);
- int err = _Py_call_instrumentation_arg(
- tstate, PY_MONITORING_EVENT_PY_YIELD,
- frame, this_instr, PyStackRef_AsPyObjectBorrow(retval));
- if (err) ERROR_NO_POP();
- tstate->exc_info = gen->gi_exc_state.previous_item;
- gen->gi_exc_state.previous_item = NULL;
- _Py_LeaveRecursiveCallPy(tstate);
- _PyInterpreterFrame *gen_frame = frame;
- frame = tstate->current_frame = frame->previous;
- gen_frame->previous = NULL;
- _PyFrame_StackPush(frame, retval);
- /* We don't know which of these is relevant here, so keep them equal */
- assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
- LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND);
- goto resume_frame;
- }
-
inst(YIELD_VALUE, (retval -- value)) {
// NOTE: It's important that YIELD_VALUE never raises an exception!
// The compiler treats any exception raised here as a failed close()
@@ -1244,6 +1196,23 @@ dummy_func(
LLTRACE_RESUME_FRAME();
}
+ tier1 op(_YIELD_VALUE_EVENT, (val -- val)) {
+ SAVE_SP();
+ int err = _Py_call_instrumentation_arg(
+ tstate, PY_MONITORING_EVENT_PY_YIELD,
+ frame, this_instr, PyStackRef_AsPyObjectBorrow(val));
+ LOAD_SP();
+ if (err) ERROR_NO_POP();
+ if (frame->instr_ptr != this_instr) {
+ next_instr = frame->instr_ptr;
+ DISPATCH();
+ }
+ }
+
+ macro(INSTRUMENTED_YIELD_VALUE) =
+ _YIELD_VALUE_EVENT +
+ YIELD_VALUE;
+
inst(POP_EXCEPT, (exc_value -- )) {
_PyErr_StackItem *exc_info = tstate->exc_info;
Py_XSETREF(exc_info->exc_value,
@@ -4450,6 +4419,36 @@ dummy_func(
assert(oparg >= 2);
}
+ inst(INSTRUMENTED_LINE, ( -- )) {
+ int original_opcode = 0;
+ if (tstate->tracing) {
+ PyCodeObject *code = _PyFrame_GetCode(frame);
+ original_opcode = code->_co_monitoring->lines[(int)(this_instr - _PyCode_CODE(code))].original_opcode;
+ next_instr = this_instr;
+ } else {
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ original_opcode = _Py_call_instrumentation_line(
+ tstate, frame, this_instr, prev_instr);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
+ if (original_opcode < 0) {
+ next_instr = this_instr+1;
+ goto error;
+ }
+ next_instr = frame->instr_ptr;
+ if (next_instr != this_instr) {
+ DISPATCH();
+ }
+ }
+ if (_PyOpcode_Caches[original_opcode]) {
+ _PyBinaryOpCache *cache = (_PyBinaryOpCache *)(next_instr+1);
+ /* Prevent the underlying instruction from specializing
+ * and overwriting the instrumentation. */
+ PAUSE_ADAPTIVE_COUNTER(cache->counter);
+ }
+ opcode = original_opcode;
+ DISPATCH_GOTO();
+ }
+
inst(INSTRUMENTED_INSTRUCTION, ( -- )) {
int next_opcode = _Py_call_instrumentation_instruction(
tstate, frame, this_instr);
diff --git a/Python/ceval.c b/Python/ceval.c
index 1e911d3ba17189..c0074c45b27111 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -835,46 +835,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
#include "generated_cases.c.h"
- /* INSTRUMENTED_LINE has to be here, rather than in bytecodes.c,
- * because it needs to capture frame->instr_ptr before it is updated,
- * as happens in the standard instruction prologue.
- */
-#if USE_COMPUTED_GOTOS
- TARGET_INSTRUMENTED_LINE:
-#else
- case INSTRUMENTED_LINE:
-#endif
- {
- _Py_CODEUNIT *prev = frame->instr_ptr;
- _Py_CODEUNIT *here = frame->instr_ptr = next_instr;
- int original_opcode = 0;
- if (tstate->tracing) {
- PyCodeObject *code = _PyFrame_GetCode(frame);
- original_opcode = code->_co_monitoring->lines[(int)(here - _PyCode_CODE(code))].original_opcode;
- } else {
- _PyFrame_SetStackPointer(frame, stack_pointer);
- original_opcode = _Py_call_instrumentation_line(
- tstate, frame, here, prev);
- stack_pointer = _PyFrame_GetStackPointer(frame);
- if (original_opcode < 0) {
- next_instr = here+1;
- goto error;
- }
- next_instr = frame->instr_ptr;
- if (next_instr != here) {
- DISPATCH();
- }
- }
- if (_PyOpcode_Caches[original_opcode]) {
- _PyBinaryOpCache *cache = (_PyBinaryOpCache *)(next_instr+1);
- /* Prevent the underlying instruction from specializing
- * and overwriting the instrumentation. */
- PAUSE_ADAPTIVE_COUNTER(cache->counter);
- }
- opcode = original_opcode;
- DISPATCH_GOTO();
- }
-
#if USE_COMPUTED_GOTOS
_unknown_opcode:
diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h
index 595b72bfaf9613..60efe3d78ff22c 100644
--- a/Python/ceval_macros.h
+++ b/Python/ceval_macros.h
@@ -402,7 +402,10 @@ static inline void _Py_LeaveRecursiveCallPy(PyThreadState *tstate) {
/* There's no STORE_IP(), it's inlined by the code generator. */
#define LOAD_SP() \
-stack_pointer = _PyFrame_GetStackPointer(frame);
+stack_pointer = _PyFrame_GetStackPointer(frame)
+
+#define SAVE_SP() \
+_PyFrame_SetStackPointer(frame, stack_pointer)
/* Tier-switching macros. */
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index 3379f0be2272dc..288e0f9135c27e 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -1153,10 +1153,6 @@
break;
}
- /* _INSTRUMENTED_RETURN_VALUE is not a viable micro-op for tier 2 because it is instrumented */
-
- /* _INSTRUMENTED_RETURN_CONST is not a viable micro-op for tier 2 because it is instrumented */
-
case _GET_AITER: {
_PyStackRef obj;
_PyStackRef iter;
@@ -1304,8 +1300,6 @@
break;
}
- /* _INSTRUMENTED_YIELD_VALUE is not a viable micro-op for tier 2 because it is instrumented */
-
case _YIELD_VALUE: {
_PyStackRef retval;
_PyStackRef value;
@@ -4913,6 +4907,8 @@
break;
}
+ /* _INSTRUMENTED_LINE is not a viable micro-op for tier 2 because it is instrumented */
+
/* _INSTRUMENTED_INSTRUCTION is not a viable micro-op for tier 2 because it is instrumented */
/* _INSTRUMENTED_JUMP_FORWARD is not a viable micro-op for tier 2 because it is instrumented */
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index c9907438ddc466..634053a93837c6 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -3657,6 +3657,41 @@
DISPATCH();
}
+ TARGET(INSTRUMENTED_LINE) {
+ _Py_CODEUNIT *prev_instr = frame->instr_ptr;
+ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr;
+ (void)this_instr;
+ next_instr += 1;
+ INSTRUCTION_STATS(INSTRUMENTED_LINE);
+ int original_opcode = 0;
+ if (tstate->tracing) {
+ PyCodeObject *code = _PyFrame_GetCode(frame);
+ original_opcode = code->_co_monitoring->lines[(int)(this_instr - _PyCode_CODE(code))].original_opcode;
+ next_instr = this_instr;
+ } else {
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ original_opcode = _Py_call_instrumentation_line(
+ tstate, frame, this_instr, prev_instr);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
+ if (original_opcode < 0) {
+ next_instr = this_instr+1;
+ goto error;
+ }
+ next_instr = frame->instr_ptr;
+ if (next_instr != this_instr) {
+ DISPATCH();
+ }
+ }
+ if (_PyOpcode_Caches[original_opcode]) {
+ _PyBinaryOpCache *cache = (_PyBinaryOpCache *)(next_instr+1);
+ /* Prevent the underlying instruction from specializing
+ * and overwriting the instrumentation. */
+ PAUSE_ADAPTIVE_COUNTER(cache->counter);
+ }
+ opcode = original_opcode;
+ DISPATCH_GOTO();
+ }
+
TARGET(INSTRUMENTED_LOAD_SUPER_ATTR) {
_Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
@@ -3785,23 +3820,44 @@
(void)this_instr;
next_instr += 1;
INSTRUCTION_STATS(INSTRUMENTED_RETURN_CONST);
- PyObject *retval = GETITEM(FRAME_CO_CONSTS, oparg);
- int err = _Py_call_instrumentation_arg(
- tstate, PY_MONITORING_EVENT_PY_RETURN,
- frame, this_instr, retval);
- if (err) goto error;
- Py_INCREF(retval);
- assert(EMPTY());
- _PyFrame_SetStackPointer(frame, stack_pointer);
- _Py_LeaveRecursiveCallPy(tstate);
- assert(frame != &entry_frame);
- // GH-99729: We need to unlink the frame *before* clearing it:
- _PyInterpreterFrame *dying = frame;
- frame = tstate->current_frame = dying->previous;
- _PyEval_FrameClearAndPop(tstate, dying);
- _PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(retval));
- LOAD_IP(frame->return_offset);
- goto resume_frame;
+ _PyStackRef value;
+ _PyStackRef val;
+ _PyStackRef retval;
+ _PyStackRef res;
+ // _LOAD_CONST
+ {
+ value = PyStackRef_FromPyObjectNew(GETITEM(FRAME_CO_CONSTS, oparg));
+ }
+ // _RETURN_VALUE_EVENT
+ val = value;
+ {
+ int err = _Py_call_instrumentation_arg(
+ tstate, PY_MONITORING_EVENT_PY_RETURN,
+ frame, this_instr, PyStackRef_AsPyObjectBorrow(val));
+ if (err) goto error;
+ }
+ // _RETURN_VALUE
+ retval = val;
+ {
+ #if TIER_ONE
+ assert(frame != &entry_frame);
+ #endif
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ assert(EMPTY());
+ _Py_LeaveRecursiveCallPy(tstate);
+ // GH-99729: We need to unlink the frame *before* clearing it:
+ _PyInterpreterFrame *dying = frame;
+ frame = tstate->current_frame = dying->previous;
+ _PyEval_FrameClearAndPop(tstate, dying);
+ LOAD_SP();
+ LOAD_IP(frame->return_offset);
+ res = retval;
+ LLTRACE_RESUME_FRAME();
+ }
+ stack_pointer[0] = res;
+ stack_pointer += 1;
+ assert(WITHIN_STACK_BOUNDS());
+ DISPATCH();
}
TARGET(INSTRUMENTED_RETURN_VALUE) {
@@ -3809,24 +3865,41 @@
(void)this_instr;
next_instr += 1;
INSTRUCTION_STATS(INSTRUMENTED_RETURN_VALUE);
+ _PyStackRef val;
_PyStackRef retval;
- retval = stack_pointer[-1];
- int err = _Py_call_instrumentation_arg(
- tstate, PY_MONITORING_EVENT_PY_RETURN,
- frame, this_instr, PyStackRef_AsPyObjectBorrow(retval));
- if (err) goto error;
- STACK_SHRINK(1);
- assert(EMPTY());
- _PyFrame_SetStackPointer(frame, stack_pointer);
- _Py_LeaveRecursiveCallPy(tstate);
- assert(frame != &entry_frame);
- // GH-99729: We need to unlink the frame *before* clearing it:
- _PyInterpreterFrame *dying = frame;
- frame = tstate->current_frame = dying->previous;
- _PyEval_FrameClearAndPop(tstate, dying);
- _PyFrame_StackPush(frame, retval);
- LOAD_IP(frame->return_offset);
- goto resume_frame;
+ _PyStackRef res;
+ // _RETURN_VALUE_EVENT
+ val = stack_pointer[-1];
+ {
+ int err = _Py_call_instrumentation_arg(
+ tstate, PY_MONITORING_EVENT_PY_RETURN,
+ frame, this_instr, PyStackRef_AsPyObjectBorrow(val));
+ if (err) goto error;
+ }
+ // _RETURN_VALUE
+ retval = val;
+ {
+ #if TIER_ONE
+ assert(frame != &entry_frame);
+ #endif
+ stack_pointer += -1;
+ assert(WITHIN_STACK_BOUNDS());
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ assert(EMPTY());
+ _Py_LeaveRecursiveCallPy(tstate);
+ // GH-99729: We need to unlink the frame *before* clearing it:
+ _PyInterpreterFrame *dying = frame;
+ frame = tstate->current_frame = dying->previous;
+ _PyEval_FrameClearAndPop(tstate, dying);
+ LOAD_SP();
+ LOAD_IP(frame->return_offset);
+ res = retval;
+ LLTRACE_RESUME_FRAME();
+ }
+ stack_pointer[0] = res;
+ stack_pointer += 1;
+ assert(WITHIN_STACK_BOUNDS());
+ DISPATCH();
}
TARGET(INSTRUMENTED_YIELD_VALUE) {
@@ -3834,30 +3907,65 @@
(void)this_instr;
next_instr += 1;
INSTRUCTION_STATS(INSTRUMENTED_YIELD_VALUE);
+ _PyStackRef val;
_PyStackRef retval;
- retval = stack_pointer[-1];
- assert(frame != &entry_frame);
- frame->instr_ptr = next_instr;
- PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame);
- assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1);
- assert(oparg == 0 || oparg == 1);
- gen->gi_frame_state = FRAME_SUSPENDED + oparg;
- _PyFrame_SetStackPointer(frame, stack_pointer - 1);
- int err = _Py_call_instrumentation_arg(
- tstate, PY_MONITORING_EVENT_PY_YIELD,
- frame, this_instr, PyStackRef_AsPyObjectBorrow(retval));
- if (err) goto error;
- tstate->exc_info = gen->gi_exc_state.previous_item;
- gen->gi_exc_state.previous_item = NULL;
- _Py_LeaveRecursiveCallPy(tstate);
- _PyInterpreterFrame *gen_frame = frame;
- frame = tstate->current_frame = frame->previous;
- gen_frame->previous = NULL;
- _PyFrame_StackPush(frame, retval);
- /* We don't know which of these is relevant here, so keep them equal */
- assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
- LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND);
- goto resume_frame;
+ _PyStackRef value;
+ // _YIELD_VALUE_EVENT
+ val = stack_pointer[-1];
+ {
+ SAVE_SP();
+ int err = _Py_call_instrumentation_arg(
+ tstate, PY_MONITORING_EVENT_PY_YIELD,
+ frame, this_instr, PyStackRef_AsPyObjectBorrow(val));
+ LOAD_SP();
+ if (err) goto error;
+ if (frame->instr_ptr != this_instr) {
+ next_instr = frame->instr_ptr;
+ DISPATCH();
+ }
+ }
+ // _YIELD_VALUE
+ retval = val;
+ {
+ // NOTE: It's important that YIELD_VALUE never raises an exception!
+ // The compiler treats any exception raised here as a failed close()
+ // or throw() call.
+ #if TIER_ONE
+ assert(frame != &entry_frame);
+ #endif
+ frame->instr_ptr++;
+ PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame);
+ assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1);
+ assert(oparg == 0 || oparg == 1);
+ gen->gi_frame_state = FRAME_SUSPENDED + oparg;
+ stack_pointer += -1;
+ assert(WITHIN_STACK_BOUNDS());
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ tstate->exc_info = gen->gi_exc_state.previous_item;
+ gen->gi_exc_state.previous_item = NULL;
+ _Py_LeaveRecursiveCallPy(tstate);
+ _PyInterpreterFrame *gen_frame = frame;
+ frame = tstate->current_frame = frame->previous;
+ gen_frame->previous = NULL;
+ /* We don't know which of these is relevant here, so keep them equal */
+ assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
+ #if TIER_ONE
+ assert(frame->instr_ptr->op.code == INSTRUMENTED_LINE ||
+ frame->instr_ptr->op.code == INSTRUMENTED_INSTRUCTION ||
+ _PyOpcode_Deopt[frame->instr_ptr->op.code] == SEND ||
+ _PyOpcode_Deopt[frame->instr_ptr->op.code] == FOR_ITER ||
+ _PyOpcode_Deopt[frame->instr_ptr->op.code] == INTERPRETER_EXIT ||
+ _PyOpcode_Deopt[frame->instr_ptr->op.code] == ENTER_EXECUTOR);
+ #endif
+ LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND);
+ LOAD_SP();
+ value = retval;
+ LLTRACE_RESUME_FRAME();
+ }
+ stack_pointer[0] = value;
+ stack_pointer += 1;
+ assert(WITHIN_STACK_BOUNDS());
+ DISPATCH();
}
TARGET(INTERPRETER_EXIT) {
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index 74544a1dff25c6..224aeb834935eb 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -238,9 +238,6 @@ static void *opcode_targets[256] = {
&&TARGET_INSTRUMENTED_RESUME,
&&TARGET_INSTRUMENTED_END_FOR,
&&TARGET_INSTRUMENTED_END_SEND,
- &&TARGET_INSTRUMENTED_RETURN_VALUE,
- &&TARGET_INSTRUMENTED_RETURN_CONST,
- &&TARGET_INSTRUMENTED_YIELD_VALUE,
&&TARGET_INSTRUMENTED_LOAD_SUPER_ATTR,
&&TARGET_INSTRUMENTED_FOR_ITER,
&&TARGET_INSTRUMENTED_CALL,
@@ -253,6 +250,9 @@ static void *opcode_targets[256] = {
&&TARGET_INSTRUMENTED_POP_JUMP_IF_FALSE,
&&TARGET_INSTRUMENTED_POP_JUMP_IF_NONE,
&&TARGET_INSTRUMENTED_POP_JUMP_IF_NOT_NONE,
+ &&TARGET_INSTRUMENTED_RETURN_VALUE,
+ &&TARGET_INSTRUMENTED_RETURN_CONST,
+ &&TARGET_INSTRUMENTED_YIELD_VALUE,
&&TARGET_INSTRUMENTED_LINE,
&&_unknown_opcode,
};
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 33af8552ba69e0..3c9e6d3043cde1 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -621,10 +621,6 @@
break;
}
- /* _INSTRUMENTED_RETURN_VALUE is not a viable micro-op for tier 2 */
-
- /* _INSTRUMENTED_RETURN_CONST is not a viable micro-op for tier 2 */
-
case _GET_AITER: {
_Py_UopsSymbol *iter;
iter = sym_new_not_null(ctx);
@@ -656,8 +652,6 @@
break;
}
- /* _INSTRUMENTED_YIELD_VALUE is not a viable micro-op for tier 2 */
-
case _YIELD_VALUE: {
_Py_UopsSymbol *res;
res = sym_new_unknown(ctx);
@@ -2056,6 +2050,8 @@
break;
}
+ /* _INSTRUMENTED_LINE is not a viable micro-op for tier 2 */
+
/* _INSTRUMENTED_INSTRUCTION is not a viable micro-op for tier 2 */
/* _INSTRUMENTED_JUMP_FORWARD is not a viable micro-op for tier 2 */
diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py
index f5cf4fad4470d8..675dc0b9acaf45 100644
--- a/Tools/cases_generator/analyzer.py
+++ b/Tools/cases_generator/analyzer.py
@@ -27,6 +27,7 @@ class Properties:
tier: int | None = None
oparg_and_1: bool = False
const_oparg: int = -1
+ needs_prev: bool = False
def dump(self, indent: str) -> None:
print(indent, end="")
@@ -53,6 +54,7 @@ def from_list(properties: list["Properties"]) -> "Properties":
has_free=any(p.has_free for p in properties),
side_exit=any(p.side_exit for p in properties),
pure=all(p.pure for p in properties),
+ needs_prev=any(p.needs_prev for p in properties),
)
@property
@@ -618,6 +620,7 @@ def compute_properties(op: parser.InstDef) -> Properties:
has_free=has_free,
pure="pure" in op.annotations,
tier=tier_variable(op),
+ needs_prev=variable_used(op, "prev_instr"),
)
@@ -797,12 +800,6 @@ def assign_opcodes(
instrumented = [name for name in instructions if name.startswith("INSTRUMENTED")]
- # Special case: this instruction is implemented in ceval.c
- # rather than bytecodes.c, so we need to add it explicitly
- # here (at least until we add something to bytecodes.c to
- # declare external instructions).
- instrumented.append("INSTRUMENTED_LINE")
-
specialized: set[str] = set()
no_arg: list[str] = []
has_arg: list[str] = []
diff --git a/Tools/cases_generator/opcode_metadata_generator.py b/Tools/cases_generator/opcode_metadata_generator.py
index 0f5790dc4af40f..09b9d3d211eb24 100644
--- a/Tools/cases_generator/opcode_metadata_generator.py
+++ b/Tools/cases_generator/opcode_metadata_generator.py
@@ -151,7 +151,6 @@ def generate_deopt_table(analysis: Analysis, out: CWriter) -> None:
if inst.family is not None:
deopt = inst.family.name
deopts.append((inst.name, deopt))
- deopts.append(("INSTRUMENTED_LINE", "INSTRUMENTED_LINE"))
for name, deopt in sorted(deopts):
out.emit(f"[{name}] = {deopt},\n")
out.emit("};\n\n")
@@ -179,7 +178,6 @@ def generate_name_table(analysis: Analysis, out: CWriter) -> None:
out.emit("#ifdef NEED_OPCODE_METADATA\n")
out.emit(f"const char *_PyOpcode_OpName[{table_size}] = {{\n")
names = list(analysis.instructions) + list(analysis.pseudos)
- names.append("INSTRUMENTED_LINE")
for name in sorted(names):
out.emit(f'[{name}] = "{name}",\n')
out.emit("};\n")
diff --git a/Tools/cases_generator/tier1_generator.py b/Tools/cases_generator/tier1_generator.py
index 5dec66e8e0af15..118f4b3a6eaa1c 100644
--- a/Tools/cases_generator/tier1_generator.py
+++ b/Tools/cases_generator/tier1_generator.py
@@ -148,6 +148,8 @@ def generate_tier1(
out.emit("\n")
out.emit(f"TARGET({name}) {{\n")
unused_guard = "(void)this_instr;\n" if inst.family is None else ""
+ if inst.properties.needs_prev:
+ out.emit(f"_Py_CODEUNIT *prev_instr = frame->instr_ptr;\n")
if needs_this and not inst.is_target:
out.emit(f"_Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr;\n")
out.emit(unused_guard)
From 95a73917cd5a204979a78c13ba912621f1eeb2e3 Mon Sep 17 00:00:00 2001
From: Mark Shannon
Date: Fri, 26 Jul 2024 14:35:57 +0100
Subject: [PATCH 15/47] GH-122029: Break INSTRUMENTED_CALL into micro-ops, so
that its behavior is consistent with CALL (GH-122177)
---
Include/internal/pycore_opcode_metadata.h | 6 +-
Include/internal/pycore_uop_ids.h | 129 ++++++++--------
Include/internal/pycore_uop_metadata.h | 4 +
Include/opcode_ids.h | 26 ++--
Lib/_opcode_metadata.py | 26 ++--
Lib/test/test_monitoring.py | 2 +-
Python/bytecodes.c | 71 +++++----
Python/executor_cases.c.h | 34 ++++-
Python/generated_cases.c.h | 176 +++++++++++++++++++---
Python/opcode_targets.h | 2 +-
Python/optimizer_cases.c.h | 18 ++-
11 files changed, 341 insertions(+), 153 deletions(-)
diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h
index f5c439e81a6232..eaba280f1bf1cd 100644
--- a/Include/internal/pycore_opcode_metadata.h
+++ b/Include/internal/pycore_opcode_metadata.h
@@ -214,7 +214,7 @@ int _PyOpcode_num_popped(int opcode, int oparg) {
case IMPORT_NAME:
return 2;
case INSTRUMENTED_CALL:
- return 0;
+ return 2 + oparg;
case INSTRUMENTED_CALL_FUNCTION_EX:
return 0;
case INSTRUMENTED_CALL_KW:
@@ -661,7 +661,7 @@ int _PyOpcode_num_pushed(int opcode, int oparg) {
case IMPORT_NAME:
return 1;
case INSTRUMENTED_CALL:
- return 0;
+ return 1;
case INSTRUMENTED_CALL_FUNCTION_EX:
return 0;
case INSTRUMENTED_CALL_KW:
@@ -1078,7 +1078,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[264] = {
[GET_YIELD_FROM_ITER] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
[IMPORT_FROM] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[IMPORT_NAME] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
- [INSTRUMENTED_CALL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
+ [INSTRUMENTED_CALL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
[INSTRUMENTED_CALL_FUNCTION_EX] = { true, INSTR_FMT_IX, 0 },
[INSTRUMENTED_CALL_KW] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[INSTRUMENTED_END_FOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG },
diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h
index 60de0573baf5f1..d6c910255eb87b 100644
--- a/Include/internal/pycore_uop_ids.h
+++ b/Include/internal/pycore_uop_ids.h
@@ -33,12 +33,11 @@ extern "C" {
#define _BUILD_SLICE BUILD_SLICE
#define _BUILD_STRING BUILD_STRING
#define _BUILD_TUPLE BUILD_TUPLE
-#define _CALL 312
#define _CALL_ALLOC_AND_ENTER_INIT CALL_ALLOC_AND_ENTER_INIT
-#define _CALL_BUILTIN_CLASS 313
-#define _CALL_BUILTIN_FAST 314
-#define _CALL_BUILTIN_FAST_WITH_KEYWORDS 315
-#define _CALL_BUILTIN_O 316
+#define _CALL_BUILTIN_CLASS 312
+#define _CALL_BUILTIN_FAST 313
+#define _CALL_BUILTIN_FAST_WITH_KEYWORDS 314
+#define _CALL_BUILTIN_O 315
#define _CALL_FUNCTION_EX CALL_FUNCTION_EX
#define _CALL_INTRINSIC_1 CALL_INTRINSIC_1
#define _CALL_INTRINSIC_2 CALL_INTRINSIC_2
@@ -46,38 +45,38 @@ extern "C" {
#define _CALL_KW CALL_KW
#define _CALL_LEN CALL_LEN
#define _CALL_LIST_APPEND CALL_LIST_APPEND
-#define _CALL_METHOD_DESCRIPTOR_FAST 317
-#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 318
-#define _CALL_METHOD_DESCRIPTOR_NOARGS 319
-#define _CALL_METHOD_DESCRIPTOR_O 320
-#define _CALL_NON_PY_GENERAL 321
-#define _CALL_STR_1 322
-#define _CALL_TUPLE_1 323
+#define _CALL_METHOD_DESCRIPTOR_FAST 316
+#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 317
+#define _CALL_METHOD_DESCRIPTOR_NOARGS 318
+#define _CALL_METHOD_DESCRIPTOR_O 319
+#define _CALL_NON_PY_GENERAL 320
+#define _CALL_STR_1 321
+#define _CALL_TUPLE_1 322
#define _CALL_TYPE_1 CALL_TYPE_1
-#define _CHECK_ATTR_CLASS 324
-#define _CHECK_ATTR_METHOD_LAZY_DICT 325
-#define _CHECK_ATTR_MODULE 326
-#define _CHECK_ATTR_WITH_HINT 327
-#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 328
+#define _CHECK_ATTR_CLASS 323
+#define _CHECK_ATTR_METHOD_LAZY_DICT 324
+#define _CHECK_ATTR_MODULE 325
+#define _CHECK_ATTR_WITH_HINT 326
+#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 327
#define _CHECK_EG_MATCH CHECK_EG_MATCH
#define _CHECK_EXC_MATCH CHECK_EXC_MATCH
-#define _CHECK_FUNCTION 329
-#define _CHECK_FUNCTION_EXACT_ARGS 330
-#define _CHECK_FUNCTION_VERSION 331
-#define _CHECK_IS_NOT_PY_CALLABLE 332
-#define _CHECK_MANAGED_OBJECT_HAS_VALUES 333
-#define _CHECK_METHOD_VERSION 334
-#define _CHECK_PEP_523 335
-#define _CHECK_PERIODIC 336
-#define _CHECK_STACK_SPACE 337
-#define _CHECK_STACK_SPACE_OPERAND 338
-#define _CHECK_VALIDITY 339
-#define _CHECK_VALIDITY_AND_SET_IP 340
-#define _COMPARE_OP 341
-#define _COMPARE_OP_FLOAT 342
-#define _COMPARE_OP_INT 343
-#define _COMPARE_OP_STR 344
-#define _CONTAINS_OP 345
+#define _CHECK_FUNCTION 328
+#define _CHECK_FUNCTION_EXACT_ARGS 329
+#define _CHECK_FUNCTION_VERSION 330
+#define _CHECK_IS_NOT_PY_CALLABLE 331
+#define _CHECK_MANAGED_OBJECT_HAS_VALUES 332
+#define _CHECK_METHOD_VERSION 333
+#define _CHECK_PEP_523 334
+#define _CHECK_PERIODIC 335
+#define _CHECK_STACK_SPACE 336
+#define _CHECK_STACK_SPACE_OPERAND 337
+#define _CHECK_VALIDITY 338
+#define _CHECK_VALIDITY_AND_SET_IP 339
+#define _COMPARE_OP 340
+#define _COMPARE_OP_FLOAT 341
+#define _COMPARE_OP_INT 342
+#define _COMPARE_OP_STR 343
+#define _CONTAINS_OP 344
#define _CONTAINS_OP_DICT CONTAINS_OP_DICT
#define _CONTAINS_OP_SET CONTAINS_OP_SET
#define _CONVERT_VALUE CONVERT_VALUE
@@ -89,9 +88,10 @@ extern "C" {
#define _DELETE_GLOBAL DELETE_GLOBAL
#define _DELETE_NAME DELETE_NAME
#define _DELETE_SUBSCR DELETE_SUBSCR
-#define _DEOPT 346
+#define _DEOPT 345
#define _DICT_MERGE DICT_MERGE
#define _DICT_UPDATE DICT_UPDATE
+#define _DO_CALL 346
#define _DYNAMIC_EXIT 347
#define _END_SEND END_SEND
#define _ERROR_POP_N 348
@@ -138,7 +138,6 @@ extern "C" {
#define _INIT_CALL_PY_EXACT_ARGS_2 378
#define _INIT_CALL_PY_EXACT_ARGS_3 379
#define _INIT_CALL_PY_EXACT_ARGS_4 380
-#define _INSTRUMENTED_CALL INSTRUMENTED_CALL
#define _INSTRUMENTED_CALL_FUNCTION_EX INSTRUMENTED_CALL_FUNCTION_EX
#define _INSTRUMENTED_CALL_KW INSTRUMENTED_CALL_KW
#define _INSTRUMENTED_FOR_ITER INSTRUMENTED_FOR_ITER
@@ -223,53 +222,55 @@ extern "C" {
#define _MATCH_KEYS MATCH_KEYS
#define _MATCH_MAPPING MATCH_MAPPING
#define _MATCH_SEQUENCE MATCH_SEQUENCE
+#define _MAYBE_EXPAND_METHOD 427
+#define _MONITOR_CALL 428
#define _NOP NOP
#define _POP_EXCEPT POP_EXCEPT
-#define _POP_JUMP_IF_FALSE 427
-#define _POP_JUMP_IF_TRUE 428
+#define _POP_JUMP_IF_FALSE 429
+#define _POP_JUMP_IF_TRUE 430
#define _POP_TOP POP_TOP
-#define _POP_TOP_LOAD_CONST_INLINE_BORROW 429
+#define _POP_TOP_LOAD_CONST_INLINE_BORROW 431
#define _PUSH_EXC_INFO PUSH_EXC_INFO
-#define _PUSH_FRAME 430
+#define _PUSH_FRAME 432
#define _PUSH_NULL PUSH_NULL
-#define _PY_FRAME_GENERAL 431
-#define _REPLACE_WITH_TRUE 432
+#define _PY_FRAME_GENERAL 433
+#define _REPLACE_WITH_TRUE 434
#define _RESUME_CHECK RESUME_CHECK
#define _RETURN_GENERATOR RETURN_GENERATOR
#define _RETURN_VALUE RETURN_VALUE
-#define _SAVE_RETURN_OFFSET 433
-#define _SEND 434
-#define _SEND_GEN_FRAME 435
+#define _SAVE_RETURN_OFFSET 435
+#define _SEND 436
+#define _SEND_GEN_FRAME 437
#define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS
#define _SET_ADD SET_ADD
#define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE
#define _SET_UPDATE SET_UPDATE
-#define _START_EXECUTOR 436
-#define _STORE_ATTR 437
-#define _STORE_ATTR_INSTANCE_VALUE 438
-#define _STORE_ATTR_SLOT 439
-#define _STORE_ATTR_WITH_HINT 440
+#define _START_EXECUTOR 438
+#define _STORE_ATTR 439
+#define _STORE_ATTR_INSTANCE_VALUE 440
+#define _STORE_ATTR_SLOT 441
+#define _STORE_ATTR_WITH_HINT 442
#define _STORE_DEREF STORE_DEREF
-#define _STORE_FAST 441
-#define _STORE_FAST_0 442
-#define _STORE_FAST_1 443
-#define _STORE_FAST_2 444
-#define _STORE_FAST_3 445
-#define _STORE_FAST_4 446
-#define _STORE_FAST_5 447
-#define _STORE_FAST_6 448
-#define _STORE_FAST_7 449
+#define _STORE_FAST 443
+#define _STORE_FAST_0 444
+#define _STORE_FAST_1 445
+#define _STORE_FAST_2 446
+#define _STORE_FAST_3 447
+#define _STORE_FAST_4 448
+#define _STORE_FAST_5 449
+#define _STORE_FAST_6 450
+#define _STORE_FAST_7 451
#define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST
#define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST
#define _STORE_GLOBAL STORE_GLOBAL
#define _STORE_NAME STORE_NAME
#define _STORE_SLICE STORE_SLICE
-#define _STORE_SUBSCR 450
+#define _STORE_SUBSCR 452
#define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT
#define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT
#define _SWAP SWAP
-#define _TIER2_RESUME_CHECK 451
-#define _TO_BOOL 452
+#define _TIER2_RESUME_CHECK 453
+#define _TO_BOOL 454
#define _TO_BOOL_BOOL TO_BOOL_BOOL
#define _TO_BOOL_INT TO_BOOL_INT
#define _TO_BOOL_LIST TO_BOOL_LIST
@@ -279,13 +280,13 @@ extern "C" {
#define _UNARY_NEGATIVE UNARY_NEGATIVE
#define _UNARY_NOT UNARY_NOT
#define _UNPACK_EX UNPACK_EX
-#define _UNPACK_SEQUENCE 453
+#define _UNPACK_SEQUENCE 455
#define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST
#define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE
#define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE
#define _WITH_EXCEPT_START WITH_EXCEPT_START
#define _YIELD_VALUE YIELD_VALUE
-#define MAX_UOP_ID 453
+#define MAX_UOP_ID 455
#ifdef __cplusplus
}
diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h
index 190c6fb2365cc4..fd0d4a67d93538 100644
--- a/Include/internal/pycore_uop_metadata.h
+++ b/Include/internal/pycore_uop_metadata.h
@@ -199,6 +199,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
[_LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = HAS_ARG_FLAG,
[_CHECK_ATTR_METHOD_LAZY_DICT] = HAS_DEOPT_FLAG,
[_LOAD_ATTR_METHOD_LAZY_DICT] = HAS_ARG_FLAG,
+ [_MAYBE_EXPAND_METHOD] = HAS_ARG_FLAG,
[_CHECK_PERIODIC] = HAS_EVAL_BREAK_FLAG,
[_PY_FRAME_GENERAL] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG,
[_CHECK_FUNCTION_VERSION] = HAS_ARG_FLAG | HAS_EXIT_FLAG,
@@ -464,6 +465,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
[_MATCH_KEYS] = "_MATCH_KEYS",
[_MATCH_MAPPING] = "_MATCH_MAPPING",
[_MATCH_SEQUENCE] = "_MATCH_SEQUENCE",
+ [_MAYBE_EXPAND_METHOD] = "_MAYBE_EXPAND_METHOD",
[_NOP] = "_NOP",
[_POP_EXCEPT] = "_POP_EXCEPT",
[_POP_TOP] = "_POP_TOP",
@@ -888,6 +890,8 @@ int _PyUop_num_popped(int opcode, int oparg)
return 1;
case _LOAD_ATTR_METHOD_LAZY_DICT:
return 1;
+ case _MAYBE_EXPAND_METHOD:
+ return 2 + oparg;
case _CHECK_PERIODIC:
return 0;
case _PY_FRAME_GENERAL:
diff --git a/Include/opcode_ids.h b/Include/opcode_ids.h
index d14b48f4289285..54dd76158bf84d 100644
--- a/Include/opcode_ids.h
+++ b/Include/opcode_ids.h
@@ -206,19 +206,19 @@ extern "C" {
#define INSTRUMENTED_END_SEND 238
#define INSTRUMENTED_LOAD_SUPER_ATTR 239
#define INSTRUMENTED_FOR_ITER 240
-#define INSTRUMENTED_CALL 241
-#define INSTRUMENTED_CALL_KW 242
-#define INSTRUMENTED_CALL_FUNCTION_EX 243
-#define INSTRUMENTED_INSTRUCTION 244
-#define INSTRUMENTED_JUMP_FORWARD 245
-#define INSTRUMENTED_JUMP_BACKWARD 246
-#define INSTRUMENTED_POP_JUMP_IF_TRUE 247
-#define INSTRUMENTED_POP_JUMP_IF_FALSE 248
-#define INSTRUMENTED_POP_JUMP_IF_NONE 249
-#define INSTRUMENTED_POP_JUMP_IF_NOT_NONE 250
-#define INSTRUMENTED_RETURN_VALUE 251
-#define INSTRUMENTED_RETURN_CONST 252
-#define INSTRUMENTED_YIELD_VALUE 253
+#define INSTRUMENTED_CALL_KW 241
+#define INSTRUMENTED_CALL_FUNCTION_EX 242
+#define INSTRUMENTED_INSTRUCTION 243
+#define INSTRUMENTED_JUMP_FORWARD 244
+#define INSTRUMENTED_JUMP_BACKWARD 245
+#define INSTRUMENTED_POP_JUMP_IF_TRUE 246
+#define INSTRUMENTED_POP_JUMP_IF_FALSE 247
+#define INSTRUMENTED_POP_JUMP_IF_NONE 248
+#define INSTRUMENTED_POP_JUMP_IF_NOT_NONE 249
+#define INSTRUMENTED_RETURN_VALUE 250
+#define INSTRUMENTED_RETURN_CONST 251
+#define INSTRUMENTED_YIELD_VALUE 252
+#define INSTRUMENTED_CALL 253
#define INSTRUMENTED_LINE 254
#define JUMP 256
#define JUMP_NO_INTERRUPT 257
diff --git a/Lib/_opcode_metadata.py b/Lib/_opcode_metadata.py
index 01c22a89846e97..05ee1f29b58331 100644
--- a/Lib/_opcode_metadata.py
+++ b/Lib/_opcode_metadata.py
@@ -310,19 +310,19 @@
'INSTRUMENTED_END_SEND': 238,
'INSTRUMENTED_LOAD_SUPER_ATTR': 239,
'INSTRUMENTED_FOR_ITER': 240,
- 'INSTRUMENTED_CALL': 241,
- 'INSTRUMENTED_CALL_KW': 242,
- 'INSTRUMENTED_CALL_FUNCTION_EX': 243,
- 'INSTRUMENTED_INSTRUCTION': 244,
- 'INSTRUMENTED_JUMP_FORWARD': 245,
- 'INSTRUMENTED_JUMP_BACKWARD': 246,
- 'INSTRUMENTED_POP_JUMP_IF_TRUE': 247,
- 'INSTRUMENTED_POP_JUMP_IF_FALSE': 248,
- 'INSTRUMENTED_POP_JUMP_IF_NONE': 249,
- 'INSTRUMENTED_POP_JUMP_IF_NOT_NONE': 250,
- 'INSTRUMENTED_RETURN_VALUE': 251,
- 'INSTRUMENTED_RETURN_CONST': 252,
- 'INSTRUMENTED_YIELD_VALUE': 253,
+ 'INSTRUMENTED_CALL_KW': 241,
+ 'INSTRUMENTED_CALL_FUNCTION_EX': 242,
+ 'INSTRUMENTED_INSTRUCTION': 243,
+ 'INSTRUMENTED_JUMP_FORWARD': 244,
+ 'INSTRUMENTED_JUMP_BACKWARD': 245,
+ 'INSTRUMENTED_POP_JUMP_IF_TRUE': 246,
+ 'INSTRUMENTED_POP_JUMP_IF_FALSE': 247,
+ 'INSTRUMENTED_POP_JUMP_IF_NONE': 248,
+ 'INSTRUMENTED_POP_JUMP_IF_NOT_NONE': 249,
+ 'INSTRUMENTED_RETURN_VALUE': 250,
+ 'INSTRUMENTED_RETURN_CONST': 251,
+ 'INSTRUMENTED_YIELD_VALUE': 252,
+ 'INSTRUMENTED_CALL': 253,
'JUMP': 256,
'JUMP_NO_INTERRUPT': 257,
'LOAD_CLOSURE': 258,
diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py
index a07be306986b43..1a129b9432e72d 100644
--- a/Lib/test/test_monitoring.py
+++ b/Lib/test/test_monitoring.py
@@ -1575,7 +1575,7 @@ def f():
('line', 'method', 2),
('line', 'method', 3),
('line', 'method', 2),
- ('call', 'method', 1),
+ ('call', 'method', d["b"]),
('line', 'method', 1),
('line', 'method', 1),
('line', 'get_events', 11),
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 971397c955de09..871e2dbf358418 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -3241,20 +3241,6 @@ dummy_func(
unused/1 +
_LOAD_ATTR_METHOD_LAZY_DICT;
- inst(INSTRUMENTED_CALL, (unused/3 -- )) {
- int is_meth = PyStackRef_AsPyObjectBorrow(PEEK(oparg + 1)) != NULL;
- int total_args = oparg + is_meth;
- PyObject *function = PyStackRef_AsPyObjectBorrow(PEEK(oparg + 2));
- PyObject *arg = total_args == 0 ?
- &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(PEEK(total_args));
- int err = _Py_call_instrumentation_2args(
- tstate, PY_MONITORING_EVENT_CALL,
- frame, this_instr, function, arg);
- ERROR_IF(err, error);
- PAUSE_ADAPTIVE_COUNTER(this_instr[1].counter);
- GO_TO_INSTRUCTION(CALL);
- }
-
// Cache layout: counter/1, func_version/2
// CALL_INTRINSIC_1/2, CALL_KW, and CALL_FUNCTION_EX aren't members!
family(CALL, INLINE_CACHE_ENTRIES_CALL) = {
@@ -3292,27 +3278,33 @@ dummy_func(
#endif /* ENABLE_SPECIALIZATION */
}
+ op(_MAYBE_EXPAND_METHOD, (callable, self_or_null, args[oparg] -- func, maybe_self, args[oparg])) {
+ if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) {
+ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
+ PyObject *self = ((PyMethodObject *)callable_o)->im_self;
+ maybe_self = PyStackRef_FromPyObjectNew(self);
+ PyObject *method = ((PyMethodObject *)callable_o)->im_func;
+ func = PyStackRef_FromPyObjectNew(method);
+ /* Make sure that callable and all args are in memory */
+ args[-2] = func;
+ args[-1] = maybe_self;
+ PyStackRef_CLOSE(callable);
+ }
+ else {
+ func = callable;
+ maybe_self = self_or_null;
+ }
+ }
+
// When calling Python, inline the call using DISPATCH_INLINED().
- op(_CALL, (callable, self_or_null, args[oparg] -- res)) {
+ op(_DO_CALL, (callable, self_or_null, args[oparg] -- res)) {
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
- PyObject *self_or_null_o = PyStackRef_AsPyObjectBorrow(self_or_null);
// oparg counts all of the args, but *not* self:
int total_args = oparg;
- if (self_or_null_o != NULL) {
- args--;
- total_args++;
- }
- else if (Py_TYPE(callable_o) == &PyMethod_Type) {
+ if (!PyStackRef_IsNull(self_or_null)) {
args--;
total_args++;
- PyObject *self = ((PyMethodObject *)callable_o)->im_self;
- args[0] = PyStackRef_FromPyObjectNew(self);
- PyObject *method = ((PyMethodObject *)callable_o)->im_func;
- args[-1] = PyStackRef_FromPyObjectNew(method);
- PyStackRef_CLOSE(callable);
- callable_o = method;
- callable = args[-1];
}
// Check if the call can be inlined or not
if (Py_TYPE(callable_o) == &PyFunction_Type &&
@@ -3376,7 +3368,28 @@ dummy_func(
CHECK_EVAL_BREAKER();
}
- macro(CALL) = _SPECIALIZE_CALL + unused/2 + _CALL + _CHECK_PERIODIC;
+ op(_MONITOR_CALL, (func, maybe_self, args[oparg] -- func, maybe_self, args[oparg])) {
+ int is_meth = !PyStackRef_IsNull(maybe_self);
+ PyObject *function = PyStackRef_AsPyObjectBorrow(func);
+ PyObject *arg0;
+ if (is_meth) {
+ arg0 = PyStackRef_AsPyObjectBorrow(maybe_self);
+ }
+ else if (oparg) {
+ arg0 = PyStackRef_AsPyObjectBorrow(args[0]);
+ }
+ else {
+ arg0 = &_PyInstrumentation_MISSING;
+ }
+ int err = _Py_call_instrumentation_2args(
+ tstate, PY_MONITORING_EVENT_CALL,
+ frame, this_instr, function, arg0
+ );
+ ERROR_IF(err, error);
+ }
+
+ macro(CALL) = _SPECIALIZE_CALL + unused/2 + _MAYBE_EXPAND_METHOD + _DO_CALL + _CHECK_PERIODIC;
+ macro(INSTRUMENTED_CALL) = unused/3 + _MAYBE_EXPAND_METHOD + _MONITOR_CALL + _DO_CALL + _CHECK_PERIODIC;
op(_PY_FRAME_GENERAL, (callable, self_or_null, args[oparg] -- new_frame: _PyInterpreterFrame*)) {
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index 288e0f9135c27e..1ced8b951b5ce9 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -3584,15 +3584,45 @@
break;
}
- /* _INSTRUMENTED_CALL is not a viable micro-op for tier 2 because it is instrumented */
+ case _MAYBE_EXPAND_METHOD: {
+ _PyStackRef *args;
+ _PyStackRef self_or_null;
+ _PyStackRef callable;
+ _PyStackRef func;
+ _PyStackRef maybe_self;
+ oparg = CURRENT_OPARG();
+ args = &stack_pointer[-oparg];
+ self_or_null = stack_pointer[-1 - oparg];
+ callable = stack_pointer[-2 - oparg];
+ if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) {
+ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
+ PyObject *self = ((PyMethodObject *)callable_o)->im_self;
+ maybe_self = PyStackRef_FromPyObjectNew(self);
+ PyObject *method = ((PyMethodObject *)callable_o)->im_func;
+ func = PyStackRef_FromPyObjectNew(method);
+ /* Make sure that callable and all args are in memory */
+ args[-2] = func;
+ args[-1] = maybe_self;
+ PyStackRef_CLOSE(callable);
+ }
+ else {
+ func = callable;
+ maybe_self = self_or_null;
+ }
+ stack_pointer[-2 - oparg] = func;
+ stack_pointer[-1 - oparg] = maybe_self;
+ break;
+ }
- /* _CALL is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */
+ /* _DO_CALL is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */
case _CHECK_PERIODIC: {
CHECK_EVAL_BREAKER();
break;
}
+ /* _MONITOR_CALL is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */
+
case _PY_FRAME_GENERAL: {
_PyStackRef *args;
_PyStackRef self_or_null;
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 634053a93837c6..76d1cc7ad6cf95 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -773,6 +773,8 @@
_PyStackRef callable;
_PyStackRef self_or_null;
_PyStackRef *args;
+ _PyStackRef func;
+ _PyStackRef maybe_self;
_PyStackRef res;
// _SPECIALIZE_CALL
self_or_null = stack_pointer[-1 - oparg];
@@ -792,26 +794,34 @@
#endif /* ENABLE_SPECIALIZATION */
}
/* Skip 2 cache entries */
- // _CALL
+ // _MAYBE_EXPAND_METHOD
+ {
+ if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) {
+ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
+ PyObject *self = ((PyMethodObject *)callable_o)->im_self;
+ maybe_self = PyStackRef_FromPyObjectNew(self);
+ PyObject *method = ((PyMethodObject *)callable_o)->im_func;
+ func = PyStackRef_FromPyObjectNew(method);
+ /* Make sure that callable and all args are in memory */
+ args[-2] = func;
+ args[-1] = maybe_self;
+ PyStackRef_CLOSE(callable);
+ }
+ else {
+ func = callable;
+ maybe_self = self_or_null;
+ }
+ }
+ // _DO_CALL
+ self_or_null = maybe_self;
+ callable = func;
{
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
- PyObject *self_or_null_o = PyStackRef_AsPyObjectBorrow(self_or_null);
// oparg counts all of the args, but *not* self:
int total_args = oparg;
- if (self_or_null_o != NULL) {
- args--;
- total_args++;
- }
- else if (Py_TYPE(callable_o) == &PyMethod_Type) {
+ if (!PyStackRef_IsNull(self_or_null)) {
args--;
total_args++;
- PyObject *self = ((PyMethodObject *)callable_o)->im_self;
- args[0] = PyStackRef_FromPyObjectNew(self);
- PyObject *method = ((PyMethodObject *)callable_o)->im_func;
- args[-1] = PyStackRef_FromPyObjectNew(method);
- PyStackRef_CLOSE(callable);
- callable_o = method;
- callable = args[-1];
}
// Check if the call can be inlined or not
if (Py_TYPE(callable_o) == &PyFunction_Type &&
@@ -3504,18 +3514,134 @@
(void)this_instr;
next_instr += 4;
INSTRUCTION_STATS(INSTRUMENTED_CALL);
+ _PyStackRef callable;
+ _PyStackRef self_or_null;
+ _PyStackRef *args;
+ _PyStackRef func;
+ _PyStackRef maybe_self;
+ _PyStackRef res;
/* Skip 3 cache entries */
- int is_meth = PyStackRef_AsPyObjectBorrow(PEEK(oparg + 1)) != NULL;
- int total_args = oparg + is_meth;
- PyObject *function = PyStackRef_AsPyObjectBorrow(PEEK(oparg + 2));
- PyObject *arg = total_args == 0 ?
- &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(PEEK(total_args));
- int err = _Py_call_instrumentation_2args(
- tstate, PY_MONITORING_EVENT_CALL,
- frame, this_instr, function, arg);
- if (err) goto error;
- PAUSE_ADAPTIVE_COUNTER(this_instr[1].counter);
- GO_TO_INSTRUCTION(CALL);
+ // _MAYBE_EXPAND_METHOD
+ args = &stack_pointer[-oparg];
+ self_or_null = stack_pointer[-1 - oparg];
+ callable = stack_pointer[-2 - oparg];
+ {
+ if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) {
+ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
+ PyObject *self = ((PyMethodObject *)callable_o)->im_self;
+ maybe_self = PyStackRef_FromPyObjectNew(self);
+ PyObject *method = ((PyMethodObject *)callable_o)->im_func;
+ func = PyStackRef_FromPyObjectNew(method);
+ /* Make sure that callable and all args are in memory */
+ args[-2] = func;
+ args[-1] = maybe_self;
+ PyStackRef_CLOSE(callable);
+ }
+ else {
+ func = callable;
+ maybe_self = self_or_null;
+ }
+ }
+ // _MONITOR_CALL
+ {
+ int is_meth = !PyStackRef_IsNull(maybe_self);
+ PyObject *function = PyStackRef_AsPyObjectBorrow(func);
+ PyObject *arg0;
+ if (is_meth) {
+ arg0 = PyStackRef_AsPyObjectBorrow(maybe_self);
+ }
+ else if (oparg) {
+ arg0 = PyStackRef_AsPyObjectBorrow(args[0]);
+ }
+ else {
+ arg0 = &_PyInstrumentation_MISSING;
+ }
+ int err = _Py_call_instrumentation_2args(
+ tstate, PY_MONITORING_EVENT_CALL,
+ frame, this_instr, function, arg0
+ );
+ if (err) goto error;
+ }
+ // _DO_CALL
+ self_or_null = maybe_self;
+ callable = func;
+ {
+ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
+ // oparg counts all of the args, but *not* self:
+ int total_args = oparg;
+ if (!PyStackRef_IsNull(self_or_null)) {
+ args--;
+ total_args++;
+ }
+ // Check if the call can be inlined or not
+ if (Py_TYPE(callable_o) == &PyFunction_Type &&
+ tstate->interp->eval_frame == NULL &&
+ ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall)
+ {
+ int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags;
+ PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o));
+ _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit(
+ tstate, (PyFunctionObject *)PyStackRef_AsPyObjectSteal(callable), locals,
+ args, total_args, NULL
+ );
+ // Manipulate stack directly since we leave using DISPATCH_INLINED().
+ STACK_SHRINK(oparg + 2);
+ // The frame has stolen all the arguments from the stack,
+ // so there is no need to clean them up.
+ if (new_frame == NULL) {
+ goto error;
+ }
+ frame->return_offset = (uint16_t)(next_instr - this_instr);
+ DISPATCH_INLINED(new_frame);
+ }
+ /* Callable is not a normal Python function */
+ STACKREFS_TO_PYOBJECTS(args, total_args, args_o);
+ if (CONVERSION_FAILED(args_o)) {
+ PyStackRef_CLOSE(callable);
+ PyStackRef_CLOSE(self_or_null);
+ for (int _i = oparg; --_i >= 0;) {
+ PyStackRef_CLOSE(args[_i]);
+ }
+ if (true) { stack_pointer += -2 - oparg; goto error; }
+ }
+ PyObject *res_o = PyObject_Vectorcall(
+ callable_o, args_o,
+ total_args | PY_VECTORCALL_ARGUMENTS_OFFSET,
+ NULL);
+ STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
+ if (opcode == INSTRUMENTED_CALL) {
+ PyObject *arg = total_args == 0 ?
+ &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(args[0]);
+ if (res_o == NULL) {
+ _Py_call_instrumentation_exc2(
+ tstate, PY_MONITORING_EVENT_C_RAISE,
+ frame, this_instr, callable_o, arg);
+ }
+ else {
+ int err = _Py_call_instrumentation_2args(
+ tstate, PY_MONITORING_EVENT_C_RETURN,
+ frame, this_instr, callable_o, arg);
+ if (err < 0) {
+ Py_CLEAR(res_o);
+ }
+ }
+ }
+ assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
+ PyStackRef_CLOSE(callable);
+ for (int i = 0; i < total_args; i++) {
+ PyStackRef_CLOSE(args[i]);
+ }
+ if (res_o == NULL) { stack_pointer += -2 - oparg; goto error; }
+ res = PyStackRef_FromPyObjectSteal(res_o);
+ }
+ // _CHECK_PERIODIC
+ {
+ }
+ stack_pointer[-2 - oparg] = res;
+ stack_pointer += -1 - oparg;
+ assert(WITHIN_STACK_BOUNDS());
+ CHECK_EVAL_BREAKER();
+ DISPATCH();
}
TARGET(INSTRUMENTED_CALL_FUNCTION_EX) {
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index 224aeb834935eb..6b5f231e13d15a 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -240,7 +240,6 @@ static void *opcode_targets[256] = {
&&TARGET_INSTRUMENTED_END_SEND,
&&TARGET_INSTRUMENTED_LOAD_SUPER_ATTR,
&&TARGET_INSTRUMENTED_FOR_ITER,
- &&TARGET_INSTRUMENTED_CALL,
&&TARGET_INSTRUMENTED_CALL_KW,
&&TARGET_INSTRUMENTED_CALL_FUNCTION_EX,
&&TARGET_INSTRUMENTED_INSTRUCTION,
@@ -253,6 +252,7 @@ static void *opcode_targets[256] = {
&&TARGET_INSTRUMENTED_RETURN_VALUE,
&&TARGET_INSTRUMENTED_RETURN_CONST,
&&TARGET_INSTRUMENTED_YIELD_VALUE,
+ &&TARGET_INSTRUMENTED_CALL,
&&TARGET_INSTRUMENTED_LINE,
&&_unknown_opcode,
};
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 3c9e6d3043cde1..166b1674bc3334 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -1598,14 +1598,28 @@
break;
}
- /* _INSTRUMENTED_CALL is not a viable micro-op for tier 2 */
+ case _MAYBE_EXPAND_METHOD: {
+ _Py_UopsSymbol *func;
+ _Py_UopsSymbol *maybe_self;
+ _Py_UopsSymbol **args;
+ func = sym_new_not_null(ctx);
+ maybe_self = sym_new_not_null(ctx);
+ for (int _i = oparg; --_i >= 0;) {
+ args[_i] = sym_new_not_null(ctx);
+ }
+ stack_pointer[-2 - oparg] = func;
+ stack_pointer[-1 - oparg] = maybe_self;
+ break;
+ }
- /* _CALL is not a viable micro-op for tier 2 */
+ /* _DO_CALL is not a viable micro-op for tier 2 */
case _CHECK_PERIODIC: {
break;
}
+ /* _MONITOR_CALL is not a viable micro-op for tier 2 */
+
case _PY_FRAME_GENERAL: {
_Py_UopsSymbol **args;
_Py_UopsSymbol *self_or_null;
From 2c42e13e80610a9dedcb15b57d142602e8143481 Mon Sep 17 00:00:00 2001
From: Mark Shannon
Date: Fri, 26 Jul 2024 14:37:35 +0100
Subject: [PATCH 16/47] GH-116090: Fix test and clarify behavior for exception
events when exhausting a generator. (GH-120697)
---
Doc/library/sys.monitoring.rst | 4 ++++
Lib/test/test_monitoring.py | 35 ++++++++++++++++++++++++++++------
2 files changed, 33 insertions(+), 6 deletions(-)
diff --git a/Doc/library/sys.monitoring.rst b/Doc/library/sys.monitoring.rst
index 0fa06da522049f..3ead20815fa30e 100644
--- a/Doc/library/sys.monitoring.rst
+++ b/Doc/library/sys.monitoring.rst
@@ -226,6 +226,10 @@ To allow tools to monitor for real exceptions without slowing down generators
and coroutines, the :monitoring-event:`STOP_ITERATION` event is provided.
:monitoring-event:`STOP_ITERATION` can be locally disabled, unlike :monitoring-event:`RAISE`.
+Note that the :monitoring-event:`STOP_ITERATION` event and the :monitoring-event:`RAISE`
+event for a :exc:`StopIteration` exception are equivalent, and are treated as interchangeable
+when generating events. Implementations will favor :monitoring-event:`STOP_ITERATION` for
+performance reasons, but may generate a :monitoring-event:`RAISE` event with a :exc:`StopIteration`.
Turning events on and off
-------------------------
diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py
index 1a129b9432e72d..d7043cd4866a1c 100644
--- a/Lib/test/test_monitoring.py
+++ b/Lib/test/test_monitoring.py
@@ -832,20 +832,43 @@ def func1():
self.check_events(func1, [("raise", KeyError)])
- # gh-116090: This test doesn't really require specialization, but running
- # it without specialization exposes a monitoring bug.
- @requires_specialization
def test_implicit_stop_iteration(self):
+ """Generators are documented as raising a StopIteration
+ when they terminate.
+ However, we don't do that if we can avoid it, for speed.
+ sys.monitoring handles that by injecting a STOP_ITERATION
+ event when we would otherwise have skip the RAISE event.
+ This test checks that both paths record an equivalent event.
+ """
def gen():
yield 1
return 2
- def implicit_stop_iteration():
- for _ in gen():
+ def implicit_stop_iteration(iterator=None):
+ if iterator is None:
+ iterator = gen()
+ for _ in iterator:
pass
- self.check_events(implicit_stop_iteration, [("raise", StopIteration)], recorders=(StopiterationRecorder,))
+ recorders=(ExceptionRecorder, StopiterationRecorder,)
+ expected = [("raise", StopIteration)]
+
+ # Make sure that the loop is unspecialized, and that it will not
+ # re-specialize immediately, so that we can we can test the
+ # unspecialized version of the loop first.
+ # Note: this assumes that we don't specialize loops over sets.
+ implicit_stop_iteration(set(range(100)))
+
+ # This will record a RAISE event for the StopIteration.
+ self.check_events(implicit_stop_iteration, expected, recorders=recorders)
+
+ # Now specialize, so that we see a STOP_ITERATION event.
+ for _ in range(100):
+ implicit_stop_iteration()
+
+ # This will record a STOP_ITERATION event for the StopIteration.
+ self.check_events(implicit_stop_iteration, expected, recorders=recorders)
initial = [
("raise", ZeroDivisionError),
From bc94cf7e254e43318223553a7959115573c679a5 Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Fri, 26 Jul 2024 14:39:56 +0100
Subject: [PATCH 17/47] gh-122245: move checks for writes and shadowing of
__debug__ to symtable (#122246)
---
Doc/whatsnew/3.14.rst | 5 +
Lib/test/test_syntax.py | 79 ++++++++++++++++
...-07-24-22-39-07.gh-issue-122245.LVa9v8.rst | 4 +
Python/compile.c | 76 ---------------
Python/symtable.c | 92 +++++++++++++++++--
5 files changed, 173 insertions(+), 83 deletions(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-07-24-22-39-07.gh-issue-122245.LVa9v8.rst
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index bd8bdcb6732fde..d2ba7ada76733a 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -80,6 +80,11 @@ Other Language Changes
command line option. For example, ``python -O -c 'assert await 1'``
now produces a :exc:`SyntaxError`. (Contributed by Jelle Zijlstra in :gh:`121637`.)
+* Writes to ``__debug__`` are now detected even if the code is optimized
+ away by the :option:`-O` command line option. For example,
+ ``python -O -c 'assert (__debug__ := 1)'`` now produces a
+ :exc:`SyntaxError`. (Contributed by Irit Katriel in :gh:`122245`.)
+
* Added class methods :meth:`float.from_number` and :meth:`complex.from_number`
to convert a number to :class:`float` or :class:`complex` type correspondingly.
They raise an error if the argument is a string.
diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py
index cdeb26adf34d89..4421d03a6d2206 100644
--- a/Lib/test/test_syntax.py
+++ b/Lib/test/test_syntax.py
@@ -59,6 +59,18 @@
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__
+>>> def __debug__(): pass
+Traceback (most recent call last):
+SyntaxError: cannot assign to __debug__
+
+>>> async def __debug__(): pass
+Traceback (most recent call last):
+SyntaxError: cannot assign to __debug__
+
+>>> class __debug__: pass
+Traceback (most recent call last):
+SyntaxError: cannot assign to __debug__
+
>>> del __debug__
Traceback (most recent call last):
SyntaxError: cannot delete __debug__
@@ -786,6 +798,9 @@
>>> __debug__: int
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__
+>>> x.__debug__: int
+Traceback (most recent call last):
+SyntaxError: cannot assign to __debug__
>>> f(a=)
Traceback (most recent call last):
SyntaxError: expected argument value expression
@@ -1182,6 +1197,24 @@
Traceback (most recent call last):
SyntaxError: expected ':'
+ >>> match x:
+ ... case a, __debug__, b:
+ ... pass
+ Traceback (most recent call last):
+ SyntaxError: cannot assign to __debug__
+
+ >>> match x:
+ ... case a, b, *__debug__:
+ ... pass
+ Traceback (most recent call last):
+ SyntaxError: cannot assign to __debug__
+
+ >>> match x:
+ ... case Foo(a, __debug__=1, b=2):
+ ... pass
+ Traceback (most recent call last):
+ SyntaxError: cannot assign to __debug__
+
>>> if x = 3:
... pass
Traceback (most recent call last):
@@ -1275,6 +1308,15 @@
Traceback (most recent call last):
SyntaxError: expected 'except' or 'finally' block
+Custom error message for __debug__ as exception variable
+
+ >>> try:
+ ... pass
+ ... except TypeError as __debug__:
+ ... pass
+ Traceback (most recent call last):
+ SyntaxError: cannot assign to __debug__
+
Custom error message for try block mixing except and except*
>>> try:
@@ -1522,6 +1564,19 @@
Traceback (most recent call last):
IndentationError: expected an indented block after class definition on line 1
+ >>> class C(__debug__=42): ...
+ Traceback (most recent call last):
+ SyntaxError: cannot assign to __debug__
+
+ >>> class Meta(type):
+ ... def __new__(*args, **kwargs):
+ ... pass
+
+ >>> class C(metaclass=Meta, __debug__=42):
+ ... pass
+ Traceback (most recent call last):
+ SyntaxError: cannot assign to __debug__
+
>>> match something:
... pass
Traceback (most recent call last):
@@ -1708,6 +1763,26 @@
Traceback (most recent call last):
SyntaxError: Did you mean to use 'from ... import ...' instead?
+>>> import __debug__
+Traceback (most recent call last):
+SyntaxError: cannot assign to __debug__
+
+>>> import a as __debug__
+Traceback (most recent call last):
+SyntaxError: cannot assign to __debug__
+
+>>> import a.b.c as __debug__
+Traceback (most recent call last):
+SyntaxError: cannot assign to __debug__
+
+>>> from a import __debug__
+Traceback (most recent call last):
+SyntaxError: cannot assign to __debug__
+
+>>> from a import b as __debug__
+Traceback (most recent call last):
+SyntaxError: cannot assign to __debug__
+
# Check that we dont raise the "trailing comma" error if there is more
# input to the left of the valid part that we parsed.
@@ -2186,6 +2261,10 @@ def f(x: *b)
...
SyntaxError: yield expression cannot be used within a type alias
+ >>> type __debug__ = int
+ Traceback (most recent call last):
+ SyntaxError: cannot assign to __debug__
+
>>> class A[T]((x := 3)): ...
Traceback (most recent call last):
...
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-07-24-22-39-07.gh-issue-122245.LVa9v8.rst b/Misc/NEWS.d/next/Core and Builtins/2024-07-24-22-39-07.gh-issue-122245.LVa9v8.rst
new file mode 100644
index 00000000000000..453c45e2f7ae3f
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2024-07-24-22-39-07.gh-issue-122245.LVa9v8.rst
@@ -0,0 +1,4 @@
+Detection of writes to ``__debug__`` is moved from the compiler's codegen
+stage to the symtable. This means that these errors now detected even in
+code that is optimized away before codegen (such as assertions with the
+:option:`-O` command line option.)
diff --git a/Python/compile.c b/Python/compile.c
index 9707759c99c943..d07a435bdf8dac 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -1954,55 +1954,6 @@ compiler_default_arguments(struct compiler *c, location loc,
return funcflags;
}
-static bool
-forbidden_name(struct compiler *c, location loc, identifier name,
- expr_context_ty ctx)
-{
- if (ctx == Store && _PyUnicode_EqualToASCIIString(name, "__debug__")) {
- compiler_error(c, loc, "cannot assign to __debug__");
- return true;
- }
- if (ctx == Del && _PyUnicode_EqualToASCIIString(name, "__debug__")) {
- compiler_error(c, loc, "cannot delete __debug__");
- return true;
- }
- return false;
-}
-
-static int
-compiler_check_debug_one_arg(struct compiler *c, arg_ty arg)
-{
- if (arg != NULL) {
- if (forbidden_name(c, LOC(arg), arg->arg, Store)) {
- return ERROR;
- }
- }
- return SUCCESS;
-}
-
-static int
-compiler_check_debug_args_seq(struct compiler *c, asdl_arg_seq *args)
-{
- if (args != NULL) {
- for (Py_ssize_t i = 0, n = asdl_seq_LEN(args); i < n; i++) {
- RETURN_IF_ERROR(
- compiler_check_debug_one_arg(c, asdl_seq_GET(args, i)));
- }
- }
- return SUCCESS;
-}
-
-static int
-compiler_check_debug_args(struct compiler *c, arguments_ty args)
-{
- RETURN_IF_ERROR(compiler_check_debug_args_seq(c, args->posonlyargs));
- RETURN_IF_ERROR(compiler_check_debug_args_seq(c, args->args));
- RETURN_IF_ERROR(compiler_check_debug_one_arg(c, args->vararg));
- RETURN_IF_ERROR(compiler_check_debug_args_seq(c, args->kwonlyargs));
- RETURN_IF_ERROR(compiler_check_debug_one_arg(c, args->kwarg));
- return SUCCESS;
-}
-
static int
wrap_in_stopiteration_handler(struct compiler *c)
{
@@ -2267,7 +2218,6 @@ compiler_function(struct compiler *c, stmt_ty s, int is_async)
type_params = s->v.FunctionDef.type_params;
}
- RETURN_IF_ERROR(compiler_check_debug_args(c, args));
RETURN_IF_ERROR(compiler_decorators(c, decos));
firstlineno = s->lineno;
@@ -2910,8 +2860,6 @@ compiler_lambda(struct compiler *c, expr_ty e)
arguments_ty args = e->v.Lambda.args;
assert(e->kind == Lambda_kind);
- RETURN_IF_ERROR(compiler_check_debug_args(c, args));
-
location loc = LOC(e);
funcflags = compiler_default_arguments(c, loc, args);
if (funcflags == -1) {
@@ -4086,10 +4034,6 @@ compiler_nameop(struct compiler *c, location loc,
!_PyUnicode_EqualToASCIIString(name, "True") &&
!_PyUnicode_EqualToASCIIString(name, "False"));
- if (forbidden_name(c, loc, name, ctx)) {
- return ERROR;
- }
-
mangled = compiler_maybe_mangle(c, name);
if (!mangled) {
return ERROR;
@@ -4878,10 +4822,6 @@ validate_keywords(struct compiler *c, asdl_keyword_seq *keywords)
if (key->arg == NULL) {
continue;
}
- location loc = LOC(key);
- if (forbidden_name(c, loc, key->arg, Store)) {
- return ERROR;
- }
for (Py_ssize_t j = i + 1; j < nkeywords; j++) {
keyword_ty other = ((keyword_ty)asdl_seq_GET(keywords, j));
if (other->arg && !PyUnicode_Compare(key->arg, other->arg)) {
@@ -6135,9 +6075,6 @@ compiler_visit_expr(struct compiler *c, expr_ty e)
ADDOP_NAME(c, loc, LOAD_ATTR, e->v.Attribute.attr, names);
break;
case Store:
- if (forbidden_name(c, loc, e->v.Attribute.attr, e->v.Attribute.ctx)) {
- return ERROR;
- }
ADDOP_NAME(c, loc, STORE_ATTR, e->v.Attribute.attr, names);
break;
case Del:
@@ -6331,9 +6268,6 @@ compiler_annassign(struct compiler *c, stmt_ty s)
}
switch (targ->kind) {
case Name_kind:
- if (forbidden_name(c, loc, targ->v.Name.id, Store)) {
- return ERROR;
- }
/* If we have a simple name in a module or class, store annotation. */
if (s->v.AnnAssign.simple &&
(c->u->u_scope_type == COMPILER_SCOPE_MODULE ||
@@ -6365,9 +6299,6 @@ compiler_annassign(struct compiler *c, stmt_ty s)
}
break;
case Attribute_kind:
- if (forbidden_name(c, loc, targ->v.Attribute.attr, Store)) {
- return ERROR;
- }
if (!s->v.AnnAssign.value &&
check_ann_expr(c, targ->v.Attribute.value) < 0) {
return ERROR;
@@ -6631,9 +6562,6 @@ pattern_helper_store_name(struct compiler *c, location loc,
ADDOP(c, loc, POP_TOP);
return SUCCESS;
}
- if (forbidden_name(c, loc, n, Store)) {
- return ERROR;
- }
// Can't assign to the same name twice:
int duplicate = PySequence_Contains(pc->stores, n);
RETURN_IF_ERROR(duplicate);
@@ -6791,10 +6719,6 @@ validate_kwd_attrs(struct compiler *c, asdl_identifier_seq *attrs, asdl_pattern_
Py_ssize_t nattrs = asdl_seq_LEN(attrs);
for (Py_ssize_t i = 0; i < nattrs; i++) {
identifier attr = ((identifier)asdl_seq_GET(attrs, i));
- location loc = LOC((pattern_ty) asdl_seq_GET(patterns, i));
- if (forbidden_name(c, loc, attr, Store)) {
- return ERROR;
- }
for (Py_ssize_t j = i + 1; j < nattrs; j++) {
identifier other = ((identifier)asdl_seq_GET(attrs, j));
if (!PyUnicode_Compare(attr, other)) {
diff --git a/Python/symtable.c b/Python/symtable.c
index c4508cac7f5928..a5fa7588785d8b 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -1495,8 +1495,57 @@ symtable_add_def_helper(struct symtable *st, PyObject *name, int flag, struct _s
}
static int
-symtable_add_def(struct symtable *st, PyObject *name, int flag, _Py_SourceLocation loc)
+check_name(struct symtable *st, PyObject *name, _Py_SourceLocation loc,
+ expr_context_ty ctx)
{
+ if (ctx == Store && _PyUnicode_EqualToASCIIString(name, "__debug__")) {
+ PyErr_SetString(PyExc_SyntaxError, "cannot assign to __debug__");
+ SET_ERROR_LOCATION(st->st_filename, loc);
+ return 0;
+ }
+ if (ctx == Del && _PyUnicode_EqualToASCIIString(name, "__debug__")) {
+ PyErr_SetString(PyExc_SyntaxError, "cannot delete __debug__");
+ SET_ERROR_LOCATION(st->st_filename, loc);
+ return 0;
+ }
+ return 1;
+}
+
+static int
+check_keywords(struct symtable *st, asdl_keyword_seq *keywords)
+{
+ for (Py_ssize_t i = 0; i < asdl_seq_LEN(keywords); i++) {
+ keyword_ty key = ((keyword_ty)asdl_seq_GET(keywords, i));
+ if (key->arg && !check_name(st, key->arg, LOCATION(key), Store)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+check_kwd_patterns(struct symtable *st, pattern_ty p)
+{
+ assert(p->kind == MatchClass_kind);
+ asdl_identifier_seq *kwd_attrs = p->v.MatchClass.kwd_attrs;
+ asdl_pattern_seq *kwd_patterns = p->v.MatchClass.kwd_patterns;
+ for (Py_ssize_t i = 0; i < asdl_seq_LEN(kwd_attrs); i++) {
+ _Py_SourceLocation loc = LOCATION(asdl_seq_GET(kwd_patterns, i));
+ if (!check_name(st, asdl_seq_GET(kwd_attrs, i), loc, Store)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+symtable_add_def_ctx(struct symtable *st, PyObject *name, int flag,
+ _Py_SourceLocation loc, expr_context_ty ctx)
+{
+ int write_mask = DEF_PARAM | DEF_LOCAL | DEF_IMPORT;
+ if ((flag & write_mask) && !check_name(st, name, loc, ctx)) {
+ return 0;
+ }
if ((flag & DEF_TYPE_PARAM) && st->st_cur->ste_mangled_names != NULL) {
if(PySet_Add(st->st_cur->ste_mangled_names, name) < 0) {
return 0;
@@ -1505,6 +1554,14 @@ symtable_add_def(struct symtable *st, PyObject *name, int flag, _Py_SourceLocati
return symtable_add_def_helper(st, name, flag, st->st_cur, loc);
}
+static int
+symtable_add_def(struct symtable *st, PyObject *name, int flag,
+ _Py_SourceLocation loc)
+{
+ return symtable_add_def_ctx(st, name, flag, loc,
+ flag == USE ? Load : Store);
+}
+
static int
symtable_enter_type_param_block(struct symtable *st, identifier name,
void *ast, int has_defaults, int has_kwdefaults,
@@ -1757,6 +1814,9 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
VISIT_SEQ(st, type_param, s->v.ClassDef.type_params);
}
VISIT_SEQ(st, expr, s->v.ClassDef.bases);
+ if (!check_keywords(st, s->v.ClassDef.keywords)) {
+ VISIT_QUIT(st, 0);
+ }
VISIT_SEQ(st, keyword, s->v.ClassDef.keywords);
if (!symtable_enter_block(st, s->v.ClassDef.name, ClassBlock,
(void *)s, LOCATION(s))) {
@@ -1871,10 +1931,11 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
VISIT(st, expr, s->v.AnnAssign.value);
}
break;
- case AugAssign_kind:
+ case AugAssign_kind: {
VISIT(st, expr, s->v.AugAssign.target);
VISIT(st, expr, s->v.AugAssign.value);
break;
+ }
case For_kind:
VISIT(st, expr, s->v.For.target);
VISIT(st, expr, s->v.For.iter);
@@ -2311,6 +2372,9 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
case Call_kind:
VISIT(st, expr, e->v.Call.func);
VISIT_SEQ(st, expr, e->v.Call.args);
+ if (!check_keywords(st, e->v.Call.keywords)) {
+ VISIT_QUIT(st, 0);
+ }
VISIT_SEQ_WITH_NULL(st, keyword, e->v.Call.keywords);
break;
case FormattedValue_kind:
@@ -2326,6 +2390,9 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
break;
/* The following exprs can be assignment targets. */
case Attribute_kind:
+ if (!check_name(st, e->v.Attribute.attr, LOCATION(e), e->v.Attribute.ctx)) {
+ VISIT_QUIT(st, 0);
+ }
VISIT(st, expr, e->v.Attribute.value);
break;
case Subscript_kind:
@@ -2344,9 +2411,11 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
VISIT(st, expr, e->v.Slice.step);
break;
case Name_kind:
- if (!symtable_add_def(st, e->v.Name.id,
- e->v.Name.ctx == Load ? USE : DEF_LOCAL, LOCATION(e)))
+ if (!symtable_add_def_ctx(st, e->v.Name.id,
+ e->v.Name.ctx == Load ? USE : DEF_LOCAL,
+ LOCATION(e), e->v.Name.ctx)) {
VISIT_QUIT(st, 0);
+ }
/* Special-case super: it counts as a use of __class__ */
if (e->v.Name.ctx == Load &&
_PyST_IsFunctionLike(st->st_cur) &&
@@ -2472,19 +2541,26 @@ symtable_visit_pattern(struct symtable *st, pattern_ty p)
break;
case MatchStar_kind:
if (p->v.MatchStar.name) {
- symtable_add_def(st, p->v.MatchStar.name, DEF_LOCAL, LOCATION(p));
+ if (!symtable_add_def(st, p->v.MatchStar.name, DEF_LOCAL, LOCATION(p))) {
+ VISIT_QUIT(st, 0);
+ }
}
break;
case MatchMapping_kind:
VISIT_SEQ(st, expr, p->v.MatchMapping.keys);
VISIT_SEQ(st, pattern, p->v.MatchMapping.patterns);
if (p->v.MatchMapping.rest) {
- symtable_add_def(st, p->v.MatchMapping.rest, DEF_LOCAL, LOCATION(p));
+ if (!symtable_add_def(st, p->v.MatchMapping.rest, DEF_LOCAL, LOCATION(p))) {
+ VISIT_QUIT(st, 0);
+ }
}
break;
case MatchClass_kind:
VISIT(st, expr, p->v.MatchClass.cls);
VISIT_SEQ(st, pattern, p->v.MatchClass.patterns);
+ if (!check_kwd_patterns(st, p)) {
+ VISIT_QUIT(st, 0);
+ }
VISIT_SEQ(st, pattern, p->v.MatchClass.kwd_patterns);
break;
case MatchAs_kind:
@@ -2492,7 +2568,9 @@ symtable_visit_pattern(struct symtable *st, pattern_ty p)
VISIT(st, pattern, p->v.MatchAs.pattern);
}
if (p->v.MatchAs.name) {
- symtable_add_def(st, p->v.MatchAs.name, DEF_LOCAL, LOCATION(p));
+ if (!symtable_add_def(st, p->v.MatchAs.name, DEF_LOCAL, LOCATION(p))) {
+ VISIT_QUIT(st, 0);
+ }
}
break;
case MatchOr_kind:
From dcafb362f7eab84710ad924cac1724bbf3b9c304 Mon Sep 17 00:00:00 2001
From: WilliamRoyNelson
Date: Fri, 26 Jul 2024 07:34:13 -0700
Subject: [PATCH 18/47] gh-121999: Change default tarfile filter to 'data'
(GH-122002)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Tomas R
Co-authored-by: Scott Odle
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Co-authored-by: Petr Viktorin
---
Doc/library/shutil.rst | 12 ++-
Doc/library/tarfile.rst | 75 ++++++++++++-------
Lib/tarfile.py | 8 +-
Lib/test/test_shutil.py | 3 -
Lib/test/test_tarfile.py | 52 +++++--------
...-07-18-21-19-04.gh-issue-121999.8IBbTK.rst | 2 +
6 files changed, 76 insertions(+), 76 deletions(-)
create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-07-18-21-19-04.gh-issue-121999.8IBbTK.rst
diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst
index fd32479195eca8..220207e5f3cbbf 100644
--- a/Doc/library/shutil.rst
+++ b/Doc/library/shutil.rst
@@ -706,11 +706,9 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules.
The keyword-only *filter* argument is passed to the underlying unpacking
function. For zip files, *filter* is not accepted.
- For tar files, it is recommended to set it to ``'data'``,
- unless using features specific to tar and UNIX-like filesystems.
+ For tar files, it is recommended to use ``'data'`` (default since Python
+ 3.14), unless using features specific to tar and UNIX-like filesystems.
(See :ref:`tarfile-extraction-filter` for details.)
- The ``'data'`` filter will become the default for tar files
- in Python 3.14.
.. audit-event:: shutil.unpack_archive filename,extract_dir,format shutil.unpack_archive
@@ -721,6 +719,12 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules.
the *extract_dir* argument, e.g. members that have absolute filenames
starting with "/" or filenames with two dots "..".
+ Since Python 3.14, the defaults for both built-in formats (zip and tar
+ files) will prevent the most dangerous of such security issues,
+ but will not prevent *all* unintended behavior.
+ Read the :ref:`tarfile-further-verification`
+ section for tar-specific details.
+
.. versionchanged:: 3.7
Accepts a :term:`path-like object` for *filename* and *extract_dir*.
diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst
index 5b624f3533136f..631d869e42d09d 100644
--- a/Doc/library/tarfile.rst
+++ b/Doc/library/tarfile.rst
@@ -40,9 +40,12 @@ Some facts and figures:
Archives are extracted using a :ref:`filter `,
which makes it possible to either limit surprising/dangerous features,
or to acknowledge that they are expected and the archive is fully trusted.
- By default, archives are fully trusted, but this default is deprecated
- and slated to change in Python 3.14.
+.. versionchanged:: 3.14
+ Set the default extraction filter to :func:`data `,
+ which disallows some dangerous features such as links to absolute paths
+ or paths outside of the destination. Previously, the filter strategy
+ was equivalent to :func:`fully_trusted `.
.. function:: open(name=None, mode='r', fileobj=None, bufsize=10240, **kwargs)
@@ -495,18 +498,18 @@ be finalized; only the internally used file object will be closed. See the
The *filter* argument specifies how ``members`` are modified or rejected
before extraction.
See :ref:`tarfile-extraction-filter` for details.
- It is recommended to set this explicitly depending on which *tar* features
- you need to support.
+ It is recommended to set this explicitly only if specific *tar* features
+ are required, or as ``filter='data'`` to support Python versions with a less
+ secure default (3.13 and lower).
.. warning::
Never extract archives from untrusted sources without prior inspection.
- It is possible that files are created outside of *path*, e.g. members
- that have absolute filenames starting with ``"/"`` or filenames with two
- dots ``".."``.
- Set ``filter='data'`` to prevent the most dangerous security issues,
- and read the :ref:`tarfile-extraction-filter` section for details.
+ Since Python 3.14, the default (:func:`data `) will prevent
+ the most dangerous security issues.
+ However, it will not prevent *all* unintended or insecure behavior.
+ Read the :ref:`tarfile-extraction-filter` section for details.
.. versionchanged:: 3.5
Added the *numeric_owner* parameter.
@@ -517,6 +520,9 @@ be finalized; only the internally used file object will be closed. See the
.. versionchanged:: 3.12
Added the *filter* parameter.
+ .. versionchanged:: 3.14
+ The *filter* parameter now defaults to ``'data'``.
+
.. method:: TarFile.extract(member, path="", set_attrs=True, *, numeric_owner=False, filter=None)
@@ -536,10 +542,8 @@ be finalized; only the internally used file object will be closed. See the
.. warning::
- See the warning for :meth:`extractall`.
-
- Set ``filter='data'`` to prevent the most dangerous security issues,
- and read the :ref:`tarfile-extraction-filter` section for details.
+ Never extract archives from untrusted sources without prior inspection.
+ See the warning for :meth:`extractall` for details.
.. versionchanged:: 3.2
Added the *set_attrs* parameter.
@@ -602,14 +606,8 @@ be finalized; only the internally used file object will be closed. See the
String names are not allowed for this attribute, unlike the *filter*
argument to :meth:`~TarFile.extract`.
- If ``extraction_filter`` is ``None`` (the default),
- calling an extraction method without a *filter* argument will raise a
- ``DeprecationWarning``,
- and fall back to the :func:`fully_trusted ` filter,
- whose dangerous behavior matches previous versions of Python.
-
- In Python 3.14+, leaving ``extraction_filter=None`` will cause
- extraction methods to use the :func:`data ` filter by default.
+ If ``extraction_filter`` is ``None`` (the default), extraction methods
+ will use the :func:`data ` filter by default.
The attribute may be set on instances or overridden in subclasses.
It also is possible to set it on the ``TarFile`` class itself to set a
@@ -619,6 +617,14 @@ be finalized; only the internally used file object will be closed. See the
To set a global default this way, a filter function needs to be wrapped in
:func:`staticmethod()` to prevent injection of a ``self`` argument.
+ .. versionchanged:: 3.14
+
+ The default filter is set to :func:`data `,
+ which disallows some dangerous features such as links to absolute paths
+ or paths outside of the destination.
+ Previously, the default was equivalent to
+ :func:`fully_trusted `.
+
.. method:: TarFile.add(name, arcname=None, recursive=True, *, filter=None)
Add the file *name* to the archive. *name* may be any type of file
@@ -969,6 +975,12 @@ In most cases, the full functionality is not needed.
Therefore, *tarfile* supports extraction filters: a mechanism to limit
functionality, and thus mitigate some of the security issues.
+.. warning::
+
+ None of the available filters blocks *all* dangerous archive features.
+ Never extract archives from untrusted sources without prior inspection.
+ See also :ref:`tarfile-further-verification`.
+
.. seealso::
:pep:`706`
@@ -992,12 +1004,13 @@ can be:
* ``None`` (default): Use :attr:`TarFile.extraction_filter`.
- If that is also ``None`` (the default), raise a ``DeprecationWarning``,
- and fall back to the ``'fully_trusted'`` filter, whose dangerous behavior
- matches previous versions of Python.
+ If that is also ``None`` (the default), the ``'data'`` filter will be used.
+
+ .. versionchanged:: 3.14
- In Python 3.14, the ``'data'`` filter will become the default instead.
- It's possible to switch earlier; see :attr:`TarFile.extraction_filter`.
+ The default filter is set to :func:`data `.
+ Previously, the default was equivalent to
+ :func:`fully_trusted `.
* A callable which will be called for each extracted member with a
:ref:`TarInfo ` describing the member and the destination
@@ -1080,6 +1093,9 @@ reused in custom filters:
Return the modified ``TarInfo`` member.
+ Note that this filter does not block *all* dangerous archive features.
+ See :ref:`tarfile-further-verification` for details.
+
.. _tarfile-extraction-refuse:
@@ -1093,6 +1109,8 @@ With ``errorlevel=0`` the error will be logged and the member will be skipped,
but extraction will continue.
+.. _tarfile-further-verification:
+
Hints for further verification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1110,9 +1128,10 @@ Here is an incomplete list of things to consider:
disk, memory and CPU usage.
* Check filenames against an allow-list of characters
(to filter out control characters, confusables, foreign path separators,
- etc.).
+ and so on).
* Check that filenames have expected extensions (discouraging files that
- execute when you “click on them”, or extension-less files like Windows special device names).
+ execute when you “click on them”, or extension-less files like Windows
+ special device names).
* Limit the number of extracted files, total size of extracted data,
filename length (including symlink length), and size of individual files.
* Check for files that would be shadowed on case-insensitive filesystems.
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index d5d8a469779f50..4fa7bb6740adbb 100644
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -2248,13 +2248,7 @@ def _get_filter_function(self, filter):
if filter is None:
filter = self.extraction_filter
if filter is None:
- import warnings
- warnings.warn(
- 'Python 3.14 will, by default, filter extracted tar '
- + 'archives and reject files or modify their metadata. '
- + 'Use the filter argument to control this behavior.',
- DeprecationWarning, stacklevel=3)
- return fully_trusted_filter
+ return data_filter
if isinstance(filter, str):
raise TypeError(
'String names are not supported for '
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
index c458c5df32572b..c770be21b41c2b 100644
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -2145,9 +2145,6 @@ def check_unpack_archive_with_converter(self, format, converter, **kwargs):
def check_unpack_tarball(self, format):
self.check_unpack_archive(format, filter='fully_trusted')
self.check_unpack_archive(format, filter='data')
- with warnings_helper.check_warnings(
- ('Python 3.14', DeprecationWarning)):
- self.check_unpack_archive(format)
def test_unpack_archive_tar(self):
self.check_unpack_tarball('tar')
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index f715940de1d584..5600f0746770b8 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -722,6 +722,24 @@ def format_mtime(mtime):
tar.close()
os_helper.rmtree(DIR)
+ @staticmethod
+ def test_extractall_default_filter():
+ # Test that the default filter is now "data", and the other filter types are not used.
+ DIR = pathlib.Path(TEMPDIR) / "extractall_default_filter"
+ with (
+ os_helper.temp_dir(DIR),
+ tarfile.open(tarname, encoding="iso8859-1") as tar,
+ unittest.mock.patch("tarfile.data_filter", wraps=tarfile.data_filter) as mock_data_filter,
+ unittest.mock.patch("tarfile.tar_filter", wraps=tarfile.tar_filter) as mock_tar_filter,
+ unittest.mock.patch("tarfile.fully_trusted_filter", wraps=tarfile.fully_trusted_filter) as mock_ft_filter
+ ):
+ directories = [t for t in tar if t.isdir()]
+ tar.extractall(DIR, directories)
+
+ mock_data_filter.assert_called()
+ mock_ft_filter.assert_not_called()
+ mock_tar_filter.assert_not_called()
+
@os_helper.skip_unless_working_chmod
def test_extract_directory(self):
dirtype = "ustar/dirtype"
@@ -738,31 +756,6 @@ def test_extract_directory(self):
finally:
os_helper.rmtree(DIR)
- def test_deprecation_if_no_filter_passed_to_extractall(self):
- DIR = pathlib.Path(TEMPDIR) / "extractall"
- with (
- os_helper.temp_dir(DIR),
- tarfile.open(tarname, encoding="iso8859-1") as tar
- ):
- directories = [t for t in tar if t.isdir()]
- with self.assertWarnsRegex(DeprecationWarning, "Use the filter argument") as cm:
- tar.extractall(DIR, directories)
- # check that the stacklevel of the deprecation warning is correct:
- self.assertEqual(cm.filename, __file__)
-
- def test_deprecation_if_no_filter_passed_to_extract(self):
- dirtype = "ustar/dirtype"
- DIR = pathlib.Path(TEMPDIR) / "extractall"
- with (
- os_helper.temp_dir(DIR),
- tarfile.open(tarname, encoding="iso8859-1") as tar
- ):
- tarinfo = tar.getmember(dirtype)
- with self.assertWarnsRegex(DeprecationWarning, "Use the filter argument") as cm:
- tar.extract(tarinfo, path=DIR)
- # check that the stacklevel of the deprecation warning is correct:
- self.assertEqual(cm.filename, __file__)
-
def test_extractall_pathlike_dir(self):
DIR = os.path.join(TEMPDIR, "extractall")
with os_helper.temp_dir(DIR), \
@@ -4011,15 +4004,6 @@ def test_data_filter(self):
self.assertIs(filtered.name, tarinfo.name)
self.assertIs(filtered.type, tarinfo.type)
- def test_default_filter_warns(self):
- """Ensure the default filter warns"""
- with ArchiveMaker() as arc:
- arc.add('foo')
- with warnings_helper.check_warnings(
- ('Python 3.14', DeprecationWarning)):
- with self.check_context(arc.open(), None):
- self.expect_file('foo')
-
def test_change_default_filter_on_instance(self):
tar = tarfile.TarFile(tarname, 'r')
def strict_filter(tarinfo, path):
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-07-18-21-19-04.gh-issue-121999.8IBbTK.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-07-18-21-19-04.gh-issue-121999.8IBbTK.rst
new file mode 100644
index 00000000000000..e65aa993566446
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-07-18-21-19-04.gh-issue-121999.8IBbTK.rst
@@ -0,0 +1,2 @@
+The default extraction filter for the :mod:`tarfile` module is now
+set to :func:`'data' `.
From 7c2921844f9fa713f93152bf3a569812cee347a0 Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka
Date: Fri, 26 Jul 2024 17:48:44 +0300
Subject: [PATCH 19/47] gh-122311: Fix typo in the pickle error formatting code
(GH-122312)
---
Lib/pickle.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Lib/pickle.py b/Lib/pickle.py
index 2d764980cdf7b2..c6c151b2065f4d 100644
--- a/Lib/pickle.py
+++ b/Lib/pickle.py
@@ -1153,7 +1153,7 @@ def _save_toplevel_by_name(self, module_name, name):
except UnicodeEncodeError:
raise PicklingError(
"can't pickle global identifier '%s.%s' using "
- "pickle protocol %i" % (module, name, self.proto)) from None
+ "pickle protocol %i" % (module_name, name, self.proto)) from None
def save_type(self, obj):
if obj is type(None):
From db2d8b6db1b56c2bd3802b86f9b76da33e8898d7 Mon Sep 17 00:00:00 2001
From: Pablo Galindo Salgado
Date: Fri, 26 Jul 2024 17:29:41 +0100
Subject: [PATCH 20/47] gh-122300: Preserve AST nodes for format specifiers
with single elements (#122308)
---
Doc/library/ast.rst | 4 +++-
Lib/test/test_ast.py | 2 +-
.../2024-07-26-14-05-51.gh-issue-122300.SVIF-l.rst | 2 ++
Parser/action_helpers.c | 3 ++-
4 files changed, 8 insertions(+), 3 deletions(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-07-26-14-05-51.gh-issue-122300.SVIF-l.rst
diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst
index d05ad1e2a7854f..dd5dd5ca4e9e32 100644
--- a/Doc/library/ast.rst
+++ b/Doc/library/ast.rst
@@ -316,7 +316,9 @@ Literals
args=[
Name(id='a', ctx=Load())]),
conversion=-1,
- format_spec=Constant(value='.3'))]))
+ format_spec=JoinedStr(
+ values=[
+ Constant(value='.3')]))]))
.. class:: List(elts, ctx)
diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py
index 5144187d7c3ddd..55725ec36fd3a7 100644
--- a/Lib/test/test_ast.py
+++ b/Lib/test/test_ast.py
@@ -3638,7 +3638,7 @@ def main():
('Expression', ('Subscript', (1, 0, 1, 10), ('List', (1, 0, 1, 3), [('Constant', (1, 1, 1, 2), 5, None)], ('Load',)), ('Slice', (1, 4, 1, 9), ('Constant', (1, 4, 1, 5), 1, None), ('Constant', (1, 6, 1, 7), 1, None), ('Constant', (1, 8, 1, 9), 1, None)), ('Load',))),
('Expression', ('IfExp', (1, 0, 1, 21), ('Name', (1, 9, 1, 10), 'x', ('Load',)), ('Call', (1, 0, 1, 5), ('Name', (1, 0, 1, 3), 'foo', ('Load',)), [], []), ('Call', (1, 16, 1, 21), ('Name', (1, 16, 1, 19), 'bar', ('Load',)), [], []))),
('Expression', ('JoinedStr', (1, 0, 1, 6), [('FormattedValue', (1, 2, 1, 5), ('Name', (1, 3, 1, 4), 'a', ('Load',)), -1, None)])),
-('Expression', ('JoinedStr', (1, 0, 1, 10), [('FormattedValue', (1, 2, 1, 9), ('Name', (1, 3, 1, 4), 'a', ('Load',)), -1, ('Constant', (1, 5, 1, 8), '.2f', None))])),
+('Expression', ('JoinedStr', (1, 0, 1, 10), [('FormattedValue', (1, 2, 1, 9), ('Name', (1, 3, 1, 4), 'a', ('Load',)), -1, ('JoinedStr', (1, 4, 1, 8), [('Constant', (1, 5, 1, 8), '.2f', None)]))])),
('Expression', ('JoinedStr', (1, 0, 1, 8), [('FormattedValue', (1, 2, 1, 7), ('Name', (1, 3, 1, 4), 'a', ('Load',)), 114, None)])),
('Expression', ('JoinedStr', (1, 0, 1, 11), [('Constant', (1, 2, 1, 6), 'foo(', None), ('FormattedValue', (1, 6, 1, 9), ('Name', (1, 7, 1, 8), 'a', ('Load',)), -1, None), ('Constant', (1, 9, 1, 10), ')', None)])),
]
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-07-26-14-05-51.gh-issue-122300.SVIF-l.rst b/Misc/NEWS.d/next/Core and Builtins/2024-07-26-14-05-51.gh-issue-122300.SVIF-l.rst
new file mode 100644
index 00000000000000..6b58f89247d1d4
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2024-07-26-14-05-51.gh-issue-122300.SVIF-l.rst
@@ -0,0 +1,2 @@
+Preserve AST nodes for f-string with single-element format specifiers. Patch
+by Pablo Galindo
diff --git a/Parser/action_helpers.c b/Parser/action_helpers.c
index db6f872c7224d1..1972c606827cdb 100644
--- a/Parser/action_helpers.c
+++ b/Parser/action_helpers.c
@@ -1010,7 +1010,8 @@ _PyPegen_setup_full_format_spec(Parser *p, Token *colon, asdl_expr_seq *spec, in
spec = resized_spec;
}
expr_ty res;
- if (asdl_seq_LEN(spec) == 0) {
+ Py_ssize_t n = asdl_seq_LEN(spec);
+ if (n == 0 || (n == 1 && asdl_seq_GET(spec, 0)->kind == Constant_kind)) {
res = _PyAST_JoinedStr(spec, lineno, col_offset, end_lineno,
end_col_offset, p->arena);
} else {
From 64857d849f3079a73367525ce93fd7a463b83908 Mon Sep 17 00:00:00 2001
From: Brandt Bucher
Date: Fri, 26 Jul 2024 09:40:15 -0700
Subject: [PATCH 21/47] GH-122294: Burn in the addresses of side exits
(GH-122295)
---
Include/internal/pycore_uop_metadata.h | 4 ++--
Python/bytecodes.c | 12 ++++++------
Python/executor_cases.c.h | 12 ++++++------
Python/optimizer.c | 10 ++++++----
Python/optimizer_bytecodes.c | 3 ++-
Python/optimizer_cases.c.h | 2 ++
6 files changed, 24 insertions(+), 19 deletions(-)
diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h
index fd0d4a67d93538..d23a4e2ea14345 100644
--- a/Include/internal/pycore_uop_metadata.h
+++ b/Include/internal/pycore_uop_metadata.h
@@ -252,7 +252,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
[_SET_IP] = 0,
[_CHECK_STACK_SPACE_OPERAND] = HAS_DEOPT_FLAG,
[_SAVE_RETURN_OFFSET] = HAS_ARG_FLAG,
- [_EXIT_TRACE] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG,
+ [_EXIT_TRACE] = HAS_ESCAPES_FLAG,
[_CHECK_VALIDITY] = HAS_DEOPT_FLAG,
[_LOAD_CONST_INLINE] = HAS_PURE_FLAG,
[_LOAD_CONST_INLINE_BORROW] = HAS_PURE_FLAG,
@@ -261,7 +261,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
[_LOAD_CONST_INLINE_BORROW_WITH_NULL] = HAS_PURE_FLAG,
[_CHECK_FUNCTION] = HAS_DEOPT_FLAG,
[_INTERNAL_INCREMENT_OPT_COUNTER] = 0,
- [_DYNAMIC_EXIT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG,
+ [_DYNAMIC_EXIT] = HAS_ESCAPES_FLAG,
[_START_EXECUTOR] = 0,
[_FATAL_ERROR] = 0,
[_CHECK_VALIDITY_AND_SET_IP] = HAS_DEOPT_FLAG,
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 871e2dbf358418..d74f2aae0483ce 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -4609,8 +4609,8 @@ dummy_func(
#endif
}
- tier2 op(_EXIT_TRACE, (--)) {
- _PyExitData *exit = ¤t_executor->exits[oparg];
+ tier2 op(_EXIT_TRACE, (exit_p/4 --)) {
+ _PyExitData *exit = (_PyExitData *)exit_p;
PyCodeObject *code = _PyFrame_GetCode(frame);
_Py_CODEUNIT *target = _PyCode_CODE(code) + exit->target;
#if defined(Py_DEBUG) && !defined(_Py_JIT)
@@ -4619,7 +4619,7 @@ dummy_func(
printf("SIDE EXIT: [UOp ");
_PyUOpPrint(&next_uop[-1]);
printf(", exit %u, temp %d, target %d -> %s]\n",
- oparg, exit->temperature.as_counter,
+ exit - current_executor->exits, exit->temperature.as_counter,
(int)(target - _PyCode_CODE(code)),
_PyOpcode_OpName[target->op.code]);
}
@@ -4698,9 +4698,9 @@ dummy_func(
exe->count++;
}
- tier2 op(_DYNAMIC_EXIT, (--)) {
+ tier2 op(_DYNAMIC_EXIT, (exit_p/4 --)) {
tstate->previous_executor = (PyObject *)current_executor;
- _PyExitData *exit = (_PyExitData *)¤t_executor->exits[oparg];
+ _PyExitData *exit = (_PyExitData *)exit_p;
_Py_CODEUNIT *target = frame->instr_ptr;
#if defined(Py_DEBUG) && !defined(_Py_JIT)
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist);
@@ -4708,7 +4708,7 @@ dummy_func(
printf("DYNAMIC EXIT: [UOp ");
_PyUOpPrint(&next_uop[-1]);
printf(", exit %u, temp %d, target %d -> %s]\n",
- oparg, exit->temperature.as_counter,
+ exit - current_executor->exits, exit->temperature.as_counter,
(int)(target - _PyCode_CODE(_PyFrame_GetCode(frame))),
_PyOpcode_OpName[target->op.code]);
}
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index 1ced8b951b5ce9..6e3f6cc62fe11f 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -5044,8 +5044,8 @@
}
case _EXIT_TRACE: {
- oparg = CURRENT_OPARG();
- _PyExitData *exit = ¤t_executor->exits[oparg];
+ PyObject *exit_p = (PyObject *)CURRENT_OPERAND();
+ _PyExitData *exit = (_PyExitData *)exit_p;
PyCodeObject *code = _PyFrame_GetCode(frame);
_Py_CODEUNIT *target = _PyCode_CODE(code) + exit->target;
#if defined(Py_DEBUG) && !defined(_Py_JIT)
@@ -5054,7 +5054,7 @@
printf("SIDE EXIT: [UOp ");
_PyUOpPrint(&next_uop[-1]);
printf(", exit %u, temp %d, target %d -> %s]\n",
- oparg, exit->temperature.as_counter,
+ exit - current_executor->exits, exit->temperature.as_counter,
(int)(target - _PyCode_CODE(code)),
_PyOpcode_OpName[target->op.code]);
}
@@ -5182,9 +5182,9 @@
}
case _DYNAMIC_EXIT: {
- oparg = CURRENT_OPARG();
+ PyObject *exit_p = (PyObject *)CURRENT_OPERAND();
tstate->previous_executor = (PyObject *)current_executor;
- _PyExitData *exit = (_PyExitData *)¤t_executor->exits[oparg];
+ _PyExitData *exit = (_PyExitData *)exit_p;
_Py_CODEUNIT *target = frame->instr_ptr;
#if defined(Py_DEBUG) && !defined(_Py_JIT)
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist);
@@ -5192,7 +5192,7 @@
printf("DYNAMIC EXIT: [UOp ");
_PyUOpPrint(&next_uop[-1]);
printf(", exit %u, temp %d, target %d -> %s]\n",
- oparg, exit->temperature.as_counter,
+ exit - current_executor->exits, exit->temperature.as_counter,
(int)(target - _PyCode_CODE(_PyFrame_GetCode(frame))),
_PyOpcode_OpName[target->op.code]);
}
diff --git a/Python/optimizer.c b/Python/optimizer.c
index f0793b8c8f2088..7b875af2aae898 100644
--- a/Python/optimizer.c
+++ b/Python/optimizer.c
@@ -1153,13 +1153,15 @@ make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFil
*dest = buffer[i];
assert(opcode != _POP_JUMP_IF_FALSE && opcode != _POP_JUMP_IF_TRUE);
if (opcode == _EXIT_TRACE) {
- executor->exits[next_exit].target = buffer[i].target;
- dest->oparg = next_exit;
+ _PyExitData *exit = &executor->exits[next_exit];
+ exit->target = buffer[i].target;
+ dest->operand = (uint64_t)exit;
next_exit--;
}
if (opcode == _DYNAMIC_EXIT) {
- executor->exits[next_exit].target = 0;
- dest->oparg = next_exit;
+ _PyExitData *exit = &executor->exits[next_exit];
+ exit->target = 0;
+ dest->operand = (uint64_t)exit;
next_exit--;
}
}
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index a506f9948fd9ae..4d4f89301c7475 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -788,7 +788,8 @@ dummy_func(void) {
ctx->done = true;
}
- op(_EXIT_TRACE, (--)) {
+ op(_EXIT_TRACE, (exit_p/4 --)) {
+ (void)exit_p;
ctx->done = true;
}
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 166b1674bc3334..fae93ce89e82e5 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -2163,6 +2163,8 @@
}
case _EXIT_TRACE: {
+ PyObject *exit_p = (PyObject *)this_instr->operand;
+ (void)exit_p;
ctx->done = true;
break;
}
From c557ae97d6bd9d04164a19b4fe136610e54dbdd8 Mon Sep 17 00:00:00 2001
From: Sam Gross
Date: Fri, 26 Jul 2024 13:06:07 -0400
Subject: [PATCH 22/47] gh-122201: Lock mutex when setting handling_thread to
NULL (#122204)
In the free-threaded build, we need to lock pending->mutex when clearing
the handling_thread in order not to race with a concurrent
make_pending_calls in the same interpreter.
---
Python/ceval_gil.c | 18 +++++++++++++++---
Tools/tsan/suppressions_free_threading.txt | 1 -
2 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c
index dc3baf79ccba62..0b45caba0d49ff 100644
--- a/Python/ceval_gil.c
+++ b/Python/ceval_gil.c
@@ -901,6 +901,18 @@ unsignal_pending_calls(PyThreadState *tstate, PyInterpreterState *interp)
#endif
}
+static void
+clear_pending_handling_thread(struct _pending_calls *pending)
+{
+#ifdef Py_GIL_DISABLED
+ PyMutex_Lock(&pending->mutex);
+ pending->handling_thread = NULL;
+ PyMutex_Unlock(&pending->mutex);
+#else
+ pending->handling_thread = NULL;
+#endif
+}
+
static int
make_pending_calls(PyThreadState *tstate)
{
@@ -933,7 +945,7 @@ make_pending_calls(PyThreadState *tstate)
int32_t npending;
if (_make_pending_calls(pending, &npending) != 0) {
- pending->handling_thread = NULL;
+ clear_pending_handling_thread(pending);
/* There might not be more calls to make, but we play it safe. */
signal_pending_calls(tstate, interp);
return -1;
@@ -945,7 +957,7 @@ make_pending_calls(PyThreadState *tstate)
if (_Py_IsMainThread() && _Py_IsMainInterpreter(interp)) {
if (_make_pending_calls(pending_main, &npending) != 0) {
- pending->handling_thread = NULL;
+ clear_pending_handling_thread(pending);
/* There might not be more calls to make, but we play it safe. */
signal_pending_calls(tstate, interp);
return -1;
@@ -956,7 +968,7 @@ make_pending_calls(PyThreadState *tstate)
}
}
- pending->handling_thread = NULL;
+ clear_pending_handling_thread(pending);
return 0;
}
diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt
index 0955387dfb8370..a54e66d1212d1f 100644
--- a/Tools/tsan/suppressions_free_threading.txt
+++ b/Tools/tsan/suppressions_free_threading.txt
@@ -28,7 +28,6 @@ race_top:assign_version_tag
race_top:new_reference
race_top:_multiprocessing_SemLock_acquire_impl
race_top:list_get_item_ref
-race_top:make_pending_calls
race_top:_Py_slot_tp_getattr_hook
race_top:add_threadstate
race_top:dump_traceback
From 1ca99ed240e1e70502d84fea274423b660d172c2 Mon Sep 17 00:00:00 2001
From: Mark Shannon
Date: Fri, 26 Jul 2024 18:38:52 +0100
Subject: [PATCH 23/47] Manually override bytecode definition in optimizer, to
avoid build error (GH-122316)
---
Python/optimizer_bytecodes.c | 8 ++++++++
Python/optimizer_cases.c.h | 13 +++++++++----
2 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index 4d4f89301c7475..c982e37182157a 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -596,6 +596,14 @@ dummy_func(void) {
}
}
+ op(_MAYBE_EXPAND_METHOD, (callable, self_or_null, args[oparg] -- func, maybe_self, args[oparg])) {
+ (void)callable;
+ (void)self_or_null;
+ (void)args;
+ func = sym_new_not_null(ctx);
+ maybe_self = sym_new_not_null(ctx);
+ }
+
op(_PY_FRAME_GENERAL, (callable, self_or_null, args[oparg] -- new_frame: _Py_UOpsAbstractFrame *)) {
/* The _Py_UOpsAbstractFrame design assumes that we can copy arguments across directly */
(void)callable;
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index fae93ce89e82e5..4fa40ff861ba70 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -1599,14 +1599,19 @@
}
case _MAYBE_EXPAND_METHOD: {
+ _Py_UopsSymbol **args;
+ _Py_UopsSymbol *self_or_null;
+ _Py_UopsSymbol *callable;
_Py_UopsSymbol *func;
_Py_UopsSymbol *maybe_self;
- _Py_UopsSymbol **args;
+ args = &stack_pointer[-oparg];
+ self_or_null = stack_pointer[-1 - oparg];
+ callable = stack_pointer[-2 - oparg];
+ (void)callable;
+ (void)self_or_null;
+ (void)args;
func = sym_new_not_null(ctx);
maybe_self = sym_new_not_null(ctx);
- for (int _i = oparg; --_i >= 0;) {
- args[_i] = sym_new_not_null(ctx);
- }
stack_pointer[-2 - oparg] = func;
stack_pointer[-1 - oparg] = maybe_self;
break;
From d791b9815a64c99991fcfd2f8408fc0b7ddb00bd Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Fri, 26 Jul 2024 19:40:36 +0100
Subject: [PATCH 24/47] gh-122245: Add test case of generic type with __debug__
(#122322)
---
Lib/test/test_syntax.py | 4 ++++
.../2024-07-24-22-39-07.gh-issue-122245.LVa9v8.rst | 4 ++--
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py
index 4421d03a6d2206..206b7f0088a925 100644
--- a/Lib/test/test_syntax.py
+++ b/Lib/test/test_syntax.py
@@ -2265,6 +2265,10 @@ def f(x: *b)
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__
+ >>> class A[__debug__]: pass
+ Traceback (most recent call last):
+ SyntaxError: cannot assign to __debug__
+
>>> class A[T]((x := 3)): ...
Traceback (most recent call last):
...
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-07-24-22-39-07.gh-issue-122245.LVa9v8.rst b/Misc/NEWS.d/next/Core and Builtins/2024-07-24-22-39-07.gh-issue-122245.LVa9v8.rst
index 453c45e2f7ae3f..fff99b4992e321 100644
--- a/Misc/NEWS.d/next/Core and Builtins/2024-07-24-22-39-07.gh-issue-122245.LVa9v8.rst
+++ b/Misc/NEWS.d/next/Core and Builtins/2024-07-24-22-39-07.gh-issue-122245.LVa9v8.rst
@@ -1,4 +1,4 @@
Detection of writes to ``__debug__`` is moved from the compiler's codegen
-stage to the symtable. This means that these errors now detected even in
+stage to the symtable. This means that these errors are now detected even in
code that is optimized away before codegen (such as assertions with the
-:option:`-O` command line option.)
+:option:`-O` command line option).
From 33586d64ca911b472de2550cf4f5b524cef65921 Mon Sep 17 00:00:00 2001
From: Carol Willing
Date: Fri, 26 Jul 2024 13:56:39 -0700
Subject: [PATCH 25/47] Remove reference to docs mailing list for bug reports
(#122323)
---
Doc/bugs.rst | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/Doc/bugs.rst b/Doc/bugs.rst
index 9aff2f0ff5187d..5d0f68ca69675e 100644
--- a/Doc/bugs.rst
+++ b/Doc/bugs.rst
@@ -16,21 +16,16 @@ Documentation bugs
==================
If you find a bug in this documentation or would like to propose an improvement,
-please submit a bug report on the :ref:`tracker `. If you
+please submit a bug report on the :ref:`issue tracker `. If you
have a suggestion on how to fix it, include that as well.
You can also open a discussion item on our
`Documentation Discourse forum `_.
If you find a bug in the theme (HTML / CSS / JavaScript) of the
-documentation, please submit a bug report on the `python-doc-theme bug
+documentation, please submit a bug report on the `python-doc-theme issue
tracker `_.
-If you're short on time, you can also email documentation bug reports to
-docs@python.org (behavioral bugs can be sent to python-list@python.org).
-'docs@' is a mailing list run by volunteers; your request will be noticed,
-though it may take a while to be processed.
-
.. seealso::
`Documentation bugs`_
From d52726ccd456833ea9f09cabb4b8aef09755e472 Mon Sep 17 00:00:00 2001
From: Subrahmanya Gaonkar <148525245+negativenagesh@users.noreply.github.com>
Date: Sat, 27 Jul 2024 03:33:08 +0530
Subject: [PATCH 26/47] Document ``mimetypes.MimeTypes.add_type()`` (#122301)
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
---
Doc/library/mimetypes.rst | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/Doc/library/mimetypes.rst b/Doc/library/mimetypes.rst
index 91e8c30f8607b3..8ad4850584a7e1 100644
--- a/Doc/library/mimetypes.rst
+++ b/Doc/library/mimetypes.rst
@@ -295,3 +295,13 @@ than one MIME-type database; it provides an interface similar to the one of the
types, else to the list of non-standard types.
.. versionadded:: 3.2
+
+
+ .. method:: MimeTypes.add_type(type, ext, strict=True)
+
+ Add a mapping from the MIME type *type* to the extension *ext*. When the
+ extension is already known, the new type will replace the old one. When the type
+ is already known the extension will be added to the list of known extensions.
+
+ When *strict* is ``True`` (the default), the mapping will be added to the
+ official MIME types, otherwise to the non-standard ones.
From 762e771cc01e1485e8040ea046fcc55a4a420d42 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
Date: Sat, 27 Jul 2024 02:00:56 +0100
Subject: [PATCH 27/47] Fix underline for 'pty' in What's New in Python 3.14
(#122337)
---
Doc/whatsnew/3.14.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index d2ba7ada76733a..cc03088592d9d4 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -309,7 +309,7 @@ pathlib
arguments are joined onto *other*.
pty
-___
+---
* Remove deprecated :func:`!pty.master_open` and :func:`!pty.slave_open`.
They had previously raised a :exc:`DeprecationWarning` since Python 3.12.
From 4a2607c1807982a107445b5a35240f587a61eb0d Mon Sep 17 00:00:00 2001
From: Russell Keith-Magee
Date: Sat, 27 Jul 2024 11:53:44 +1000
Subject: [PATCH 28/47] gh-120831: Correct default minimum iOS version.
(#122339)
Correct default minimum iOS version.
---
Lib/sysconfig/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py
index 83e057c177f8c0..80aef3447117e5 100644
--- a/Lib/sysconfig/__init__.py
+++ b/Lib/sysconfig/__init__.py
@@ -642,7 +642,7 @@ def get_platform():
release = m.group()
elif osname[:6] == "darwin":
if sys.platform == "ios":
- release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "12.0")
+ release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0")
osname = sys.platform
machine = sys.implementation._multiarch
else:
From 863a92f2bc708b9e3dfa9828bb8155b8d371e09c Mon Sep 17 00:00:00 2001
From: Russell Keith-Magee
Date: Sat, 27 Jul 2024 12:24:30 +1000
Subject: [PATCH 29/47] gh-121832: Revert test skip introduced by #122150.
(#122340)
Revert test skip introduced by #122150.
---
Lib/test/test_types.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py
index a87bb275d296a0..a84f43ba9b51fd 100644
--- a/Lib/test/test_types.py
+++ b/Lib/test/test_types.py
@@ -2382,7 +2382,6 @@ def setUpClass(cls):
@cpython_only
@no_rerun('channels (and queues) might have a refleak; see gh-122199')
- @unittest.skipIf(is_apple_mobile, "Fails on iOS due to test ordering; see #121832.")
def test_slot_wrappers(self):
rch, sch = interpreters.channels.create()
From c08696286f52d286674f264eecf7b33a335a890b Mon Sep 17 00:00:00 2001
From: Peter Bierma
Date: Sat, 27 Jul 2024 02:27:48 -0400
Subject: [PATCH 30/47] gh-122332: Fix missing `NULL` check in
`asyncio.Task.get_coro` (#122338)
---
Lib/test/test_asyncio/test_eager_task_factory.py | 12 ++++++++++++
.../2024-07-26-21-21-13.gh-issue-122332.fvw88r.rst | 2 ++
Modules/_asynciomodule.c | 6 +++++-
3 files changed, 19 insertions(+), 1 deletion(-)
create mode 100644 Misc/NEWS.d/next/Library/2024-07-26-21-21-13.gh-issue-122332.fvw88r.rst
diff --git a/Lib/test/test_asyncio/test_eager_task_factory.py b/Lib/test/test_asyncio/test_eager_task_factory.py
index 0f8212dbec47be..0777f39b572486 100644
--- a/Lib/test/test_asyncio/test_eager_task_factory.py
+++ b/Lib/test/test_asyncio/test_eager_task_factory.py
@@ -241,6 +241,18 @@ class DummyLoop:
_, out, err = assert_python_ok("-c", code)
self.assertFalse(err)
+ def test_issue122332(self):
+ async def coro():
+ pass
+
+ async def run():
+ task = self.loop.create_task(coro())
+ await task
+ self.assertIsNone(task.get_coro())
+
+ self.run_coro(run())
+
+
class AsyncTaskCounter:
def __init__(self, loop, *, task_class, eager):
self.suspense_count = 0
diff --git a/Misc/NEWS.d/next/Library/2024-07-26-21-21-13.gh-issue-122332.fvw88r.rst b/Misc/NEWS.d/next/Library/2024-07-26-21-21-13.gh-issue-122332.fvw88r.rst
new file mode 100644
index 00000000000000..55bb1dc44add1b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-07-26-21-21-13.gh-issue-122332.fvw88r.rst
@@ -0,0 +1,2 @@
+Fixed segfault with :meth:`asyncio.Task.get_coro` when using an eager task
+factory.
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index 1a223f9bd0cbae..873c17cd78709d 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -2500,7 +2500,11 @@ static PyObject *
_asyncio_Task_get_coro_impl(TaskObj *self)
/*[clinic end generated code: output=bcac27c8cc6c8073 input=d2e8606c42a7b403]*/
{
- return Py_NewRef(self->task_coro);
+ if (self->task_coro) {
+ return Py_NewRef(self->task_coro);
+ }
+
+ Py_RETURN_NONE;
}
/*[clinic input]
From bb09ba679223666e01f8da780f97888a29d07131 Mon Sep 17 00:00:00 2001
From: Petr Viktorin
Date: Sat, 27 Jul 2024 10:27:06 +0200
Subject: [PATCH 31/47] gh-122291: Intern latin-1 one-byte strings at startup
(GH-122303)
---
InternalDocs/string_interning.md | 66 +++++++++++++++-----------------
Objects/unicodeobject.c | 36 +++++------------
2 files changed, 40 insertions(+), 62 deletions(-)
diff --git a/InternalDocs/string_interning.md b/InternalDocs/string_interning.md
index 930ea110d857ac..358e2c070cd5fa 100644
--- a/InternalDocs/string_interning.md
+++ b/InternalDocs/string_interning.md
@@ -8,51 +8,50 @@
This is used to optimize dict and attribute lookups, among other things.
-Python uses three different mechanisms to intern strings:
+Python uses two different mechanisms to intern strings: singletons and
+dynamic interning.
-- Singleton strings marked in C source with `_Py_STR` and `_Py_ID` macros.
- These are statically allocated, and collected using `make regen-global-objects`
- (`Tools/build/generate_global_objects.py`), which generates code
- for declaration, initialization and finalization.
+## Singletons
- The difference between the two kinds is not important. (A `_Py_ID` string is
- a valid C name, with which we can refer to it; a `_Py_STR` may e.g. contain
- non-identifier characters, so it needs a separate C-compatible name.)
+The 256 possible one-character latin-1 strings, which can be retrieved with
+`_Py_LATIN1_CHR(c)`, are stored in statically allocated arrays,
+`_PyRuntime.static_objects.strings.ascii` and
+`_PyRuntime.static_objects.strings.latin1`.
- The empty string is in this category (as `_Py_STR(empty)`).
+Longer singleton strings are marked in C source with `_Py_ID` (if the string
+is a valid C identifier fragment) or `_Py_STR` (if it needs a separate
+C-compatible name.)
+These are also stored in statically allocated arrays.
+They are collected from CPython sources using `make regen-global-objects`
+(`Tools/build/generate_global_objects.py`), which generates code
+for declaration, initialization and finalization.
- These singletons are interned in a runtime-global lookup table,
- `_PyRuntime.cached_objects.interned_strings` (`INTERNED_STRINGS`),
- at runtime initialization.
+The empty string is one of the singletons: `_Py_STR(empty)`.
-- The 256 possible one-character latin-1 strings are singletons,
- which can be retrieved with `_Py_LATIN1_CHR(c)`, are stored in runtime-global
- arrays, `_PyRuntime.static_objects.strings.ascii` and
- `_PyRuntime.static_objects.strings.latin1`.
+The three sets of singletons (`_Py_LATIN1_CHR`, `_Py_ID`, `_Py_STR`)
+are disjoint.
+If you have such a singleton, it (and no other copy) will be interned.
- These are NOT interned at startup in the normal build.
- In the free-threaded build, they are; this avoids modifying the
- global lookup table after threads are started.
+These singletons are interned in a runtime-global lookup table,
+`_PyRuntime.cached_objects.interned_strings` (`INTERNED_STRINGS`),
+at runtime initialization, and immutable until it's torn down
+at runtime finalization.
+It is shared across threads and interpreters without any synchronization.
- Interning a one-char latin-1 string will always intern the corresponding
- singleton.
-- All other strings are allocated dynamically, and have their
- `_PyUnicode_STATE(s).statically_allocated` flag set to zero.
- When interned, such strings are added to an interpreter-wide dict,
- `PyInterpreterState.cached_objects.interned_strings`.
+## Dynamically allocated strings
- The key and value of each entry in this dict reference the same object.
+All other strings are allocated dynamically, and have their
+`_PyUnicode_STATE(s).statically_allocated` flag set to zero.
+When interned, such strings are added to an interpreter-wide dict,
+`PyInterpreterState.cached_objects.interned_strings`.
-The three sets of singletons (`_Py_STR`, `_Py_ID`, `_Py_LATIN1_CHR`)
-are disjoint.
-If you have such a singleton, it (and no other copy) will be interned.
+The key and value of each entry in this dict reference the same object.
## Immortality and reference counting
-Invariant: Every immortal string is interned, *except* the one-char latin-1
-singletons (which might but might not be interned).
+Invariant: Every immortal string is interned.
In practice, this means that you must not use `_Py_SetImmortal` on
a string. (If you know it's already immortal, don't immortalize it;
@@ -115,8 +114,5 @@ The valid transitions between these states are:
Using `_PyUnicode_InternStatic` on these is an error; the other cases
don't change the state.
-- One-char latin-1 singletons can be interned (0 -> 3) using any interning
- function; after that the functions don't change the state.
-
-- Other statically allocated strings are interned (0 -> 3) at runtime init;
+- Singletons are interned (0 -> 3) at runtime init;
after that all interning functions don't change the state.
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index 6196a8e766a15b..ffb879a68745b1 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -325,7 +325,8 @@ init_global_interned_strings(PyInterpreterState *interp)
return _PyStatus_ERR("failed to create global interned dict");
}
- /* Intern statically allocated string identifiers and deepfreeze strings.
+ /* Intern statically allocated string identifiers, deepfreeze strings,
+ * and one-byte latin-1 strings.
* This must be done before any module initialization so that statically
* allocated string identifiers are used instead of heap allocated strings.
* Deepfreeze uses the interned identifiers if present to save space
@@ -333,14 +334,11 @@ init_global_interned_strings(PyInterpreterState *interp)
*/
_PyUnicode_InitStaticStrings(interp);
-#ifdef Py_GIL_DISABLED
-// In the free-threaded build, intern the 1-byte strings as well
for (int i = 0; i < 256; i++) {
PyObject *s = LATIN1(i);
_PyUnicode_InternStatic(interp, &s);
assert(s == LATIN1(i));
}
-#endif
#ifdef Py_DEBUG
assert(_PyUnicode_CheckConsistency(&_Py_STR(empty), 1));
@@ -15355,26 +15353,14 @@ intern_static(PyInterpreterState *interp, PyObject *s /* stolen */)
assert(s != NULL);
assert(_PyUnicode_CHECK(s));
assert(_PyUnicode_STATE(s).statically_allocated);
-
- switch (PyUnicode_CHECK_INTERNED(s)) {
- case SSTATE_NOT_INTERNED:
- break;
- case SSTATE_INTERNED_IMMORTAL_STATIC:
- return s;
- default:
- Py_FatalError("_PyUnicode_InternStatic called on wrong string");
- }
+ assert(!PyUnicode_CHECK_INTERNED(s));
#ifdef Py_DEBUG
/* We must not add process-global interned string if there's already a
* per-interpreter interned_dict, which might contain duplicates.
- * Except "short string" singletons: those are special-cased. */
+ */
PyObject *interned = get_interned_dict(interp);
- assert(interned == NULL || unicode_is_singleton(s));
-#ifdef Py_GIL_DISABLED
- // In the free-threaded build, don't allow even the short strings.
assert(interned == NULL);
-#endif
#endif
/* Look in the global cache first. */
@@ -15446,11 +15432,6 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */,
return s;
}
- /* Handle statically allocated strings. */
- if (_PyUnicode_STATE(s).statically_allocated) {
- return intern_static(interp, s);
- }
-
/* Is it already interned? */
switch (PyUnicode_CHECK_INTERNED(s)) {
case SSTATE_NOT_INTERNED:
@@ -15467,6 +15448,9 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */,
return s;
}
+ /* Statically allocated strings must be already interned. */
+ assert(!_PyUnicode_STATE(s).statically_allocated);
+
#if Py_GIL_DISABLED
/* In the free-threaded build, all interned strings are immortal */
immortalize = 1;
@@ -15477,13 +15461,11 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */,
immortalize = 1;
}
- /* if it's a short string, get the singleton -- and intern it */
+ /* if it's a short string, get the singleton */
if (PyUnicode_GET_LENGTH(s) == 1 &&
PyUnicode_KIND(s) == PyUnicode_1BYTE_KIND) {
PyObject *r = LATIN1(*(unsigned char*)PyUnicode_DATA(s));
- if (!PyUnicode_CHECK_INTERNED(r)) {
- r = intern_static(interp, r);
- }
+ assert(PyUnicode_CHECK_INTERNED(r));
Py_DECREF(s);
return r;
}
From 8ac5565be2e5a11fad643c2fe9cbf16d2ddb95cd Mon Sep 17 00:00:00 2001
From: Nate Ohlson
Date: Sat, 27 Jul 2024 04:57:44 -0500
Subject: [PATCH 32/47] gh-112301: Compiler warning management tooling
(#121730)
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
---
.github/workflows/build.yml | 2 +-
.github/workflows/reusable-ubuntu.yml | 5 +-
...-07-13-21-55-58.gh-issue-112301.YJS1dl.rst | 2 +
Tools/build/.warningignore_ubuntu | 3 +
Tools/build/check_warnings.py | 195 ++++++++++++++++++
5 files changed, 205 insertions(+), 2 deletions(-)
create mode 100644 Misc/NEWS.d/next/Tests/2024-07-13-21-55-58.gh-issue-112301.YJS1dl.rst
create mode 100644 Tools/build/.warningignore_ubuntu
create mode 100644 Tools/build/check_warnings.py
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 613578ae176ad9..6568b50c3a6a13 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -348,7 +348,7 @@ jobs:
with:
save: false
- name: Configure CPython
- run: ./configure --config-cache --enable-slower-safety --with-pydebug --with-openssl=$OPENSSL_DIR
+ run: ./configure CFLAGS="-fdiagnostics-format=json" --config-cache --enable-slower-safety --with-pydebug --with-openssl=$OPENSSL_DIR
- name: Build CPython
run: make -j4
- name: Display build info
diff --git a/.github/workflows/reusable-ubuntu.yml b/.github/workflows/reusable-ubuntu.yml
index 54d7765d159d49..c6289a74e9a5f6 100644
--- a/.github/workflows/reusable-ubuntu.yml
+++ b/.github/workflows/reusable-ubuntu.yml
@@ -67,6 +67,7 @@ jobs:
working-directory: ${{ env.CPYTHON_BUILDDIR }}
run: >-
../cpython-ro-srcdir/configure
+ CFLAGS="-fdiagnostics-format=json"
--config-cache
--with-pydebug
--enable-slower-safety
@@ -74,10 +75,12 @@ jobs:
${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }}
- name: Build CPython out-of-tree
working-directory: ${{ env.CPYTHON_BUILDDIR }}
- run: make -j4
+ run: make -j4 &> compiler_output.txt
- name: Display build info
working-directory: ${{ env.CPYTHON_BUILDDIR }}
run: make pythoninfo
+ - name: Check compiler warnings
+ run: python Tools/build/check_warnings.py --compiler-output-file-path=${{ env.CPYTHON_BUILDDIR }}/compiler_output.txt --warning-ignore-file-path ${GITHUB_WORKSPACE}/Tools/build/.warningignore_ubuntu
- name: Remount sources writable for tests
# some tests write to srcdir, lack of pyc files slows down testing
run: sudo mount $CPYTHON_RO_SRCDIR -oremount,rw
diff --git a/Misc/NEWS.d/next/Tests/2024-07-13-21-55-58.gh-issue-112301.YJS1dl.rst b/Misc/NEWS.d/next/Tests/2024-07-13-21-55-58.gh-issue-112301.YJS1dl.rst
new file mode 100644
index 00000000000000..d5718ed4be7606
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2024-07-13-21-55-58.gh-issue-112301.YJS1dl.rst
@@ -0,0 +1,2 @@
+Add tooling to check for changes in compiler warnings.
+Patch by Nate Ohlson.
diff --git a/Tools/build/.warningignore_ubuntu b/Tools/build/.warningignore_ubuntu
new file mode 100644
index 00000000000000..8242c8d17c89fb
--- /dev/null
+++ b/Tools/build/.warningignore_ubuntu
@@ -0,0 +1,3 @@
+# Files listed will be ignored by the compiler warning checker
+# for the Ubuntu/build and test job.
+# Keep lines sorted lexicographically to help avoid merge conflicts.
diff --git a/Tools/build/check_warnings.py b/Tools/build/check_warnings.py
new file mode 100644
index 00000000000000..f0c0067f4ab255
--- /dev/null
+++ b/Tools/build/check_warnings.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python3
+"""
+Parses compiler output with -fdiagnostics-format=json and checks that warnings
+exist only in files that are expected to have warnings.
+"""
+import argparse
+import json
+import re
+import sys
+from pathlib import Path
+
+
+def extract_warnings_from_compiler_output(compiler_output: str) -> list[dict]:
+ """
+ Extracts warnings from the compiler output when using
+ -fdiagnostics-format=json
+
+ Compiler output as a whole is not a valid json document, but includes many
+ json objects and may include other output that is not json.
+ """
+
+ # Regex to find json arrays at the top level of the file
+ # in the compiler output
+ json_arrays = re.findall(
+ r"\[(?:[^\[\]]|\[(?:[^\[\]]|\[[^\[\]]*\])*\])*\]", compiler_output
+ )
+ compiler_warnings = []
+ for array in json_arrays:
+ try:
+ json_data = json.loads(array)
+ json_objects_in_array = [entry for entry in json_data]
+ compiler_warnings.extend(
+ [
+ entry
+ for entry in json_objects_in_array
+ if entry.get("kind") == "warning"
+ ]
+ )
+ except json.JSONDecodeError:
+ continue # Skip malformed JSON
+
+ return compiler_warnings
+
+
+def get_warnings_by_file(warnings: list[dict]) -> dict[str, list[dict]]:
+ """
+ Returns a dictionary where the key is the file and the data is the warnings
+ in that file
+ """
+ warnings_by_file = {}
+ for warning in warnings:
+ locations = warning["locations"]
+ for location in locations:
+ for key in ["caret", "start", "end"]:
+ if key in location:
+ file = location[key]["file"]
+ file = file.lstrip(
+ "./"
+ ) # Remove leading current directory if present
+ if file not in warnings_by_file:
+ warnings_by_file[file] = []
+ warnings_by_file[file].append(warning)
+
+ return warnings_by_file
+
+
+def get_unexpected_warnings(
+ warnings: list[dict],
+ files_with_expected_warnings: set[str],
+ files_with_warnings: set[str],
+) -> int:
+ """
+ Returns failure status if warnings discovered in list of warnings
+ are associated with a file that is not found in the list of files
+ with expected warnings
+ """
+ unexpected_warnings = []
+ for file in files_with_warnings.keys():
+ if file not in files_with_expected_warnings:
+ unexpected_warnings.extend(files_with_warnings[file])
+
+ if unexpected_warnings:
+ print("Unexpected warnings:")
+ for warning in unexpected_warnings:
+ print(warning)
+ return 1
+
+ return 0
+
+
+def get_unexpected_improvements(
+ warnings: list[dict],
+ files_with_expected_warnings: set[str],
+ files_with_warnings: set[str],
+) -> int:
+ """
+ Returns failure status if there are no warnings in the list of warnings for
+ a file that is in the list of files with expected warnings
+ """
+ unexpected_improvements = []
+ for file in files_with_expected_warnings:
+ if file not in files_with_warnings.keys():
+ unexpected_improvements.append(file)
+
+ if unexpected_improvements:
+ print("Unexpected improvements:")
+ for file in unexpected_improvements:
+ print(file)
+ return 1
+
+ return 0
+
+
+def main(argv: list[str] | None = None) -> int:
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--compiler-output-file-path",
+ type=str,
+ required=True,
+ help="Path to the compiler output file",
+ )
+ parser.add_argument(
+ "--warning-ignore-file-path",
+ type=str,
+ required=True,
+ help="Path to the warning ignore file",
+ )
+ parser.add_argument(
+ "--fail-on-regression",
+ action="store_true",
+ default=False,
+ help="Flag to fail if new warnings are found",
+ )
+ parser.add_argument(
+ "--fail-on-improvement",
+ action="store_true",
+ default=False,
+ help="Flag to fail if files that were expected "
+ "to have warnings have no warnings",
+ )
+
+ args = parser.parse_args(argv)
+
+ exit_code = 0
+
+ # Check that the compiler output file is a valid path
+ if not Path(args.compiler_output_file_path).is_file():
+ print(
+ "Compiler output file does not exist: "
+ f"{args.compiler_output_file_path}"
+ )
+ return 1
+
+ # Check that the warning ignore file is a valid path
+ if not Path(args.warning_ignore_file_path).is_file():
+ print(
+ "Warning ignore file does not exist: "
+ f"{args.warning_ignore_file_path}"
+ )
+ return 1
+
+ with Path(args.compiler_output_file_path).open(encoding="UTF-8") as f:
+ compiler_output_file_contents = f.read()
+
+ with Path(args.warning_ignore_file_path).open(
+ encoding="UTF-8"
+ ) as clean_files:
+ files_with_expected_warnings = {
+ file.strip()
+ for file in clean_files
+ if file.strip() and not file.startswith("#")
+ }
+
+ warnings = extract_warnings_from_compiler_output(
+ compiler_output_file_contents
+ )
+ files_with_warnings = get_warnings_by_file(warnings)
+
+ status = get_unexpected_warnings(
+ warnings, files_with_expected_warnings, files_with_warnings
+ )
+ if args.fail_on_regression:
+ exit_code |= status
+
+ status = get_unexpected_improvements(
+ warnings, files_with_expected_warnings, files_with_warnings
+ )
+ if args.fail_on_improvement:
+ exit_code |= status
+
+ return exit_code
+
+
+if __name__ == "__main__":
+ sys.exit(main())
From 7a6d4ccf0ec16e09f0d8b21c5a0c591e5e3e45f7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?=
<10796600+picnixz@users.noreply.github.com>
Date: Sat, 27 Jul 2024 12:10:42 +0200
Subject: [PATCH 33/47] gh-122170: Handle ValueError raised by os.stat() in
linecache (GH-122176)
---
Lib/linecache.py | 6 ++--
Lib/test/test_linecache.py | 31 +++++++++++++++++++
...-07-23-15-30-23.gh-issue-122170.Z9gi3Y.rst | 2 ++
3 files changed, 37 insertions(+), 2 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2024-07-23-15-30-23.gh-issue-122170.Z9gi3Y.rst
diff --git a/Lib/linecache.py b/Lib/linecache.py
index 3462f1c451ba29..4b38a0464d8747 100644
--- a/Lib/linecache.py
+++ b/Lib/linecache.py
@@ -70,7 +70,7 @@ def checkcache(filename=None):
return
try:
stat = os.stat(fullname)
- except OSError:
+ except (OSError, ValueError):
cache.pop(filename, None)
continue
if size != stat.st_size or mtime != stat.st_mtime:
@@ -135,10 +135,12 @@ def updatecache(filename, module_globals=None):
try:
stat = os.stat(fullname)
break
- except OSError:
+ except (OSError, ValueError):
pass
else:
return []
+ except ValueError: # may be raised by os.stat()
+ return []
try:
with tokenize.open(fullname) as fp:
lines = fp.readlines()
diff --git a/Lib/test/test_linecache.py b/Lib/test/test_linecache.py
index 8ac521d72ef13e..6f5955791407ea 100644
--- a/Lib/test/test_linecache.py
+++ b/Lib/test/test_linecache.py
@@ -280,6 +280,37 @@ def test_loader(self):
self.assertEqual(linecache.getlines(filename, module_globals),
['source for x.y.z\n'])
+ def test_invalid_names(self):
+ for name, desc in [
+ ('\x00', 'NUL bytes filename'),
+ (__file__ + '\x00', 'filename with embedded NUL bytes'),
+ # A filename with surrogate codes. A UnicodeEncodeError is raised
+ # by os.stat() upon querying, which is a subclass of ValueError.
+ ("\uD834\uDD1E.py", 'surrogate codes (MUSICAL SYMBOL G CLEF)'),
+ # For POSIX platforms, an OSError will be raised but for Windows
+ # platforms, a ValueError is raised due to the path_t converter.
+ # See: https://github.com/python/cpython/issues/122170
+ ('a' * 1_000_000, 'very long filename'),
+ ]:
+ with self.subTest(f'updatecache: {desc}'):
+ linecache.clearcache()
+ lines = linecache.updatecache(name)
+ self.assertListEqual(lines, [])
+ self.assertNotIn(name, linecache.cache)
+
+ # hack into the cache (it shouldn't be allowed
+ # but we never know what people do...)
+ for key, fullname in [(name, 'ok'), ('key', name), (name, name)]:
+ with self.subTest(f'checkcache: {desc}',
+ key=key, fullname=fullname):
+ linecache.clearcache()
+ linecache.cache[key] = (0, 1234, [], fullname)
+ linecache.checkcache(key)
+ self.assertNotIn(key, linecache.cache)
+
+ # just to be sure that we did not mess with cache
+ linecache.clearcache()
+
class LineCacheInvalidationTests(unittest.TestCase):
def setUp(self):
diff --git a/Misc/NEWS.d/next/Library/2024-07-23-15-30-23.gh-issue-122170.Z9gi3Y.rst b/Misc/NEWS.d/next/Library/2024-07-23-15-30-23.gh-issue-122170.Z9gi3Y.rst
new file mode 100644
index 00000000000000..7eeb9f67ad4b3a
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-07-23-15-30-23.gh-issue-122170.Z9gi3Y.rst
@@ -0,0 +1,2 @@
+Handle :exc:`ValueError`\s raised by :func:`os.stat` in :mod:`linecache`.
+Patch by Bénédikt Tran.
From 4e04d1a3d237abd0cba354024556c39519e0d163 Mon Sep 17 00:00:00 2001
From: Seth Michael Larson
Date: Sat, 27 Jul 2024 06:10:05 -0500
Subject: [PATCH 34/47] gh-122044: Don't error during gitignore filtering with
no files (#122045)
---
Tools/build/generate_sbom.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Tools/build/generate_sbom.py b/Tools/build/generate_sbom.py
index c08568f2e00326..1b000c3b16a17a 100644
--- a/Tools/build/generate_sbom.py
+++ b/Tools/build/generate_sbom.py
@@ -108,6 +108,10 @@ def filter_gitignored_paths(paths: list[str]) -> list[str]:
'.gitignore:9:*.a Tools/lib.a'
"""
+ # No paths means no filtering to be done.
+ if not paths:
+ return []
+
# Filter out files in gitignore.
# Non-matching files show up as '::'
git_check_ignore_proc = subprocess.run(
From 4e7550934941050f54c86338cd5e40cd565ceaf2 Mon Sep 17 00:00:00 2001
From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
Date: Sat, 27 Jul 2024 14:17:54 +0300
Subject: [PATCH 35/47] gh-122085: Use include files for `whatsnew/3.14.rst`
deprecations (#122242)
---
Doc/deprecations/pending-removal-in-3.16.rst | 7 +++-
.../pending-removal-in-future.rst | 4 +++
Doc/whatsnew/3.12.rst | 2 ++
Doc/whatsnew/3.14.rst | 32 ++++++++++++-------
4 files changed, 32 insertions(+), 13 deletions(-)
diff --git a/Doc/deprecations/pending-removal-in-3.16.rst b/Doc/deprecations/pending-removal-in-3.16.rst
index 97e6bf28efddf2..10cb5e424a623b 100644
--- a/Doc/deprecations/pending-removal-in-3.16.rst
+++ b/Doc/deprecations/pending-removal-in-3.16.rst
@@ -1,5 +1,10 @@
Pending Removal in Python 3.16
------------------------------
-* :class:`array.array` ``'u'`` type (:c:type:`wchar_t`):
+* :mod:`array`:
+ :class:`array.array` ``'u'`` type (:c:type:`wchar_t`):
use the ``'w'`` type instead (``Py_UCS4``).
+
+* :mod:`symtable`:
+ Deprecate :meth:`symtable.Class.get_methods` due to the lack of interest.
+ (Contributed by Bénédikt Tran in :gh:`119698`.)
diff --git a/Doc/deprecations/pending-removal-in-future.rst b/Doc/deprecations/pending-removal-in-future.rst
index db6a41fe8880f6..7f10d9a98257f9 100644
--- a/Doc/deprecations/pending-removal-in-future.rst
+++ b/Doc/deprecations/pending-removal-in-future.rst
@@ -34,6 +34,10 @@ although there is currently no date scheduled for their removal.
:class:`complex`: these methods will be required to return an instance of
:class:`complex`.
* Delegation of ``int()`` to ``__trunc__()`` method.
+ * Passing a complex number as the *real* or *imag* argument in the
+ :func:`complex` constructor is now deprecated; it should only be passed
+ as a single positional argument.
+ (Contributed by Serhiy Storchaka in :gh:`109218`.)
* :mod:`calendar`: ``calendar.January`` and ``calendar.February`` constants are
deprecated and replaced by :data:`calendar.JANUARY` and
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index b4cd4aa6e83b91..fc2b6519fb1307 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -1336,6 +1336,8 @@ Deprecated
.. include:: ../deprecations/pending-removal-in-3.15.rst
+.. include:: ../deprecations/pending-removal-in-3.16.rst
+
.. include:: ../deprecations/pending-removal-in-future.rst
Removed
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index cc03088592d9d4..7450597e8597ad 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -156,6 +156,12 @@ pdb
:pdbcmd:`commands` are preserved across hard-coded breakpoints.
(Contributed by Tian Gao in :gh:`121450`.)
+pickle
+------
+
+* Set the default protocol version on the :mod:`pickle` module to 5.
+ For more details, please see :ref:`pickle protocols `.
+
symtable
--------
@@ -167,12 +173,7 @@ symtable
(Contributed by Bénédikt Tran in :gh:`120029`.)
-pickle
-------
-
-* Set the default protocol version on the :mod:`pickle` module to 5.
- For more details, please see :ref:`pickle protocols `.
-
+.. Add improved modules above alphabetically, not here at the end.
Optimizations
=============
@@ -185,24 +186,32 @@ asyncio
reduces memory usage.
(Contributed by Kumar Aditya in :gh:`107803`.)
-
-
Deprecated
==========
-* Passing a complex number as the *real* or *imag* argument in the
+* :mod:`builtins`:
+ Passing a complex number as the *real* or *imag* argument in the
:func:`complex` constructor is now deprecated; it should only be passed
as a single positional argument.
(Contributed by Serhiy Storchaka in :gh:`109218`.)
-* :term:`Soft deprecate ` :func:`os.popen` and
+* :mod:`os`:
+ :term:`Soft deprecate ` :func:`os.popen` and
:func:`os.spawn* ` functions. They should no longer be used to
write new code. The :mod:`subprocess` module is recommended instead.
(Contributed by Victor Stinner in :gh:`120743`.)
-* Deprecate :meth:`symtable.Class.get_methods` due to the lack of interest.
+* :mod:`symtable`:
+ Deprecate :meth:`symtable.Class.get_methods` due to the lack of interest.
(Contributed by Bénédikt Tran in :gh:`119698`.)
+.. Add deprecations above alphabetically, not here at the end.
+
+.. include:: ../deprecations/pending-removal-in-3.15.rst
+
+.. include:: ../deprecations/pending-removal-in-3.16.rst
+
+.. include:: ../deprecations/pending-removal-in-future.rst
Removed
=======
@@ -262,7 +271,6 @@ asyncio
(Contributed by Kumar Aditya in :gh:`120804`.)
-
collections.abc
---------------
From 45614ecb2bdc2b984f051c7eade39458a3f8709f Mon Sep 17 00:00:00 2001
From: Jelle Zijlstra
Date: Sat, 27 Jul 2024 09:36:06 -0700
Subject: [PATCH 36/47] gh-119180: Use type descriptors to access annotations
(PEP 749) (#122074)
---
Lib/annotationlib.py | 36 +++++++-
Lib/test/test_annotationlib.py | 84 ++++++++++++++++++-
...-07-23-17-13-10.gh-issue-119180.5PZELo.rst | 2 +
3 files changed, 117 insertions(+), 5 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2024-07-23-17-13-10.gh-issue-119180.5PZELo.rst
diff --git a/Lib/annotationlib.py b/Lib/annotationlib.py
index b4036ffb189c2d..eea24232f9f0d0 100644
--- a/Lib/annotationlib.py
+++ b/Lib/annotationlib.py
@@ -524,6 +524,27 @@ def call_annotate_function(annotate, format, owner=None):
raise ValueError(f"Invalid format: {format!r}")
+# We use the descriptors from builtins.type instead of accessing
+# .__annotations__ and .__annotate__ directly on class objects, because
+# otherwise we could get wrong results in some cases involving metaclasses.
+# See PEP 749.
+_BASE_GET_ANNOTATE = type.__dict__["__annotate__"].__get__
+_BASE_GET_ANNOTATIONS = type.__dict__["__annotations__"].__get__
+
+
+def get_annotate_function(obj):
+ """Get the __annotate__ function for an object.
+
+ obj may be a function, class, or module, or a user-defined type with
+ an `__annotate__` attribute.
+
+ Returns the __annotate__ function or None.
+ """
+ if isinstance(obj, type):
+ return _BASE_GET_ANNOTATE(obj)
+ return getattr(obj, "__annotate__", None)
+
+
def get_annotations(
obj, *, globals=None, locals=None, eval_str=False, format=Format.VALUE
):
@@ -576,16 +597,23 @@ def get_annotations(
# For VALUE format, we look at __annotations__ directly.
if format != Format.VALUE:
- annotate = getattr(obj, "__annotate__", None)
+ annotate = get_annotate_function(obj)
if annotate is not None:
ann = call_annotate_function(annotate, format, owner=obj)
if not isinstance(ann, dict):
raise ValueError(f"{obj!r}.__annotate__ returned a non-dict")
return dict(ann)
- ann = getattr(obj, "__annotations__", None)
- if ann is None:
- return {}
+ if isinstance(obj, type):
+ try:
+ ann = _BASE_GET_ANNOTATIONS(obj)
+ except AttributeError:
+ # For static types, the descriptor raises AttributeError.
+ return {}
+ else:
+ ann = getattr(obj, "__annotations__", None)
+ if ann is None:
+ return {}
if not isinstance(ann, dict):
raise ValueError(f"{obj!r}.__annotations__ is neither a dict nor None")
diff --git a/Lib/test/test_annotationlib.py b/Lib/test/test_annotationlib.py
index e68d63c91d1a73..e459d27d3c4b38 100644
--- a/Lib/test/test_annotationlib.py
+++ b/Lib/test/test_annotationlib.py
@@ -2,8 +2,10 @@
import annotationlib
import functools
+import itertools
import pickle
import unittest
+from annotationlib import Format, get_annotations, get_annotate_function
from typing import Unpack
from test.test_inspect import inspect_stock_annotations
@@ -767,5 +769,85 @@ def test_pep_695_generics_with_future_annotations_nested_in_function(self):
self.assertEqual(
set(results.generic_func_annotations.values()),
- set(results.generic_func.__type_params__)
+ set(results.generic_func.__type_params__),
)
+
+
+class MetaclassTests(unittest.TestCase):
+ def test_annotated_meta(self):
+ class Meta(type):
+ a: int
+
+ class X(metaclass=Meta):
+ pass
+
+ class Y(metaclass=Meta):
+ b: float
+
+ self.assertEqual(get_annotations(Meta), {"a": int})
+ self.assertEqual(get_annotate_function(Meta)(Format.VALUE), {"a": int})
+
+ self.assertEqual(get_annotations(X), {})
+ self.assertIs(get_annotate_function(X), None)
+
+ self.assertEqual(get_annotations(Y), {"b": float})
+ self.assertEqual(get_annotate_function(Y)(Format.VALUE), {"b": float})
+
+ def test_unannotated_meta(self):
+ class Meta(type): pass
+
+ class X(metaclass=Meta):
+ a: str
+
+ class Y(X): pass
+
+ self.assertEqual(get_annotations(Meta), {})
+ self.assertIs(get_annotate_function(Meta), None)
+
+ self.assertEqual(get_annotations(Y), {})
+ self.assertIs(get_annotate_function(Y), None)
+
+ self.assertEqual(get_annotations(X), {"a": str})
+ self.assertEqual(get_annotate_function(X)(Format.VALUE), {"a": str})
+
+ def test_ordering(self):
+ # Based on a sample by David Ellis
+ # https://discuss.python.org/t/pep-749-implementing-pep-649/54974/38
+
+ def make_classes():
+ class Meta(type):
+ a: int
+ expected_annotations = {"a": int}
+
+ class A(type, metaclass=Meta):
+ b: float
+ expected_annotations = {"b": float}
+
+ class B(metaclass=A):
+ c: str
+ expected_annotations = {"c": str}
+
+ class C(B):
+ expected_annotations = {}
+
+ class D(metaclass=Meta):
+ expected_annotations = {}
+
+ return Meta, A, B, C, D
+
+ classes = make_classes()
+ class_count = len(classes)
+ for order in itertools.permutations(range(class_count), class_count):
+ names = ", ".join(classes[i].__name__ for i in order)
+ with self.subTest(names=names):
+ classes = make_classes() # Regenerate classes
+ for i in order:
+ get_annotations(classes[i])
+ for c in classes:
+ with self.subTest(c=c):
+ self.assertEqual(get_annotations(c), c.expected_annotations)
+ annotate_func = get_annotate_function(c)
+ if c.expected_annotations:
+ self.assertEqual(annotate_func(Format.VALUE), c.expected_annotations)
+ else:
+ self.assertIs(annotate_func, None)
diff --git a/Misc/NEWS.d/next/Library/2024-07-23-17-13-10.gh-issue-119180.5PZELo.rst b/Misc/NEWS.d/next/Library/2024-07-23-17-13-10.gh-issue-119180.5PZELo.rst
new file mode 100644
index 00000000000000..d65e89f7523b0a
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-07-23-17-13-10.gh-issue-119180.5PZELo.rst
@@ -0,0 +1,2 @@
+Fix handling of classes with custom metaclasses in
+``annotationlib.get_annotations``.
From cbac8a3888411587beb026e246889154fbdd49a3 Mon Sep 17 00:00:00 2001
From: Barney Gale
Date: Sat, 27 Jul 2024 18:03:18 +0100
Subject: [PATCH 37/47] GH-121462: pathlib docs: improve table of corresponding
os/os.path functions (#121465)
Re-order table of corresponding functions with the following priorities:
1. Pure functionality is at the top
2. `os.path` functions are shown before `os` functions
3. Similar functionality is kept together
4. Functionality follows docs order where possible
Add a few missed correspondences:
- `os.path.isjunction` and `Path.is_junction`
- `os.path.ismount` and `Path.is_mount`
- `os.lstat()` and `Path.lstat()`
- `os.lchmod()` and `Path.lchmod()`
Also add footnotes describing a few differences.
---
Doc/library/pathlib.rst | 87 ++++++++++++++++++++++++-----------------
1 file changed, 51 insertions(+), 36 deletions(-)
diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst
index 41b2d40a504a12..60c099af928123 100644
--- a/Doc/library/pathlib.rst
+++ b/Doc/library/pathlib.rst
@@ -1864,39 +1864,54 @@ Corresponding tools
Below is a table mapping various :mod:`os` functions to their corresponding
:class:`PurePath`/:class:`Path` equivalent.
-==================================== ==============================
-:mod:`os` and :mod:`os.path` :mod:`pathlib`
-==================================== ==============================
-:func:`os.path.abspath` :meth:`Path.absolute`
-:func:`os.path.realpath` :meth:`Path.resolve`
-:func:`os.chmod` :meth:`Path.chmod`
-:func:`os.mkdir` :meth:`Path.mkdir`
-:func:`os.makedirs` :meth:`Path.mkdir`
-:func:`os.rename` :meth:`Path.rename`
-:func:`os.replace` :meth:`Path.replace`
-:func:`os.rmdir` :meth:`Path.rmdir`
-:func:`os.remove`, :func:`os.unlink` :meth:`Path.unlink`
-:func:`os.getcwd` :func:`Path.cwd`
-:func:`os.path.exists` :meth:`Path.exists`
-:func:`os.path.expanduser` :meth:`Path.expanduser` and
- :meth:`Path.home`
-:func:`os.listdir` :meth:`Path.iterdir`
-:func:`os.walk` :meth:`Path.walk`
-:func:`os.path.isdir` :meth:`Path.is_dir`
-:func:`os.path.isfile` :meth:`Path.is_file`
-:func:`os.path.islink` :meth:`Path.is_symlink`
-:func:`os.link` :meth:`Path.hardlink_to`
-:func:`os.symlink` :meth:`Path.symlink_to`
-:func:`os.readlink` :meth:`Path.readlink`
-:func:`os.path.relpath` :meth:`PurePath.relative_to`
-:func:`os.stat` :meth:`Path.stat`,
- :meth:`Path.owner`,
- :meth:`Path.group`
-:func:`os.path.isabs` :meth:`PurePath.is_absolute`
-:func:`os.path.join` :func:`PurePath.joinpath`
-:func:`os.path.basename` :attr:`PurePath.name`
-:func:`os.path.dirname` :attr:`PurePath.parent`
-:func:`os.path.samefile` :meth:`Path.samefile`
-:func:`os.path.splitext` :attr:`PurePath.stem` and
- :attr:`PurePath.suffix`
-==================================== ==============================
+===================================== ==============================================
+:mod:`os` and :mod:`os.path` :mod:`pathlib`
+===================================== ==============================================
+:func:`os.path.dirname` :attr:`PurePath.parent`
+:func:`os.path.basename` :attr:`PurePath.name`
+:func:`os.path.splitext` :attr:`PurePath.stem`, :attr:`PurePath.suffix`
+:func:`os.path.join` :meth:`PurePath.joinpath`
+:func:`os.path.isabs` :meth:`PurePath.is_absolute`
+:func:`os.path.relpath` :meth:`PurePath.relative_to` [1]_
+:func:`os.path.expanduser` :meth:`Path.expanduser` [2]_
+:func:`os.path.realpath` :meth:`Path.resolve`
+:func:`os.path.abspath` :meth:`Path.absolute` [3]_
+:func:`os.path.exists` :meth:`Path.exists`
+:func:`os.path.isfile` :meth:`Path.is_file`
+:func:`os.path.isdir` :meth:`Path.is_dir`
+:func:`os.path.islink` :meth:`Path.is_symlink`
+:func:`os.path.isjunction` :meth:`Path.is_junction`
+:func:`os.path.ismount` :meth:`Path.is_mount`
+:func:`os.path.samefile` :meth:`Path.samefile`
+:func:`os.getcwd` :meth:`Path.cwd`
+:func:`os.stat` :meth:`Path.stat`
+:func:`os.lstat` :meth:`Path.lstat`
+:func:`os.listdir` :meth:`Path.iterdir`
+:func:`os.walk` :meth:`Path.walk` [4]_
+:func:`os.mkdir`, :func:`os.makedirs` :meth:`Path.mkdir`
+:func:`os.link` :meth:`Path.hardlink_to`
+:func:`os.symlink` :meth:`Path.symlink_to`
+:func:`os.readlink` :meth:`Path.readlink`
+:func:`os.rename` :meth:`Path.rename`
+:func:`os.replace` :meth:`Path.replace`
+:func:`os.remove`, :func:`os.unlink` :meth:`Path.unlink`
+:func:`os.rmdir` :meth:`Path.rmdir`
+:func:`os.chmod` :meth:`Path.chmod`
+:func:`os.lchmod` :meth:`Path.lchmod`
+===================================== ==============================================
+
+.. rubric:: Footnotes
+
+.. [1] :func:`os.path.relpath` calls :func:`~os.path.abspath` to make paths
+ absolute and remove "``..``" parts, whereas :meth:`PurePath.relative_to`
+ is a lexical operation that raises :exc:`ValueError` when its inputs'
+ anchors differ (e.g. if one path is absolute and the other relative.)
+.. [2] :func:`os.path.expanduser` returns the path unchanged if the home
+ directory can't be resolved, whereas :meth:`Path.expanduser` raises
+ :exc:`RuntimeError`.
+.. [3] :func:`os.path.abspath` removes "``..``" components without resolving
+ symlinks, which may change the meaning of the path, whereas
+ :meth:`Path.absolute` leaves any "``..``" components in the path.
+.. [4] :func:`os.walk` always follows symlinks when categorizing paths into
+ *dirnames* and *filenames*, whereas :meth:`Path.walk` categorizes all
+ symlinks into *filenames* when *follow_symlinks* is false (the default.)
From ae192262ad1cffb6ece9d16e67804386c382be0c Mon Sep 17 00:00:00 2001
From: Jelle Zijlstra
Date: Sat, 27 Jul 2024 10:24:10 -0700
Subject: [PATCH 38/47] gh-119180: Add evaluate functions for type params and
type aliases (#122212)
---
Include/internal/pycore_global_objects.h | 1 +
Include/internal/pycore_typevarobject.h | 1 +
Lib/annotationlib.py | 19 +-
Lib/test/test_annotationlib.py | 19 ++
Lib/test/test_type_params.py | 43 ++-
...-07-23-22-26-00.gh-issue-119180.B2IVT8.rst | 7 +
Objects/genericaliasobject.c | 70 +----
Objects/typevarobject.c | 274 ++++++++++++++++++
Objects/unionobject.c | 66 +----
Python/compile.c | 13 +-
Python/symtable.c | 31 +-
11 files changed, 385 insertions(+), 159 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2024-07-23-22-26-00.gh-issue-119180.B2IVT8.rst
diff --git a/Include/internal/pycore_global_objects.h b/Include/internal/pycore_global_objects.h
index 327fcc24cb29f1..913dce6f1ec0fe 100644
--- a/Include/internal/pycore_global_objects.h
+++ b/Include/internal/pycore_global_objects.h
@@ -81,6 +81,7 @@ struct _Py_interp_cached_objects {
PyTypeObject *paramspec_type;
PyTypeObject *paramspecargs_type;
PyTypeObject *paramspeckwargs_type;
+ PyTypeObject *constevaluator_type;
};
#define _Py_INTERP_STATIC_OBJECT(interp, NAME) \
diff --git a/Include/internal/pycore_typevarobject.h b/Include/internal/pycore_typevarobject.h
index a368edebd622a1..4d7556e68cdaee 100644
--- a/Include/internal/pycore_typevarobject.h
+++ b/Include/internal/pycore_typevarobject.h
@@ -16,6 +16,7 @@ extern PyObject *_Py_subscript_generic(PyThreadState *, PyObject *);
extern PyObject *_Py_set_typeparam_default(PyThreadState *, PyObject *, PyObject *);
extern int _Py_initialize_generic(PyInterpreterState *);
extern void _Py_clear_generic_types(PyInterpreterState *);
+extern int _Py_typing_type_repr(PyUnicodeWriter *, PyObject *);
extern PyTypeObject _PyTypeAlias_Type;
extern PyTypeObject _PyNoDefault_Type;
diff --git a/Lib/annotationlib.py b/Lib/annotationlib.py
index eea24232f9f0d0..141e31bbf910e3 100644
--- a/Lib/annotationlib.py
+++ b/Lib/annotationlib.py
@@ -413,7 +413,16 @@ def __missing__(self, key):
return fwdref
-def call_annotate_function(annotate, format, owner=None):
+def call_evaluate_function(evaluate, format, *, owner=None):
+ """Call an evaluate function. Evaluate functions are normally generated for
+ the value of type aliases and the bounds, constraints, and defaults of
+ type parameter objects.
+ """
+ return call_annotate_function(evaluate, format, owner=owner, _is_evaluate=True)
+
+
+def call_annotate_function(annotate, format, *, owner=None,
+ _is_evaluate=False):
"""Call an __annotate__ function. __annotate__ functions are normally
generated by the compiler to defer the evaluation of annotations. They
can be called with any of the format arguments in the Format enum, but
@@ -459,8 +468,11 @@ def call_annotate_function(annotate, format, owner=None):
closure = tuple(new_closure)
else:
closure = None
- func = types.FunctionType(annotate.__code__, globals, closure=closure)
+ func = types.FunctionType(annotate.__code__, globals, closure=closure,
+ argdefs=annotate.__defaults__, kwdefaults=annotate.__kwdefaults__)
annos = func(Format.VALUE)
+ if _is_evaluate:
+ return annos if isinstance(annos, str) else repr(annos)
return {
key: val if isinstance(val, str) else repr(val)
for key, val in annos.items()
@@ -511,7 +523,8 @@ def call_annotate_function(annotate, format, owner=None):
closure = tuple(new_closure)
else:
closure = None
- func = types.FunctionType(annotate.__code__, globals, closure=closure)
+ func = types.FunctionType(annotate.__code__, globals, closure=closure,
+ argdefs=annotate.__defaults__, kwdefaults=annotate.__kwdefaults__)
result = func(Format.VALUE)
for obj in globals.stringifiers:
obj.__class__ = ForwardRef
diff --git a/Lib/test/test_annotationlib.py b/Lib/test/test_annotationlib.py
index e459d27d3c4b38..e4dcdb6b58d009 100644
--- a/Lib/test/test_annotationlib.py
+++ b/Lib/test/test_annotationlib.py
@@ -773,6 +773,25 @@ def test_pep_695_generics_with_future_annotations_nested_in_function(self):
)
+class TestCallEvaluateFunction(unittest.TestCase):
+ def test_evaluation(self):
+ def evaluate(format, exc=NotImplementedError):
+ if format != 1:
+ raise exc
+ return undefined
+
+ with self.assertRaises(NameError):
+ annotationlib.call_evaluate_function(evaluate, annotationlib.Format.VALUE)
+ self.assertEqual(
+ annotationlib.call_evaluate_function(evaluate, annotationlib.Format.FORWARDREF),
+ annotationlib.ForwardRef("undefined"),
+ )
+ self.assertEqual(
+ annotationlib.call_evaluate_function(evaluate, annotationlib.Format.SOURCE),
+ "undefined",
+ )
+
+
class MetaclassTests(unittest.TestCase):
def test_annotated_meta(self):
class Meta(type):
diff --git a/Lib/test/test_type_params.py b/Lib/test/test_type_params.py
index bf1a34b9fc82b3..d9c9ec4eddc368 100644
--- a/Lib/test/test_type_params.py
+++ b/Lib/test/test_type_params.py
@@ -1,3 +1,4 @@
+import annotationlib
import asyncio
import textwrap
import types
@@ -6,7 +7,7 @@
import weakref
from test.support import requires_working_socket, check_syntax_error, run_code
-from typing import Generic, NoDefault, Sequence, TypeVar, TypeVarTuple, ParamSpec, get_args
+from typing import Generic, NoDefault, Sequence, TypeAliasType, TypeVar, TypeVarTuple, ParamSpec, get_args
class TypeParamsInvalidTest(unittest.TestCase):
@@ -1394,3 +1395,43 @@ def test_symtable_key_regression_name(self):
self.assertEqual(ns["X1"].__type_params__[0].__default__, "A")
self.assertEqual(ns["X2"].__type_params__[0].__default__, "B")
+
+
+class TestEvaluateFunctions(unittest.TestCase):
+ def test_general(self):
+ type Alias = int
+ Alias2 = TypeAliasType("Alias2", int)
+ def f[T: int = int, **P = int, *Ts = int](): pass
+ T, P, Ts = f.__type_params__
+ T2 = TypeVar("T2", bound=int, default=int)
+ P2 = ParamSpec("P2", default=int)
+ Ts2 = TypeVarTuple("Ts2", default=int)
+ cases = [
+ Alias.evaluate_value,
+ Alias2.evaluate_value,
+ T.evaluate_bound,
+ T.evaluate_default,
+ P.evaluate_default,
+ Ts.evaluate_default,
+ T2.evaluate_bound,
+ T2.evaluate_default,
+ P2.evaluate_default,
+ Ts2.evaluate_default,
+ ]
+ for case in cases:
+ with self.subTest(case=case):
+ self.assertIs(case(1), int)
+ self.assertIs(annotationlib.call_evaluate_function(case, annotationlib.Format.VALUE), int)
+ self.assertIs(annotationlib.call_evaluate_function(case, annotationlib.Format.FORWARDREF), int)
+ self.assertEqual(annotationlib.call_evaluate_function(case, annotationlib.Format.SOURCE), 'int')
+
+ def test_constraints(self):
+ def f[T: (int, str)](): pass
+ T, = f.__type_params__
+ T2 = TypeVar("T2", int, str)
+ for case in [T, T2]:
+ with self.subTest(case=case):
+ self.assertEqual(case.evaluate_constraints(1), (int, str))
+ self.assertEqual(annotationlib.call_evaluate_function(case.evaluate_constraints, annotationlib.Format.VALUE), (int, str))
+ self.assertEqual(annotationlib.call_evaluate_function(case.evaluate_constraints, annotationlib.Format.FORWARDREF), (int, str))
+ self.assertEqual(annotationlib.call_evaluate_function(case.evaluate_constraints, annotationlib.Format.SOURCE), '(int, str)')
diff --git a/Misc/NEWS.d/next/Library/2024-07-23-22-26-00.gh-issue-119180.B2IVT8.rst b/Misc/NEWS.d/next/Library/2024-07-23-22-26-00.gh-issue-119180.B2IVT8.rst
new file mode 100644
index 00000000000000..13f51e4c42f4a0
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-07-23-22-26-00.gh-issue-119180.B2IVT8.rst
@@ -0,0 +1,7 @@
+As part of :pep:`749`, add the following attributes for customizing
+evaluation of annotation scopes:
+
+* ``evaluate_value`` on :class:`typing.TypeAliasType`
+* ``evaluate_bound``, ``evaluate_constraints``, and ``evaluate_default`` on :class:`typing.TypeVar`
+* ``evaluate_default`` on :class:`typing.ParamSpec`
+* ``evaluate_default`` on :class:`typing.TypeVarTuple`
diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c
index 96c96491501a2c..64b4e2645cbaee 100644
--- a/Objects/genericaliasobject.c
+++ b/Objects/genericaliasobject.c
@@ -4,6 +4,7 @@
#include "pycore_ceval.h" // _PyEval_GetBuiltin()
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
#include "pycore_object.h"
+#include "pycore_typevarobject.h" // _Py_typing_type_repr
#include "pycore_unionobject.h" // _Py_union_type_or, _PyGenericAlias_Check
@@ -50,69 +51,6 @@ ga_traverse(PyObject *self, visitproc visit, void *arg)
return 0;
}
-static int
-ga_repr_item(PyUnicodeWriter *writer, PyObject *p)
-{
- PyObject *qualname = NULL;
- PyObject *module = NULL;
- int rc;
-
- if (p == Py_Ellipsis) {
- // The Ellipsis object
- rc = PyUnicodeWriter_WriteUTF8(writer, "...", 3);
- goto done;
- }
-
- if ((rc = PyObject_HasAttrWithError(p, &_Py_ID(__origin__))) > 0 &&
- (rc = PyObject_HasAttrWithError(p, &_Py_ID(__args__))) > 0)
- {
- // It looks like a GenericAlias
- goto use_repr;
- }
- if (rc < 0) {
- goto error;
- }
-
- if (PyObject_GetOptionalAttr(p, &_Py_ID(__qualname__), &qualname) < 0) {
- goto error;
- }
- if (qualname == NULL) {
- goto use_repr;
- }
- if (PyObject_GetOptionalAttr(p, &_Py_ID(__module__), &module) < 0) {
- goto error;
- }
- if (module == NULL || module == Py_None) {
- goto use_repr;
- }
-
- // Looks like a class
- if (PyUnicode_Check(module) &&
- _PyUnicode_EqualToASCIIString(module, "builtins"))
- {
- // builtins don't need a module name
- rc = PyUnicodeWriter_WriteStr(writer, qualname);
- goto done;
- }
- else {
- rc = PyUnicodeWriter_Format(writer, "%S.%S", module, qualname);
- goto done;
- }
-
-error:
- rc = -1;
- goto done;
-
-use_repr:
- rc = PyUnicodeWriter_WriteRepr(writer, p);
- goto done;
-
-done:
- Py_XDECREF(qualname);
- Py_XDECREF(module);
- return rc;
-}
-
static int
ga_repr_items_list(PyUnicodeWriter *writer, PyObject *p)
{
@@ -131,7 +69,7 @@ ga_repr_items_list(PyUnicodeWriter *writer, PyObject *p)
}
}
PyObject *item = PyList_GET_ITEM(p, i);
- if (ga_repr_item(writer, item) < 0) {
+ if (_Py_typing_type_repr(writer, item) < 0) {
return -1;
}
}
@@ -162,7 +100,7 @@ ga_repr(PyObject *self)
goto error;
}
}
- if (ga_repr_item(writer, alias->origin) < 0) {
+ if (_Py_typing_type_repr(writer, alias->origin) < 0) {
goto error;
}
if (PyUnicodeWriter_WriteChar(writer, '[') < 0) {
@@ -181,7 +119,7 @@ ga_repr(PyObject *self)
goto error;
}
}
- else if (ga_repr_item(writer, p) < 0) {
+ else if (_Py_typing_type_repr(writer, p) < 0) {
goto error;
}
}
diff --git a/Objects/typevarobject.c b/Objects/typevarobject.c
index c8ab14053de418..fb1f260571b582 100644
--- a/Objects/typevarobject.c
+++ b/Objects/typevarobject.c
@@ -116,6 +116,201 @@ PyTypeObject _PyNoDefault_Type = {
PyObject _Py_NoDefaultStruct = _PyObject_HEAD_INIT(&_PyNoDefault_Type);
+typedef struct {
+ PyObject_HEAD
+ PyObject *value;
+} constevaluatorobject;
+
+static void
+constevaluator_dealloc(PyObject *self)
+{
+ PyTypeObject *tp = Py_TYPE(self);
+ constevaluatorobject *ce = (constevaluatorobject *)self;
+
+ _PyObject_GC_UNTRACK(self);
+
+ Py_XDECREF(ce->value);
+
+ Py_TYPE(self)->tp_free(self);
+ Py_DECREF(tp);
+}
+
+static int
+constevaluator_traverse(PyObject *self, visitproc visit, void *arg)
+{
+ constevaluatorobject *ce = (constevaluatorobject *)self;
+ Py_VISIT(ce->value);
+ return 0;
+}
+
+static int
+constevaluator_clear(PyObject *self)
+{
+ Py_CLEAR(((constevaluatorobject *)self)->value);
+ return 0;
+}
+
+static PyObject *
+constevaluator_repr(PyObject *self, PyObject *repr)
+{
+ PyObject *value = ((constevaluatorobject *)self)->value;
+ return PyUnicode_FromFormat("", value);
+}
+
+static PyObject *
+constevaluator_call(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ if (!_PyArg_NoKeywords("constevaluator.__call__", kwargs)) {
+ return NULL;
+ }
+ int format;
+ if (!PyArg_ParseTuple(args, "i:constevaluator.__call__", &format)) {
+ return NULL;
+ }
+ PyObject *value = ((constevaluatorobject *)self)->value;
+ if (format == 3) { // SOURCE
+ _PyUnicodeWriter writer;
+ _PyUnicodeWriter_Init(&writer);
+ if (PyTuple_Check(value)) {
+ if (_PyUnicodeWriter_WriteASCIIString(&writer, "(", 1) < 0) {
+ _PyUnicodeWriter_Dealloc(&writer);
+ return NULL;
+ }
+ for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(value); i++) {
+ PyObject *item = PyTuple_GET_ITEM(value, i);
+ if (i > 0) {
+ if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0) {
+ _PyUnicodeWriter_Dealloc(&writer);
+ return NULL;
+ }
+ }
+ if (_Py_typing_type_repr(&writer, item) < 0) {
+ _PyUnicodeWriter_Dealloc(&writer);
+ return NULL;
+ }
+ }
+ if (_PyUnicodeWriter_WriteASCIIString(&writer, ")", 1) < 0) {
+ _PyUnicodeWriter_Dealloc(&writer);
+ return NULL;
+ }
+ }
+ else {
+ if (_Py_typing_type_repr(&writer, value) < 0) {
+ _PyUnicodeWriter_Dealloc(&writer);
+ return NULL;
+ }
+ }
+ return _PyUnicodeWriter_Finish(&writer);
+ }
+ return Py_NewRef(value);
+}
+
+static PyObject *
+constevaluator_alloc(PyObject *value)
+{
+ PyTypeObject *tp = _PyInterpreterState_GET()->cached_objects.constevaluator_type;
+ assert(tp != NULL);
+ constevaluatorobject *ce = PyObject_GC_New(constevaluatorobject, tp);
+ if (ce == NULL) {
+ return NULL;
+ }
+ ce->value = Py_NewRef(value);
+ _PyObject_GC_TRACK(ce);
+ return (PyObject *)ce;
+
+}
+
+PyDoc_STRVAR(constevaluator_doc,
+"_ConstEvaluator()\n"
+"--\n\n"
+"Internal type for implementing evaluation functions.");
+
+static PyType_Slot constevaluator_slots[] = {
+ {Py_tp_doc, (void *)constevaluator_doc},
+ {Py_tp_dealloc, constevaluator_dealloc},
+ {Py_tp_traverse, constevaluator_traverse},
+ {Py_tp_clear, constevaluator_clear},
+ {Py_tp_repr, constevaluator_repr},
+ {Py_tp_call, constevaluator_call},
+ {Py_tp_alloc, PyType_GenericAlloc},
+ {Py_tp_free, PyObject_GC_Del},
+ {0, NULL},
+};
+
+PyType_Spec constevaluator_spec = {
+ .name = "_typing._ConstEvaluator",
+ .basicsize = sizeof(constevaluatorobject),
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE,
+ .slots = constevaluator_slots,
+};
+
+int
+_Py_typing_type_repr(PyUnicodeWriter *writer, PyObject *p)
+{
+ PyObject *qualname = NULL;
+ PyObject *module = NULL;
+ PyObject *r = NULL;
+ int rc;
+
+ if (p == Py_Ellipsis) {
+ // The Ellipsis object
+ r = PyUnicode_FromString("...");
+ goto exit;
+ }
+
+ if (p == (PyObject *)&_PyNone_Type) {
+ return _PyUnicodeWriter_WriteASCIIString(writer, "None", 4);
+ }
+
+ if ((rc = PyObject_HasAttrWithError(p, &_Py_ID(__origin__))) > 0 &&
+ (rc = PyObject_HasAttrWithError(p, &_Py_ID(__args__))) > 0)
+ {
+ // It looks like a GenericAlias
+ goto use_repr;
+ }
+ if (rc < 0) {
+ goto exit;
+ }
+
+ if (PyObject_GetOptionalAttr(p, &_Py_ID(__qualname__), &qualname) < 0) {
+ goto exit;
+ }
+ if (qualname == NULL) {
+ goto use_repr;
+ }
+ if (PyObject_GetOptionalAttr(p, &_Py_ID(__module__), &module) < 0) {
+ goto exit;
+ }
+ if (module == NULL || module == Py_None) {
+ goto use_repr;
+ }
+
+ // Looks like a class
+ if (PyUnicode_Check(module) &&
+ _PyUnicode_EqualToASCIIString(module, "builtins"))
+ {
+ // builtins don't need a module name
+ r = PyObject_Str(qualname);
+ goto exit;
+ }
+ else {
+ r = PyUnicode_FromFormat("%S.%S", module, qualname);
+ goto exit;
+ }
+
+use_repr:
+ r = PyObject_Repr(p);
+exit:
+ Py_XDECREF(qualname);
+ Py_XDECREF(module);
+ if (r == NULL) {
+ return -1;
+ }
+ rc = _PyUnicodeWriter_WriteStr(writer, r);
+ Py_DECREF(r);
+ return rc;
+}
+
static PyObject *
call_typing_func_object(const char *name, PyObject **args, size_t nargs)
@@ -364,10 +559,49 @@ typevar_constraints(typevarobject *self, void *Py_UNUSED(ignored))
return constraints;
}
+static PyObject *
+typevar_evaluate_bound(typevarobject *self, void *Py_UNUSED(ignored))
+{
+ if (self->evaluate_bound != NULL) {
+ return Py_NewRef(self->evaluate_bound);
+ }
+ if (self->bound != NULL) {
+ return constevaluator_alloc(self->bound);
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+typevar_evaluate_constraints(typevarobject *self, void *Py_UNUSED(ignored))
+{
+ if (self->evaluate_constraints != NULL) {
+ return Py_NewRef(self->evaluate_constraints);
+ }
+ if (self->constraints != NULL) {
+ return constevaluator_alloc(self->constraints);
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+typevar_evaluate_default(typevarobject *self, void *Py_UNUSED(ignored))
+{
+ if (self->evaluate_default != NULL) {
+ return Py_NewRef(self->evaluate_default);
+ }
+ if (self->default_value != NULL) {
+ return constevaluator_alloc(self->default_value);
+ }
+ Py_RETURN_NONE;
+}
+
static PyGetSetDef typevar_getset[] = {
{"__bound__", (getter)typevar_bound, NULL, NULL, NULL},
{"__constraints__", (getter)typevar_constraints, NULL, NULL, NULL},
{"__default__", (getter)typevar_default, NULL, NULL, NULL},
+ {"evaluate_bound", (getter)typevar_evaluate_bound, NULL, NULL, NULL},
+ {"evaluate_constraints", (getter)typevar_evaluate_constraints, NULL, NULL, NULL},
+ {"evaluate_default", (getter)typevar_evaluate_default, NULL, NULL, NULL},
{0}
};
@@ -995,10 +1229,23 @@ paramspec_default(paramspecobject *self, void *unused)
return default_value;
}
+static PyObject *
+paramspec_evaluate_default(paramspecobject *self, void *unused)
+{
+ if (self->evaluate_default != NULL) {
+ return Py_NewRef(self->evaluate_default);
+ }
+ if (self->default_value != NULL) {
+ return constevaluator_alloc(self->default_value);
+ }
+ Py_RETURN_NONE;
+}
+
static PyGetSetDef paramspec_getset[] = {
{"args", (getter)paramspec_args, NULL, PyDoc_STR("Represents positional arguments."), NULL},
{"kwargs", (getter)paramspec_kwargs, NULL, PyDoc_STR("Represents keyword arguments."), NULL},
{"__default__", (getter)paramspec_default, NULL, "The default value for this ParamSpec.", NULL},
+ {"evaluate_default", (getter)paramspec_evaluate_default, NULL, NULL, NULL},
{0},
};
@@ -1437,8 +1684,21 @@ typevartuple_default(typevartupleobject *self, void *unused)
return default_value;
}
+static PyObject *
+typevartuple_evaluate_default(typevartupleobject *self, void *unused)
+{
+ if (self->evaluate_default != NULL) {
+ return Py_NewRef(self->evaluate_default);
+ }
+ if (self->default_value != NULL) {
+ return constevaluator_alloc(self->default_value);
+ }
+ Py_RETURN_NONE;
+}
+
static PyGetSetDef typevartuple_getset[] = {
{"__default__", (getter)typevartuple_default, NULL, "The default value for this TypeVarTuple.", NULL},
+ {"evaluate_default", (getter)typevartuple_evaluate_default, NULL, NULL, NULL},
{0},
};
@@ -1584,6 +1844,17 @@ typealias_value(PyObject *self, void *unused)
return typealias_get_value(ta);
}
+static PyObject *
+typealias_evaluate_value(PyObject *self, void *unused)
+{
+ typealiasobject *ta = (typealiasobject *)self;
+ if (ta->compute_value != NULL) {
+ return Py_NewRef(ta->compute_value);
+ }
+ assert(ta->value != NULL);
+ return constevaluator_alloc(ta->value);
+}
+
static PyObject *
typealias_parameters(PyObject *self, void *unused)
{
@@ -1627,6 +1898,7 @@ static PyGetSetDef typealias_getset[] = {
{"__parameters__", typealias_parameters, (setter)NULL, NULL, NULL},
{"__type_params__", typealias_type_params, (setter)NULL, NULL, NULL},
{"__value__", typealias_value, (setter)NULL, NULL, NULL},
+ {"evaluate_value", typealias_evaluate_value, (setter)NULL, NULL, NULL},
{"__module__", typealias_module, (setter)NULL, NULL, NULL},
{0}
};
@@ -1952,6 +2224,7 @@ int _Py_initialize_generic(PyInterpreterState *interp)
MAKE_TYPE(paramspec);
MAKE_TYPE(paramspecargs);
MAKE_TYPE(paramspeckwargs);
+ MAKE_TYPE(constevaluator);
#undef MAKE_TYPE
return 0;
}
@@ -1964,6 +2237,7 @@ void _Py_clear_generic_types(PyInterpreterState *interp)
Py_CLEAR(interp->cached_objects.paramspec_type);
Py_CLEAR(interp->cached_objects.paramspecargs_type);
Py_CLEAR(interp->cached_objects.paramspeckwargs_type);
+ Py_CLEAR(interp->cached_objects.constevaluator_type);
}
PyObject *
diff --git a/Objects/unionobject.c b/Objects/unionobject.c
index 7931f4345f7fdd..6e65a653a95c46 100644
--- a/Objects/unionobject.c
+++ b/Objects/unionobject.c
@@ -1,11 +1,10 @@
// types.UnionType -- used to represent e.g. Union[int, str], int | str
#include "Python.h"
#include "pycore_object.h" // _PyObject_GC_TRACK/UNTRACK
-#include "pycore_typevarobject.h" // _PyTypeAlias_Type
+#include "pycore_typevarobject.h" // _PyTypeAlias_Type, _Py_typing_type_repr
#include "pycore_unionobject.h"
-
static PyObject *make_union(PyObject *);
@@ -181,67 +180,6 @@ _Py_union_type_or(PyObject* self, PyObject* other)
return new_union;
}
-static int
-union_repr_item(PyUnicodeWriter *writer, PyObject *p)
-{
- PyObject *qualname = NULL;
- PyObject *module = NULL;
- int rc;
-
- if (p == (PyObject *)&_PyNone_Type) {
- return PyUnicodeWriter_WriteUTF8(writer, "None", 4);
- }
-
- if ((rc = PyObject_HasAttrWithError(p, &_Py_ID(__origin__))) > 0 &&
- (rc = PyObject_HasAttrWithError(p, &_Py_ID(__args__))) > 0)
- {
- // It looks like a GenericAlias
- goto use_repr;
- }
- if (rc < 0) {
- goto error;
- }
-
- if (PyObject_GetOptionalAttr(p, &_Py_ID(__qualname__), &qualname) < 0) {
- goto error;
- }
- if (qualname == NULL) {
- goto use_repr;
- }
- if (PyObject_GetOptionalAttr(p, &_Py_ID(__module__), &module) < 0) {
- goto error;
- }
- if (module == NULL || module == Py_None) {
- goto use_repr;
- }
-
- // Looks like a class
- if (PyUnicode_Check(module) &&
- _PyUnicode_EqualToASCIIString(module, "builtins"))
- {
- // builtins don't need a module name
- rc = PyUnicodeWriter_WriteStr(writer, qualname);
- goto done;
- }
- else {
- rc = PyUnicodeWriter_Format(writer, "%S.%S", module, qualname);
- goto done;
- }
-
-error:
- rc = -1;
- goto done;
-
-use_repr:
- rc = PyUnicodeWriter_WriteRepr(writer, p);
- goto done;
-
-done:
- Py_XDECREF(qualname);
- Py_XDECREF(module);
- return rc;
-}
-
static PyObject *
union_repr(PyObject *self)
{
@@ -260,7 +198,7 @@ union_repr(PyObject *self)
goto error;
}
PyObject *p = PyTuple_GET_ITEM(alias->args, i);
- if (union_repr_item(writer, p) < 0) {
+ if (_Py_typing_type_repr(writer, p) < 0) {
goto error;
}
}
diff --git a/Python/compile.c b/Python/compile.c
index d07a435bdf8dac..02b5345cedd0a3 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -1978,8 +1978,9 @@ compiler_type_param_bound_or_default(struct compiler *c, expr_ty e,
identifier name, void *key,
bool allow_starred)
{
- if (compiler_enter_scope(c, name, COMPILER_SCOPE_ANNOTATIONS,
- key, e->lineno, NULL) == -1) {
+ PyObject *defaults = PyTuple_Pack(1, _PyLong_GetOne());
+ ADDOP_LOAD_CONST_NEW(c, LOC(e), defaults);
+ if (compiler_setup_annotations_scope(c, LOC(e), key, name) == -1) {
return ERROR;
}
if (allow_starred && e->kind == Starred_kind) {
@@ -1995,7 +1996,7 @@ compiler_type_param_bound_or_default(struct compiler *c, expr_ty e,
if (co == NULL) {
return ERROR;
}
- if (compiler_make_closure(c, LOC(e), co, 0) < 0) {
+ if (compiler_make_closure(c, LOC(e), co, MAKE_FUNCTION_DEFAULTS) < 0) {
Py_DECREF(co);
return ERROR;
}
@@ -2566,8 +2567,10 @@ compiler_typealias_body(struct compiler *c, stmt_ty s)
{
location loc = LOC(s);
PyObject *name = s->v.TypeAlias.name->v.Name.id;
+ PyObject *defaults = PyTuple_Pack(1, _PyLong_GetOne());
+ ADDOP_LOAD_CONST_NEW(c, loc, defaults);
RETURN_IF_ERROR(
- compiler_enter_scope(c, name, COMPILER_SCOPE_FUNCTION, s, loc.lineno, NULL));
+ compiler_setup_annotations_scope(c, LOC(s), s, name));
/* Make None the first constant, so the evaluate function can't have a
docstring. */
RETURN_IF_ERROR(compiler_add_const(c, Py_None));
@@ -2578,7 +2581,7 @@ compiler_typealias_body(struct compiler *c, stmt_ty s)
if (co == NULL) {
return ERROR;
}
- if (compiler_make_closure(c, loc, co, 0) < 0) {
+ if (compiler_make_closure(c, loc, co, MAKE_FUNCTION_DEFAULTS) < 0) {
Py_DECREF(co);
return ERROR;
}
diff --git a/Python/symtable.c b/Python/symtable.c
index a5fa7588785d8b..88af37198bfba5 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -260,6 +260,7 @@ static int symtable_visit_pattern(struct symtable *st, pattern_ty s);
static int symtable_raise_if_annotation_block(struct symtable *st, const char *, expr_ty);
static int symtable_raise_if_not_coroutine(struct symtable *st, const char *msg, _Py_SourceLocation loc);
static int symtable_raise_if_comprehension_block(struct symtable *st, expr_ty);
+static int symtable_add_def(struct symtable *st, PyObject *name, int flag, _Py_SourceLocation loc);
/* For debugging purposes only */
#if _PY_DUMP_SYMTABLE
@@ -1388,6 +1389,16 @@ symtable_enter_block(struct symtable *st, identifier name, _Py_block_ty block,
return 0;
int result = symtable_enter_existing_block(st, ste);
Py_DECREF(ste);
+ if (block == AnnotationBlock || block == TypeVariableBlock || block == TypeAliasBlock) {
+ _Py_DECLARE_STR(format, ".format");
+ // We need to insert code that reads this "parameter" to the function.
+ if (!symtable_add_def(st, &_Py_STR(format), DEF_PARAM, loc)) {
+ return 0;
+ }
+ if (!symtable_add_def(st, &_Py_STR(format), USE, loc)) {
+ return 0;
+ }
+ }
return result;
}
@@ -2630,18 +2641,6 @@ symtable_visit_annotation(struct symtable *st, expr_ty annotation, void *key)
return 0;
}
}
-
- _Py_DECLARE_STR(format, ".format");
- // The generated __annotate__ function takes a single parameter with the
- // internal name ".format".
- if (!symtable_add_def(st, &_Py_STR(format), DEF_PARAM,
- LOCATION(annotation))) {
- return 0;
- }
- if (!symtable_add_def(st, &_Py_STR(format), USE,
- LOCATION(annotation))) {
- return 0;
- }
}
else {
if (!symtable_enter_existing_block(st, parent_ste->ste_annotation_block)) {
@@ -2690,14 +2689,6 @@ symtable_visit_annotations(struct symtable *st, stmt_ty o, arguments_ty a, expr_
return 0;
}
}
- _Py_DECLARE_STR(format, ".format");
- // We need to insert code that reads this "parameter" to the function.
- if (!symtable_add_def(st, &_Py_STR(format), DEF_PARAM, LOCATION(o))) {
- return 0;
- }
- if (!symtable_add_def(st, &_Py_STR(format), USE, LOCATION(o))) {
- return 0;
- }
if (a->posonlyargs && !symtable_visit_argannotations(st, a->posonlyargs))
return 0;
if (a->args && !symtable_visit_argannotations(st, a->args))
From 04eb5c8db1e24cabd0cb81392bb2632c03be1550 Mon Sep 17 00:00:00 2001
From: sobolevn
Date: Sat, 27 Jul 2024 21:33:38 +0300
Subject: [PATCH 39/47] gh-122361: Use proper `PyUnicodeWriter_*` API in
`constevaluator_call` (#122362)
---
Objects/typevarobject.c | 32 +++++++++++++++++---------------
1 file changed, 17 insertions(+), 15 deletions(-)
diff --git a/Objects/typevarobject.c b/Objects/typevarobject.c
index fb1f260571b582..3c96850589d378 100644
--- a/Objects/typevarobject.c
+++ b/Objects/typevarobject.c
@@ -169,38 +169,40 @@ constevaluator_call(PyObject *self, PyObject *args, PyObject *kwargs)
}
PyObject *value = ((constevaluatorobject *)self)->value;
if (format == 3) { // SOURCE
- _PyUnicodeWriter writer;
- _PyUnicodeWriter_Init(&writer);
+ PyUnicodeWriter *writer = PyUnicodeWriter_Create(5); // cannot be <5
+ if (writer == NULL) {
+ return NULL;
+ }
if (PyTuple_Check(value)) {
- if (_PyUnicodeWriter_WriteASCIIString(&writer, "(", 1) < 0) {
- _PyUnicodeWriter_Dealloc(&writer);
+ if (PyUnicodeWriter_WriteChar(writer, '(') < 0) {
+ PyUnicodeWriter_Discard(writer);
return NULL;
}
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(value); i++) {
PyObject *item = PyTuple_GET_ITEM(value, i);
if (i > 0) {
- if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0) {
- _PyUnicodeWriter_Dealloc(&writer);
+ if (PyUnicodeWriter_WriteUTF8(writer, ", ", 2) < 0) {
+ PyUnicodeWriter_Discard(writer);
return NULL;
}
}
- if (_Py_typing_type_repr(&writer, item) < 0) {
- _PyUnicodeWriter_Dealloc(&writer);
+ if (_Py_typing_type_repr(writer, item) < 0) {
+ PyUnicodeWriter_Discard(writer);
return NULL;
}
}
- if (_PyUnicodeWriter_WriteASCIIString(&writer, ")", 1) < 0) {
- _PyUnicodeWriter_Dealloc(&writer);
+ if (PyUnicodeWriter_WriteChar(writer, ')') < 0) {
+ PyUnicodeWriter_Discard(writer);
return NULL;
}
}
else {
- if (_Py_typing_type_repr(&writer, value) < 0) {
- _PyUnicodeWriter_Dealloc(&writer);
+ if (_Py_typing_type_repr(writer, value) < 0) {
+ PyUnicodeWriter_Discard(writer);
return NULL;
}
}
- return _PyUnicodeWriter_Finish(&writer);
+ return PyUnicodeWriter_Finish(writer);
}
return Py_NewRef(value);
}
@@ -259,7 +261,7 @@ _Py_typing_type_repr(PyUnicodeWriter *writer, PyObject *p)
}
if (p == (PyObject *)&_PyNone_Type) {
- return _PyUnicodeWriter_WriteASCIIString(writer, "None", 4);
+ return PyUnicodeWriter_WriteUTF8(writer, "None", 4);
}
if ((rc = PyObject_HasAttrWithError(p, &_Py_ID(__origin__))) > 0 &&
@@ -306,7 +308,7 @@ _Py_typing_type_repr(PyUnicodeWriter *writer, PyObject *p)
if (r == NULL) {
return -1;
}
- rc = _PyUnicodeWriter_WriteStr(writer, r);
+ rc = PyUnicodeWriter_WriteStr(writer, r);
Py_DECREF(r);
return rc;
}
From 3ff5ce4706630207bb2c2e2589a4501bf0d1bd78 Mon Sep 17 00:00:00 2001
From: Jelle Zijlstra
Date: Sun, 28 Jul 2024 00:50:14 -0700
Subject: [PATCH 40/47] gh-119180: Add myself as CODEOWNER for annotationlib
(#122366)
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
---
.github/CODEOWNERS | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 95e30ac3001c9c..9aa5004b0cdb7f 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -214,6 +214,7 @@ Doc/c-api/stable.rst @encukou
**/*idlelib* @terryjreedy
/Doc/library/idle.rst @terryjreedy
+**/*annotationlib* @JelleZijlstra
**/*typing* @JelleZijlstra @AlexWaygood
**/*ftplib @giampaolo
From aa449cf063581ea515e2a6194d175f5e1db3d62e Mon Sep 17 00:00:00 2001
From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
Date: Sun, 28 Jul 2024 10:53:21 +0300
Subject: [PATCH 41/47] gh-122085: Create dedicated page for deprecations
(#122352)
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
---
Doc/contents.rst | 1 +
Doc/deprecations/index.rst | 10 ++++++++++
Doc/tools/templates/indexcontent.html | 2 ++
3 files changed, 13 insertions(+)
create mode 100644 Doc/deprecations/index.rst
diff --git a/Doc/contents.rst b/Doc/contents.rst
index 24ceacb0076b5e..b57f4b09a5dcb6 100644
--- a/Doc/contents.rst
+++ b/Doc/contents.rst
@@ -14,6 +14,7 @@
installing/index.rst
howto/index.rst
faq/index.rst
+ deprecations/index.rst
glossary.rst
about.rst
diff --git a/Doc/deprecations/index.rst b/Doc/deprecations/index.rst
new file mode 100644
index 00000000000000..cfb30dd09aef6f
--- /dev/null
+++ b/Doc/deprecations/index.rst
@@ -0,0 +1,10 @@
+Deprecations
+============
+
+.. include:: pending-removal-in-3.14.rst
+
+.. include:: pending-removal-in-3.15.rst
+
+.. include:: pending-removal-in-3.16.rst
+
+.. include:: pending-removal-in-future.rst
diff --git a/Doc/tools/templates/indexcontent.html b/Doc/tools/templates/indexcontent.html
index 6f854e86ab8ef1..f2e9fbb0106452 100644
--- a/Doc/tools/templates/indexcontent.html
+++ b/Doc/tools/templates/indexcontent.html
@@ -33,6 +33,8 @@ {{ docstitle|e }}
{% trans %}C API reference{% endtrans %}
{% trans %}FAQs{% endtrans %}
{% trans %}Frequently asked questions (with answers!){% endtrans %}
+ {% trans %}Deprecations{% endtrans %}
+ {% trans %}Deprecated functionality{% endtrans %}
From b359f66c4c315ca14b2a075ee136145ba6610760 Mon Sep 17 00:00:00 2001
From: Victor Stinner
Date: Sun, 28 Jul 2024 09:59:07 +0200
Subject: [PATCH 42/47] gh-120593: Make _PyLong_CompactValue() parameter const
again (#122367)
Change _PyLong_IsCompact() and _PyLong_CompactValue() parameter type
from 'PyObject*' to 'const PyObject*'. Avoid the Py_TYPE() macro
which does not support const parameter.
---
Include/cpython/longintrepr.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/Include/cpython/longintrepr.h b/Include/cpython/longintrepr.h
index d841c043f37fc4..c60ccc463653f9 100644
--- a/Include/cpython/longintrepr.h
+++ b/Include/cpython/longintrepr.h
@@ -119,18 +119,18 @@ PyAPI_FUNC(PyLongObject*) _PyLong_FromDigits(
static inline int
-_PyLong_IsCompact(PyLongObject* op) {
- assert(PyType_HasFeature(Py_TYPE(op), Py_TPFLAGS_LONG_SUBCLASS));
+_PyLong_IsCompact(const PyLongObject* op) {
+ assert(PyType_HasFeature(op->ob_base.ob_type, Py_TPFLAGS_LONG_SUBCLASS));
return op->long_value.lv_tag < (2 << _PyLong_NON_SIZE_BITS);
}
#define PyUnstable_Long_IsCompact _PyLong_IsCompact
static inline Py_ssize_t
-_PyLong_CompactValue(PyLongObject *op)
+_PyLong_CompactValue(const PyLongObject *op)
{
Py_ssize_t sign;
- assert(PyType_HasFeature(Py_TYPE(op), Py_TPFLAGS_LONG_SUBCLASS));
+ assert(PyType_HasFeature(op->ob_base.ob_type, Py_TPFLAGS_LONG_SUBCLASS));
assert(PyUnstable_Long_IsCompact(op));
sign = 1 - (op->long_value.lv_tag & _PyLong_SIGN_MASK);
return sign * (Py_ssize_t)op->long_value.ob_digit[0];
From bc93923a2dee00751e44da58b6967c63e3f5c392 Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka
Date: Sun, 28 Jul 2024 11:33:17 +0300
Subject: [PATCH 43/47] gh-122311: Add more tests for pickle (GH-122376)
---
Lib/test/pickletester.py | 770 ++++++++++++++++++++++++++++++++-------
Lib/test/test_pickle.py | 18 +
2 files changed, 660 insertions(+), 128 deletions(-)
diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py
index 13663220fc77ea..174f4ff6d021b2 100644
--- a/Lib/test/pickletester.py
+++ b/Lib/test/pickletester.py
@@ -144,6 +144,14 @@ class E(C):
def __getinitargs__(self):
return ()
+import __main__
+__main__.C = C
+C.__module__ = "__main__"
+__main__.D = D
+D.__module__ = "__main__"
+__main__.E = E
+E.__module__ = "__main__"
+
# Simple mutable object.
class Object:
pass
@@ -157,14 +165,6 @@ def __reduce__(self):
# Shouldn't support the recursion itself
return K, (self.value,)
-import __main__
-__main__.C = C
-C.__module__ = "__main__"
-__main__.D = D
-D.__module__ = "__main__"
-__main__.E = E
-E.__module__ = "__main__"
-
class myint(int):
def __init__(self, x):
self.str = str(x)
@@ -1179,6 +1179,124 @@ def test_compat_unpickle(self):
self.assertIs(type(unpickled), collections.UserDict)
self.assertEqual(unpickled, collections.UserDict({1: 2}))
+ def test_load_global(self):
+ self.assertIs(self.loads(b'cbuiltins\nstr\n.'), str)
+ self.assertIs(self.loads(b'cmath\nlog\n.'), math.log)
+ self.assertIs(self.loads(b'cos.path\njoin\n.'), os.path.join)
+ self.assertIs(self.loads(b'\x80\x04cbuiltins\nstr.upper\n.'), str.upper)
+ with support.swap_item(sys.modules, 'mödule', types.SimpleNamespace(glöbal=42)):
+ self.assertEqual(self.loads(b'\x80\x04cm\xc3\xb6dule\ngl\xc3\xb6bal\n.'), 42)
+
+ self.assertRaises(UnicodeDecodeError, self.loads, b'c\xff\nlog\n.')
+ self.assertRaises(UnicodeDecodeError, self.loads, b'cmath\n\xff\n.')
+ self.assertRaises(self.truncated_errors, self.loads, b'c\nlog\n.')
+ self.assertRaises(self.truncated_errors, self.loads, b'cmath\n\n.')
+ self.assertRaises(self.truncated_errors, self.loads, b'\x80\x04cmath\n\n.')
+
+ def test_load_stack_global(self):
+ self.assertIs(self.loads(b'\x8c\x08builtins\x8c\x03str\x93.'), str)
+ self.assertIs(self.loads(b'\x8c\x04math\x8c\x03log\x93.'), math.log)
+ self.assertIs(self.loads(b'\x8c\x07os.path\x8c\x04join\x93.'),
+ os.path.join)
+ self.assertIs(self.loads(b'\x80\x04\x8c\x08builtins\x8c\x09str.upper\x93.'),
+ str.upper)
+ with support.swap_item(sys.modules, 'mödule', types.SimpleNamespace(glöbal=42)):
+ self.assertEqual(self.loads(b'\x80\x04\x8c\x07m\xc3\xb6dule\x8c\x07gl\xc3\xb6bal\x93.'), 42)
+
+ self.assertRaises(UnicodeDecodeError, self.loads, b'\x8c\x01\xff\x8c\x03log\x93.')
+ self.assertRaises(UnicodeDecodeError, self.loads, b'\x8c\x04math\x8c\x01\xff\x93.')
+ self.assertRaises(ValueError, self.loads, b'\x8c\x00\x8c\x03log\x93.')
+ self.assertRaises(AttributeError, self.loads, b'\x8c\x04math\x8c\x00\x93.')
+ self.assertRaises(AttributeError, self.loads, b'\x80\x04\x8c\x04math\x8c\x00\x93.')
+
+ self.assertRaises(pickle.UnpicklingError, self.loads, b'N\x8c\x03log\x93.')
+ self.assertRaises(pickle.UnpicklingError, self.loads, b'\x8c\x04mathN\x93.')
+ self.assertRaises(pickle.UnpicklingError, self.loads, b'\x80\x04\x8c\x04mathN\x93.')
+
+ def test_find_class(self):
+ unpickler = self.unpickler(io.BytesIO())
+ unpickler_nofix = self.unpickler(io.BytesIO(), fix_imports=False)
+ unpickler4 = self.unpickler(io.BytesIO(b'\x80\x04N.'))
+ unpickler4.load()
+
+ self.assertIs(unpickler.find_class('__builtin__', 'str'), str)
+ self.assertRaises(ModuleNotFoundError,
+ unpickler_nofix.find_class, '__builtin__', 'str')
+ self.assertIs(unpickler.find_class('builtins', 'str'), str)
+ self.assertIs(unpickler_nofix.find_class('builtins', 'str'), str)
+ self.assertIs(unpickler.find_class('math', 'log'), math.log)
+ self.assertIs(unpickler.find_class('os.path', 'join'), os.path.join)
+ self.assertIs(unpickler.find_class('os.path', 'join'), os.path.join)
+
+ self.assertIs(unpickler4.find_class('builtins', 'str.upper'), str.upper)
+ with self.assertRaises(AttributeError):
+ unpickler.find_class('builtins', 'str.upper')
+
+ with self.assertRaises(AttributeError):
+ unpickler.find_class('math', 'spam')
+ with self.assertRaises(AttributeError):
+ unpickler4.find_class('math', 'spam')
+ with self.assertRaises(AttributeError):
+ unpickler.find_class('math', 'log.spam')
+ with self.assertRaises(AttributeError):
+ unpickler4.find_class('math', 'log.spam')
+ with self.assertRaises(AttributeError):
+ unpickler.find_class('math', 'log..spam')
+ with self.assertRaises(AttributeError):
+ unpickler4.find_class('math', 'log..spam')
+ with self.assertRaises(AttributeError):
+ unpickler.find_class('math', '')
+ with self.assertRaises(AttributeError):
+ unpickler4.find_class('math', '')
+ self.assertRaises(ModuleNotFoundError, unpickler.find_class, 'spam', 'log')
+ self.assertRaises(ValueError, unpickler.find_class, '', 'log')
+
+ self.assertRaises(TypeError, unpickler.find_class, None, 'log')
+ self.assertRaises(TypeError, unpickler.find_class, 'math', None)
+ self.assertRaises((TypeError, AttributeError), unpickler4.find_class, 'math', None)
+
+ def test_custom_find_class(self):
+ def loads(data):
+ class Unpickler(self.unpickler):
+ def find_class(self, module_name, global_name):
+ return (module_name, global_name)
+ return Unpickler(io.BytesIO(data)).load()
+
+ self.assertEqual(loads(b'cmath\nlog\n.'), ('math', 'log'))
+ self.assertEqual(loads(b'\x8c\x04math\x8c\x03log\x93.'), ('math', 'log'))
+
+ def loads(data):
+ class Unpickler(self.unpickler):
+ @staticmethod
+ def find_class(module_name, global_name):
+ return (module_name, global_name)
+ return Unpickler(io.BytesIO(data)).load()
+
+ self.assertEqual(loads(b'cmath\nlog\n.'), ('math', 'log'))
+ self.assertEqual(loads(b'\x8c\x04math\x8c\x03log\x93.'), ('math', 'log'))
+
+ def loads(data):
+ class Unpickler(self.unpickler):
+ @classmethod
+ def find_class(cls, module_name, global_name):
+ return (module_name, global_name)
+ return Unpickler(io.BytesIO(data)).load()
+
+ self.assertEqual(loads(b'cmath\nlog\n.'), ('math', 'log'))
+ self.assertEqual(loads(b'\x8c\x04math\x8c\x03log\x93.'), ('math', 'log'))
+
+ def loads(data):
+ class Unpickler(self.unpickler):
+ pass
+ def find_class(module_name, global_name):
+ return (module_name, global_name)
+ unpickler = Unpickler(io.BytesIO(data))
+ unpickler.find_class = find_class
+ return unpickler.load()
+
+ self.assertEqual(loads(b'cmath\nlog\n.'), ('math', 'log'))
+ self.assertEqual(loads(b'\x8c\x04math\x8c\x03log\x93.'), ('math', 'log'))
+
def test_bad_reduce(self):
self.assertEqual(self.loads(b'cbuiltins\nint\n)R.'), 0)
self.check_unpickling_error(TypeError, b'N)R.')
@@ -1443,6 +1561,474 @@ def t():
[ToBeUnpickled] * 2)
+class AbstractPicklingErrorTests:
+ # Subclass must define self.dumps, self.pickler.
+
+ def test_bad_reduce_result(self):
+ obj = REX([print, ()])
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ obj = REX((print,))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ obj = REX((print, (), None, None, None, None, None))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ def test_bad_reconstructor(self):
+ obj = REX((42, ()))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ def test_unpickleable_reconstructor(self):
+ obj = REX((UnpickleableCallable(), ()))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+
+ def test_bad_reconstructor_args(self):
+ obj = REX((print, []))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ def test_unpickleable_reconstructor_args(self):
+ obj = REX((print, (1, 2, UNPICKLEABLE)))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+
+ def test_bad_newobj_args(self):
+ obj = REX((copyreg.__newobj__, ()))
+ for proto in protocols[2:]:
+ with self.subTest(proto=proto):
+ with self.assertRaises((IndexError, pickle.PicklingError)) as cm:
+ self.dumps(obj, proto)
+
+ obj = REX((copyreg.__newobj__, [REX]))
+ for proto in protocols[2:]:
+ with self.subTest(proto=proto):
+ with self.assertRaises((IndexError, pickle.PicklingError)):
+ self.dumps(obj, proto)
+
+ def test_bad_newobj_class(self):
+ obj = REX((copyreg.__newobj__, (NoNew(),)))
+ for proto in protocols[2:]:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ def test_wrong_newobj_class(self):
+ obj = REX((copyreg.__newobj__, (str,)))
+ for proto in protocols[2:]:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ def test_unpickleable_newobj_class(self):
+ class LocalREX(REX): pass
+ obj = LocalREX((copyreg.__newobj__, (LocalREX,)))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises((pickle.PicklingError, AttributeError)):
+ self.dumps(obj, proto)
+
+ def test_unpickleable_newobj_args(self):
+ obj = REX((copyreg.__newobj__, (REX, 1, 2, UNPICKLEABLE)))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+
+ def test_bad_newobj_ex_args(self):
+ obj = REX((copyreg.__newobj_ex__, ()))
+ for proto in protocols[2:]:
+ with self.subTest(proto=proto):
+ with self.assertRaises((ValueError, pickle.PicklingError)):
+ self.dumps(obj, proto)
+
+ obj = REX((copyreg.__newobj_ex__, 42))
+ for proto in protocols[2:]:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ obj = REX((copyreg.__newobj_ex__, (REX, 42, {})))
+ is_py = self.pickler is pickle._Pickler
+ for proto in protocols[2:4] if is_py else protocols[2:]:
+ with self.subTest(proto=proto):
+ with self.assertRaises((TypeError, pickle.PicklingError)):
+ self.dumps(obj, proto)
+
+ obj = REX((copyreg.__newobj_ex__, (REX, (), [])))
+ for proto in protocols[2:4] if is_py else protocols[2:]:
+ with self.subTest(proto=proto):
+ with self.assertRaises((TypeError, pickle.PicklingError)):
+ self.dumps(obj, proto)
+
+ def test_bad_newobj_ex__class(self):
+ obj = REX((copyreg.__newobj_ex__, (NoNew(), (), {})))
+ for proto in protocols[2:]:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ def test_wrong_newobj_ex_class(self):
+ if self.pickler is not pickle._Pickler:
+ self.skipTest('only verified in the Python implementation')
+ obj = REX((copyreg.__newobj_ex__, (str, (), {})))
+ for proto in protocols[2:]:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ def test_unpickleable_newobj_ex_class(self):
+ class LocalREX(REX): pass
+ obj = LocalREX((copyreg.__newobj_ex__, (LocalREX, (), {})))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises((pickle.PicklingError, AttributeError)):
+ self.dumps(obj, proto)
+
+ def test_unpickleable_newobj_ex_args(self):
+ obj = REX((copyreg.__newobj_ex__, (REX, (1, 2, UNPICKLEABLE), {})))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+
+ def test_unpickleable_newobj_ex_kwargs(self):
+ obj = REX((copyreg.__newobj_ex__, (REX, (), {'a': UNPICKLEABLE})))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+
+ def test_unpickleable_state(self):
+ obj = REX_state(UNPICKLEABLE)
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+
+ def test_bad_state_setter(self):
+ if self.pickler is pickle._Pickler:
+ self.skipTest('only verified in the C implementation')
+ obj = REX((print, (), 'state', None, None, 42))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ def test_unpickleable_state_setter(self):
+ obj = REX((print, (), 'state', None, None, UnpickleableCallable()))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+
+ def test_unpickleable_state_with_state_setter(self):
+ obj = REX((print, (), UNPICKLEABLE, None, None, print))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+
+ def test_bad_object_list_items(self):
+ # Issue4176: crash when 4th and 5th items of __reduce__()
+ # are not iterators
+ obj = REX((list, (), None, 42))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises((TypeError, pickle.PicklingError)):
+ self.dumps(obj, proto)
+
+ if self.pickler is not pickle._Pickler:
+ # Python implementation is less strict and also accepts iterables.
+ obj = REX((list, (), None, []))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises((TypeError, pickle.PicklingError)):
+ self.dumps(obj, proto)
+
+ def test_unpickleable_object_list_items(self):
+ obj = REX_six([1, 2, UNPICKLEABLE])
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+
+ def test_bad_object_dict_items(self):
+ # Issue4176: crash when 4th and 5th items of __reduce__()
+ # are not iterators
+ obj = REX((dict, (), None, None, 42))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises((TypeError, pickle.PicklingError)):
+ self.dumps(obj, proto)
+
+ for proto in protocols:
+ obj = REX((dict, (), None, None, iter([('a',)])))
+ with self.subTest(proto=proto):
+ with self.assertRaises((ValueError, TypeError)):
+ self.dumps(obj, proto)
+
+ if self.pickler is not pickle._Pickler:
+ # Python implementation is less strict and also accepts iterables.
+ obj = REX((dict, (), None, None, []))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises((TypeError, pickle.PicklingError)):
+ self.dumps(obj, proto)
+
+ def test_unpickleable_object_dict_items(self):
+ obj = REX_seven({'a': UNPICKLEABLE})
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+
+ def test_unpickleable_list_items(self):
+ obj = [1, [2, 3, UNPICKLEABLE]]
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+ for n in [0, 1, 1000, 1005]:
+ obj = [*range(n), UNPICKLEABLE]
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+
+ def test_unpickleable_tuple_items(self):
+ obj = (1, (2, 3, UNPICKLEABLE))
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+ obj = (*range(10), UNPICKLEABLE)
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+
+ def test_unpickleable_dict_items(self):
+ obj = {'a': {'b': UNPICKLEABLE}}
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+ for n in [0, 1, 1000, 1005]:
+ obj = dict.fromkeys(range(n))
+ obj['a'] = UNPICKLEABLE
+ for proto in protocols:
+ with self.subTest(proto=proto, n=n):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+
+ def test_unpickleable_set_items(self):
+ obj = {UNPICKLEABLE}
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+
+ def test_unpickleable_frozenset_items(self):
+ obj = frozenset({frozenset({UNPICKLEABLE})})
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(CustomError):
+ self.dumps(obj, proto)
+
+ def test_global_lookup_error(self):
+ # Global name does not exist
+ obj = REX('spam')
+ obj.__module__ = __name__
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ obj.__module__ = 'nonexisting'
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ obj.__module__ = ''
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises((ValueError, pickle.PicklingError)):
+ self.dumps(obj, proto)
+
+ obj.__module__ = None
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ def test_nonencodable_global_name_error(self):
+ for proto in protocols[:4]:
+ with self.subTest(proto=proto):
+ name = 'nonascii\xff' if proto < 3 else 'nonencodable\udbff'
+ obj = REX(name)
+ obj.__module__ = __name__
+ with support.swap_item(globals(), name, obj):
+ with self.assertRaises((UnicodeEncodeError, pickle.PicklingError)):
+ self.dumps(obj, proto)
+
+ def test_nonencodable_module_name_error(self):
+ for proto in protocols[:4]:
+ with self.subTest(proto=proto):
+ name = 'nonascii\xff' if proto < 3 else 'nonencodable\udbff'
+ obj = REX('test')
+ obj.__module__ = name
+ mod = types.SimpleNamespace(test=obj)
+ with support.swap_item(sys.modules, name, mod):
+ with self.assertRaises((UnicodeEncodeError, pickle.PicklingError)):
+ self.dumps(obj, proto)
+
+ def test_nested_lookup_error(self):
+ # Nested name does not exist
+ obj = REX('AbstractPickleTests.spam')
+ obj.__module__ = __name__
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ obj.__module__ = None
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ def test_wrong_object_lookup_error(self):
+ # Name is bound to different object
+ obj = REX('AbstractPickleTests')
+ obj.__module__ = __name__
+ AbstractPickleTests.ham = []
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ obj.__module__ = None
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(obj, proto)
+
+ def test_local_lookup_error(self):
+ # Test that whichmodule() errors out cleanly when looking up
+ # an assumed globally-reachable object fails.
+ def f():
+ pass
+ # Since the function is local, lookup will fail
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises((AttributeError, pickle.PicklingError)):
+ self.dumps(f, proto)
+ # Same without a __module__ attribute (exercises a different path
+ # in _pickle.c).
+ del f.__module__
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises((AttributeError, pickle.PicklingError)):
+ self.dumps(f, proto)
+ # Yet a different path.
+ f.__name__ = f.__qualname__
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ with self.assertRaises((AttributeError, pickle.PicklingError)):
+ self.dumps(f, proto)
+
+ def test_reduce_ex_None(self):
+ c = REX_None()
+ with self.assertRaises(TypeError):
+ self.dumps(c)
+
+ def test_reduce_None(self):
+ c = R_None()
+ with self.assertRaises(TypeError):
+ self.dumps(c)
+
+ @no_tracing
+ def test_bad_getattr(self):
+ # Issue #3514: crash when there is an infinite loop in __getattr__
+ x = BadGetattr()
+ for proto in range(2):
+ with support.infinite_recursion(25):
+ self.assertRaises(RuntimeError, self.dumps, x, proto)
+ for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
+ s = self.dumps(x, proto)
+
+ def test_picklebuffer_error(self):
+ # PickleBuffer forbidden with protocol < 5
+ pb = pickle.PickleBuffer(b"foobar")
+ for proto in range(0, 5):
+ with self.subTest(proto=proto):
+ with self.assertRaises(pickle.PickleError):
+ self.dumps(pb, proto)
+
+ def test_non_continuous_buffer(self):
+ if self.pickler is pickle._Pickler:
+ self.skipTest('CRASHES (see gh-122306)')
+ for proto in protocols[5:]:
+ with self.subTest(proto=proto):
+ pb = pickle.PickleBuffer(memoryview(b"foobar")[::2])
+ with self.assertRaises(pickle.PicklingError):
+ self.dumps(pb, proto)
+
+ def test_buffer_callback_error(self):
+ def buffer_callback(buffers):
+ raise CustomError
+ pb = pickle.PickleBuffer(b"foobar")
+ with self.assertRaises(CustomError):
+ self.dumps(pb, 5, buffer_callback=buffer_callback)
+
+ def test_evil_pickler_mutating_collection(self):
+ # https://github.com/python/cpython/issues/92930
+ global Clearer
+ class Clearer:
+ pass
+
+ def check(collection):
+ class EvilPickler(self.pickler):
+ def persistent_id(self, obj):
+ if isinstance(obj, Clearer):
+ collection.clear()
+ return None
+ pickler = EvilPickler(io.BytesIO(), proto)
+ try:
+ pickler.dump(collection)
+ except RuntimeError as e:
+ expected = "changed size during iteration"
+ self.assertIn(expected, str(e))
+
+ for proto in protocols:
+ check([Clearer()])
+ check([Clearer(), Clearer()])
+ check({Clearer()})
+ check({Clearer(), Clearer()})
+ check({Clearer(): 1})
+ check({Clearer(): 1, Clearer(): 2})
+ check({1: Clearer(), 2: Clearer()})
+
class AbstractPickleTests:
# Subclass must define self.dumps, self.loads.
@@ -2453,55 +3039,12 @@ def test_reduce_calls_base(self):
y = self.loads(s)
self.assertEqual(y._reduce_called, 1)
- def test_reduce_ex_None(self):
- c = REX_None()
- with self.assertRaises(TypeError):
- self.dumps(c)
-
- def test_reduce_None(self):
- c = R_None()
- with self.assertRaises(TypeError):
- self.dumps(c)
-
def test_pickle_setstate_None(self):
c = C_None_setstate()
p = self.dumps(c)
with self.assertRaises(TypeError):
self.loads(p)
- @no_tracing
- def test_bad_getattr(self):
- # Issue #3514: crash when there is an infinite loop in __getattr__
- x = BadGetattr()
- for proto in range(2):
- with support.infinite_recursion(25):
- self.assertRaises(RuntimeError, self.dumps, x, proto)
- for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
- s = self.dumps(x, proto)
-
- def test_reduce_bad_iterator(self):
- # Issue4176: crash when 4th and 5th items of __reduce__()
- # are not iterators
- class C(object):
- def __reduce__(self):
- # 4th item is not an iterator
- return list, (), None, [], None
- class D(object):
- def __reduce__(self):
- # 5th item is not an iterator
- return dict, (), None, None, []
-
- # Python implementation is less strict and also accepts iterables.
- for proto in protocols:
- try:
- self.dumps(C(), proto)
- except pickle.PicklingError:
- pass
- try:
- self.dumps(D(), proto)
- except pickle.PicklingError:
- pass
-
def test_many_puts_and_gets(self):
# Test that internal data structures correctly deal with lots of
# puts/gets.
@@ -2950,27 +3493,6 @@ def test_compat_pickle(self):
self.assertIn(('c%s\n%s' % (mod, name)).encode(), pickled)
self.assertIs(type(self.loads(pickled)), type(val))
- def test_local_lookup_error(self):
- # Test that whichmodule() errors out cleanly when looking up
- # an assumed globally-reachable object fails.
- def f():
- pass
- # Since the function is local, lookup will fail
- for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
- with self.assertRaises((AttributeError, pickle.PicklingError)):
- pickletools.dis(self.dumps(f, proto))
- # Same without a __module__ attribute (exercises a different path
- # in _pickle.c).
- del f.__module__
- for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
- with self.assertRaises((AttributeError, pickle.PicklingError)):
- pickletools.dis(self.dumps(f, proto))
- # Yet a different path.
- f.__name__ = f.__qualname__
- for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
- with self.assertRaises((AttributeError, pickle.PicklingError)):
- pickletools.dis(self.dumps(f, proto))
-
#
# PEP 574 tests below
#
@@ -3081,20 +3603,6 @@ def test_oob_buffers_writable_to_readonly(self):
self.assertIs(type(new), type(obj))
self.assertEqual(new, obj)
- def test_picklebuffer_error(self):
- # PickleBuffer forbidden with protocol < 5
- pb = pickle.PickleBuffer(b"foobar")
- for proto in range(0, 5):
- with self.assertRaises(pickle.PickleError):
- self.dumps(pb, proto)
-
- def test_buffer_callback_error(self):
- def buffer_callback(buffers):
- 1/0
- pb = pickle.PickleBuffer(b"foobar")
- with self.assertRaises(ZeroDivisionError):
- self.dumps(pb, 5, buffer_callback=buffer_callback)
-
def test_buffers_error(self):
pb = pickle.PickleBuffer(b"foobar")
for proto in range(5, pickle.HIGHEST_PROTOCOL + 1):
@@ -3186,37 +3694,6 @@ def __reduce__(self):
expected = "changed size during iteration"
self.assertIn(expected, str(e))
- def test_evil_pickler_mutating_collection(self):
- # https://github.com/python/cpython/issues/92930
- if not hasattr(self, "pickler"):
- raise self.skipTest(f"{type(self)} has no associated pickler type")
-
- global Clearer
- class Clearer:
- pass
-
- def check(collection):
- class EvilPickler(self.pickler):
- def persistent_id(self, obj):
- if isinstance(obj, Clearer):
- collection.clear()
- return None
- pickler = EvilPickler(io.BytesIO(), proto)
- try:
- pickler.dump(collection)
- except RuntimeError as e:
- expected = "changed size during iteration"
- self.assertIn(expected, str(e))
-
- for proto in protocols:
- check([Clearer()])
- check([Clearer(), Clearer()])
- check({Clearer()})
- check({Clearer(), Clearer()})
- check({Clearer(): 1})
- check({Clearer(): 1, Clearer(): 2})
- check({1: Clearer(), 2: Clearer()})
-
class BigmemPickleTests:
@@ -3347,6 +3824,18 @@ def test_huge_str_64b(self, size):
# Test classes for reduce_ex
+class R:
+ def __init__(self, reduce=None):
+ self.reduce = reduce
+ def __reduce__(self, proto):
+ return self.reduce
+
+class REX:
+ def __init__(self, reduce_ex=None):
+ self.reduce_ex = reduce_ex
+ def __reduce_ex__(self, proto):
+ return self.reduce_ex
+
class REX_one(object):
"""No __reduce_ex__ here, but inheriting it from object"""
_reduce_called = 0
@@ -3437,6 +3926,19 @@ def __getstate__(self):
__setstate__ = None
+class CustomError(Exception):
+ pass
+
+class Unpickleable:
+ def __reduce__(self):
+ raise CustomError
+
+UNPICKLEABLE = Unpickleable()
+
+class UnpickleableCallable(Unpickleable):
+ def __call__(self, *args, **kwargs):
+ pass
+
# Test classes for newobj
@@ -3505,6 +4007,12 @@ class BadGetattr:
def __getattr__(self, key):
self.foo
+class NoNew:
+ def __getattribute__(self, name):
+ if name == '__new__':
+ raise AttributeError
+ return super().__getattribute__(name)
+
class AbstractPickleModuleTests:
@@ -3577,7 +4085,7 @@ def raises_oserror(self, *args, **kwargs):
raise OSError
@property
def bad_property(self):
- 1/0
+ raise CustomError
# File without read and readline
class F:
@@ -3598,23 +4106,23 @@ class F:
class F:
read = bad_property
readline = raises_oserror
- self.assertRaises(ZeroDivisionError, self.Unpickler, F())
+ self.assertRaises(CustomError, self.Unpickler, F())
# File with bad readline
class F:
readline = bad_property
read = raises_oserror
- self.assertRaises(ZeroDivisionError, self.Unpickler, F())
+ self.assertRaises(CustomError, self.Unpickler, F())
# File with bad readline, no read
class F:
readline = bad_property
- self.assertRaises(ZeroDivisionError, self.Unpickler, F())
+ self.assertRaises(CustomError, self.Unpickler, F())
# File with bad read, no readline
class F:
read = bad_property
- self.assertRaises((AttributeError, ZeroDivisionError), self.Unpickler, F())
+ self.assertRaises((AttributeError, CustomError), self.Unpickler, F())
# File with bad peek
class F:
@@ -3623,7 +4131,7 @@ class F:
readline = raises_oserror
try:
self.Unpickler(F())
- except ZeroDivisionError:
+ except CustomError:
pass
# File with bad readinto
@@ -3633,7 +4141,7 @@ class F:
readline = raises_oserror
try:
self.Unpickler(F())
- except ZeroDivisionError:
+ except CustomError:
pass
def test_pickler_bad_file(self):
@@ -3646,8 +4154,8 @@ class F:
class F:
@property
def write(self):
- 1/0
- self.assertRaises(ZeroDivisionError, self.Pickler, F())
+ raise CustomError
+ self.assertRaises(CustomError, self.Pickler, F())
def check_dumps_loads_oob_buffers(self, dumps, loads):
# No need to do the full gamut of tests here, just enough to
@@ -3755,9 +4263,15 @@ def test_return_correct_type(self):
def test_protocol0_is_ascii_only(self):
non_ascii_str = "\N{EMPTY SET}"
- self.assertRaises(pickle.PicklingError, self.dumps, non_ascii_str, 0)
+ with self.assertRaises(pickle.PicklingError) as cm:
+ self.dumps(non_ascii_str, 0)
+ self.assertEqual(str(cm.exception),
+ 'persistent IDs in protocol 0 must be ASCII strings')
pickled = pickle.PERSID + non_ascii_str.encode('utf-8') + b'\n.'
- self.assertRaises(pickle.UnpicklingError, self.loads, pickled)
+ with self.assertRaises(pickle.UnpicklingError) as cm:
+ self.loads(pickled)
+ self.assertEqual(str(cm.exception),
+ 'persistent IDs in protocol 0 must be ASCII strings')
class AbstractPicklerUnpicklerObjectTests:
diff --git a/Lib/test/test_pickle.py b/Lib/test/test_pickle.py
index 49aa4b386039ec..c84e507cdf645f 100644
--- a/Lib/test/test_pickle.py
+++ b/Lib/test/test_pickle.py
@@ -16,6 +16,7 @@
from test.pickletester import AbstractHookTests
from test.pickletester import AbstractUnpickleTests
+from test.pickletester import AbstractPicklingErrorTests
from test.pickletester import AbstractPickleTests
from test.pickletester import AbstractPickleModuleTests
from test.pickletester import AbstractPersistentPicklerTests
@@ -55,6 +56,18 @@ def loads(self, buf, **kwds):
return u.load()
+class PyPicklingErrorTests(AbstractPicklingErrorTests, unittest.TestCase):
+
+ pickler = pickle._Pickler
+
+ def dumps(self, arg, proto=None, **kwargs):
+ f = io.BytesIO()
+ p = self.pickler(f, proto, **kwargs)
+ p.dump(arg)
+ f.seek(0)
+ return bytes(f.read())
+
+
class PyPicklerTests(AbstractPickleTests, unittest.TestCase):
pickler = pickle._Pickler
@@ -88,6 +101,8 @@ def loads(self, buf, **kwds):
return pickle.loads(buf, **kwds)
test_framed_write_sizes_with_delayed_writer = None
+ test_find_class = None
+ test_custom_find_class = None
class PersistentPicklerUnpicklerMixin(object):
@@ -267,6 +282,9 @@ class CUnpicklerTests(PyUnpicklerTests):
bad_stack_errors = (pickle.UnpicklingError,)
truncated_errors = (pickle.UnpicklingError,)
+ class CPicklingErrorTests(PyPicklingErrorTests):
+ pickler = _pickle.Pickler
+
class CPicklerTests(PyPicklerTests):
pickler = _pickle.Pickler
unpickler = _pickle.Unpickler
From 169e7138ab84db465b6bf28e6c1dc6c39dbf89f4 Mon Sep 17 00:00:00 2001
From: Sergey B Kirpichev
Date: Mon, 29 Jul 2024 06:56:40 +0300
Subject: [PATCH 44/47] gh-122234: fix accuracy issues for sum() (#122236)
* Use compensated summation for complex sums with floating-point items.
This amends #121176.
* sum() specializations for floats and complexes now use
PyLong_AsDouble() instead of PyLong_AsLongAndOverflow() and
compensated summation as well.
---
Lib/test/test_builtin.py | 5 ++++
...-07-24-17-11-51.gh-issue-122234.VxsP_F.rst | 4 ++++
Python/bltinmodule.c | 24 ++++++++++---------
3 files changed, 22 insertions(+), 11 deletions(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-07-24-17-11-51.gh-issue-122234.VxsP_F.rst
diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py
index c6a563cc90fec4..85f139db9bcd45 100644
--- a/Lib/test/test_builtin.py
+++ b/Lib/test/test_builtin.py
@@ -1778,6 +1778,8 @@ def test_sum(self):
self.assertRaises(TypeError, sum, [], '')
self.assertRaises(TypeError, sum, [], b'')
self.assertRaises(TypeError, sum, [], bytearray())
+ self.assertRaises(OverflowError, sum, [1.0, 10**1000])
+ self.assertRaises(OverflowError, sum, [1j, 10**1000])
class BadSeq:
def __getitem__(self, index):
@@ -1803,6 +1805,9 @@ def test_sum_accuracy(self):
self.assertEqual(sum([1.0, 10E100, 1.0, -10E100, 2j]), 2+2j)
self.assertEqual(sum([2+1j, 10E100j, 1j, -10E100j]), 2+2j)
self.assertEqual(sum([1j, 1, 10E100j, 1j, 1.0, -10E100j]), 2+2j)
+ self.assertEqual(sum([2j, 1., 10E100, 1., -10E100]), 2+2j)
+ self.assertEqual(sum([1.0, 10**100, 1.0, -10**100]), 2.0)
+ self.assertEqual(sum([2j, 1.0, 10**100, 1.0, -10**100]), 2+2j)
self.assertEqual(sum([0.1j]*10 + [fractions.Fraction(1, 10)]), 0.1+1j)
def test_type(self):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-07-24-17-11-51.gh-issue-122234.VxsP_F.rst b/Misc/NEWS.d/next/Core and Builtins/2024-07-24-17-11-51.gh-issue-122234.VxsP_F.rst
new file mode 100644
index 00000000000000..b86d6fbdfc648f
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2024-07-24-17-11-51.gh-issue-122234.VxsP_F.rst
@@ -0,0 +1,4 @@
+Specializations for sums with float and complex inputs in :func:`sum()` now
+always use compensated summation. Also, for integer items in above
+specializations: :c:func:`PyLong_AsDouble` is used, instead of
+:c:func:`PyLong_AsLongAndOverflow`. Patch by Sergey B Kirpichev.
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index 3f7bf4d568ee46..ae025e767ec838 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -2687,14 +2687,15 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
continue;
}
if (PyLong_Check(item)) {
- long value;
- int overflow;
- value = PyLong_AsLongAndOverflow(item, &overflow);
- if (!overflow) {
- re_sum.hi += (double)value;
+ double value = PyLong_AsDouble(item);
+ if (value != -1.0 || !PyErr_Occurred()) {
+ re_sum = cs_add(re_sum, value);
Py_DECREF(item);
continue;
}
+ else {
+ return NULL;
+ }
}
result = PyFloat_FromDouble(cs_to_double(re_sum));
if (result == NULL) {
@@ -2736,19 +2737,20 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
continue;
}
if (PyLong_Check(item)) {
- long value;
- int overflow;
- value = PyLong_AsLongAndOverflow(item, &overflow);
- if (!overflow) {
- re_sum.hi += (double)value;
+ double value = PyLong_AsDouble(item);
+ if (value != -1.0 || !PyErr_Occurred()) {
+ re_sum = cs_add(re_sum, value);
im_sum.hi += 0.0;
Py_DECREF(item);
continue;
}
+ else {
+ return NULL;
+ }
}
if (PyFloat_Check(item)) {
double value = PyFloat_AS_DOUBLE(item);
- re_sum.hi += value;
+ re_sum = cs_add(re_sum, value);
im_sum.hi += 0.0;
_Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc);
continue;
From 3b034d26eb8480f8d12ae11f42d038d24cf8498a Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka
Date: Mon, 29 Jul 2024 11:49:13 +0300
Subject: [PATCH 45/47] gh-122311: Fix some error messages in pickle
(GH-122386)
---
Lib/pickle.py | 7 ++++---
Lib/test/pickletester.py | 4 +++-
.../Library/2024-07-29-10-24-48.gh-issue-122311.xChV1b.rst | 1 +
Modules/_pickle.c | 6 +++---
4 files changed, 11 insertions(+), 7 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2024-07-29-10-24-48.gh-issue-122311.xChV1b.rst
diff --git a/Lib/pickle.py b/Lib/pickle.py
index c6c151b2065f4d..299c9e0e5e5641 100644
--- a/Lib/pickle.py
+++ b/Lib/pickle.py
@@ -314,16 +314,17 @@ def load_frame(self, frame_size):
# Tools used for pickling.
def _getattribute(obj, name):
+ top = obj
for subpath in name.split('.'):
if subpath == '':
raise AttributeError("Can't get local attribute {!r} on {!r}"
- .format(name, obj))
+ .format(name, top))
try:
parent = obj
obj = getattr(obj, subpath)
except AttributeError:
raise AttributeError("Can't get attribute {!r} on {!r}"
- .format(name, obj)) from None
+ .format(name, top)) from None
return obj, parent
def whichmodule(obj, name):
@@ -832,7 +833,7 @@ def save_bytearray(self, obj):
if _HAVE_PICKLE_BUFFER:
def save_picklebuffer(self, obj):
if self.proto < 5:
- raise PicklingError("PickleBuffer can only pickled with "
+ raise PicklingError("PickleBuffer can only be pickled with "
"protocol >= 5")
with obj.raw() as m:
if not m.contiguous:
diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py
index 174f4ff6d021b2..a2b49e6c92a7b3 100644
--- a/Lib/test/pickletester.py
+++ b/Lib/test/pickletester.py
@@ -1982,8 +1982,10 @@ def test_picklebuffer_error(self):
pb = pickle.PickleBuffer(b"foobar")
for proto in range(0, 5):
with self.subTest(proto=proto):
- with self.assertRaises(pickle.PickleError):
+ with self.assertRaises(pickle.PickleError) as cm:
self.dumps(pb, proto)
+ self.assertEqual(str(cm.exception),
+ 'PickleBuffer can only be pickled with protocol >= 5')
def test_non_continuous_buffer(self):
if self.pickler is pickle._Pickler:
diff --git a/Misc/NEWS.d/next/Library/2024-07-29-10-24-48.gh-issue-122311.xChV1b.rst b/Misc/NEWS.d/next/Library/2024-07-29-10-24-48.gh-issue-122311.xChV1b.rst
new file mode 100644
index 00000000000000..8d70c610a8dad6
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-07-29-10-24-48.gh-issue-122311.xChV1b.rst
@@ -0,0 +1 @@
+Fix some error messages in :mod:`pickle`.
diff --git a/Modules/_pickle.c b/Modules/_pickle.c
index 861363b68c20c5..452b4aff0237ca 100644
--- a/Modules/_pickle.c
+++ b/Modules/_pickle.c
@@ -1817,10 +1817,10 @@ get_dotted_path(PyObject *obj, PyObject *name)
if (_PyUnicode_EqualToASCIIString(subpath, "")) {
if (obj == NULL)
PyErr_Format(PyExc_AttributeError,
- "Can't pickle local object %R", name);
+ "Can't get local object %R", name);
else
PyErr_Format(PyExc_AttributeError,
- "Can't pickle local attribute %R on %R", name, obj);
+ "Can't get local attribute %R on %R", name, obj);
Py_DECREF(dotted_path);
return NULL;
}
@@ -2507,7 +2507,7 @@ save_picklebuffer(PickleState *st, PicklerObject *self, PyObject *obj)
{
if (self->proto < 5) {
PyErr_SetString(st->PicklingError,
- "PickleBuffer can only pickled with protocol >= 5");
+ "PickleBuffer can only be pickled with protocol >= 5");
return -1;
}
const Py_buffer* view = PyPickleBuffer_GetBuffer(obj);
From 0697188084bf61b28f258fbbe867e1010d679b3e Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka
Date: Mon, 29 Jul 2024 13:40:16 +0300
Subject: [PATCH 46/47] gh-122311: Add more tests for error messages in pickle
(GH-122373)
---
Lib/test/pickletester.py | 235 ++++++++++++++++++++++++++++++---------
1 file changed, 185 insertions(+), 50 deletions(-)
diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py
index a2b49e6c92a7b3..3c936b3bc4029e 100644
--- a/Lib/test/pickletester.py
+++ b/Lib/test/pickletester.py
@@ -1229,24 +1229,38 @@ def test_find_class(self):
self.assertIs(unpickler.find_class('os.path', 'join'), os.path.join)
self.assertIs(unpickler4.find_class('builtins', 'str.upper'), str.upper)
- with self.assertRaises(AttributeError):
+ with self.assertRaisesRegex(AttributeError,
+ r"module 'builtins' has no attribute 'str\.upper'|"
+ r"Can't get attribute 'str\.upper' on \.spam'|"
+ r"Can't get attribute 'log\.\.spam' on .spam')
- with self.assertRaises(AttributeError):
+ with self.assertRaisesRegex(AttributeError,
+ r"Can't get local attribute 'log\.\.spam' on .spam')
- with self.assertRaises(AttributeError):
+ with self.assertRaisesRegex(AttributeError,
+ "module 'math' has no attribute ''|"
+ "Can't get attribute '' on )'
+ r' must return (a )?string or tuple')
with self.assertRaisesRegex(
ValueError, 'The reducer just failed'):
From 9187484dd97f6beb94fc17676014706922e380e1 Mon Sep 17 00:00:00 2001
From: Kirill Podoprigora
Date: Mon, 29 Jul 2024 13:59:42 +0300
Subject: [PATCH 47/47] gh-122292: Split up ``Lib/test/test_ast.py`` into a
couple of files (#122293)
---
Lib/test/test_ast/__init__.py | 7 +
Lib/test/test_ast/snippets.py | 602 +++++++++++++++++++++++++++
Lib/test/{ => test_ast}/test_ast.py | 616 +---------------------------
Lib/test/test_ast/utils.py | 15 +
Makefile.pre.in | 1 +
5 files changed, 629 insertions(+), 612 deletions(-)
create mode 100644 Lib/test/test_ast/__init__.py
create mode 100644 Lib/test/test_ast/snippets.py
rename Lib/test/{ => test_ast}/test_ast.py (68%)
create mode 100644 Lib/test/test_ast/utils.py
diff --git a/Lib/test/test_ast/__init__.py b/Lib/test/test_ast/__init__.py
new file mode 100644
index 00000000000000..9a89d27ba9f979
--- /dev/null
+++ b/Lib/test/test_ast/__init__.py
@@ -0,0 +1,7 @@
+import os
+
+from test import support
+
+
+def load_tests(*args):
+ return support.load_package_tests(os.path.dirname(__file__), *args)
diff --git a/Lib/test/test_ast/snippets.py b/Lib/test/test_ast/snippets.py
new file mode 100644
index 00000000000000..95dc3ca03cd38b
--- /dev/null
+++ b/Lib/test/test_ast/snippets.py
@@ -0,0 +1,602 @@
+import ast
+import sys
+
+from test.test_ast.utils import to_tuple
+
+
+# These tests are compiled through "exec"
+# There should be at least one test per statement
+exec_tests = [
+ # Module docstring
+ "'module docstring'",
+ # FunctionDef
+ "def f(): pass",
+ # FunctionDef with docstring
+ "def f(): 'function docstring'",
+ # FunctionDef with arg
+ "def f(a): pass",
+ # FunctionDef with arg and default value
+ "def f(a=0): pass",
+ # FunctionDef with varargs
+ "def f(*args): pass",
+ # FunctionDef with varargs as TypeVarTuple
+ "def f(*args: *Ts): pass",
+ # FunctionDef with varargs as unpacked Tuple
+ "def f(*args: *tuple[int, ...]): pass",
+ # FunctionDef with varargs as unpacked Tuple *and* TypeVarTuple
+ "def f(*args: *tuple[int, *Ts]): pass",
+ # FunctionDef with kwargs
+ "def f(**kwargs): pass",
+ # FunctionDef with all kind of args and docstring
+ "def f(a, b=1, c=None, d=[], e={}, *args, f=42, **kwargs): 'doc for f()'",
+ # FunctionDef with type annotation on return involving unpacking
+ "def f() -> tuple[*Ts]: pass",
+ "def f() -> tuple[int, *Ts]: pass",
+ "def f() -> tuple[int, *tuple[int, ...]]: pass",
+ # ClassDef
+ "class C:pass",
+ # ClassDef with docstring
+ "class C: 'docstring for class C'",
+ # ClassDef, new style class
+ "class C(object): pass",
+ # Classdef with multiple bases
+ "class C(A, B): pass",
+ # Return
+ "def f():return 1",
+ "def f():return",
+ # Delete
+ "del v",
+ # Assign
+ "v = 1",
+ "a,b = c",
+ "(a,b) = c",
+ "[a,b] = c",
+ "a[b] = c",
+ # AnnAssign with unpacked types
+ "x: tuple[*Ts]",
+ "x: tuple[int, *Ts]",
+ "x: tuple[int, *tuple[str, ...]]",
+ # AugAssign
+ "v += 1",
+ "v -= 1",
+ "v *= 1",
+ "v @= 1",
+ "v /= 1",
+ "v %= 1",
+ "v **= 1",
+ "v <<= 1",
+ "v >>= 1",
+ "v |= 1",
+ "v ^= 1",
+ "v &= 1",
+ "v //= 1",
+ # For
+ "for v in v:pass",
+ # For-Else
+ "for v in v:\n pass\nelse:\n pass",
+ # While
+ "while v:pass",
+ # While-Else
+ "while v:\n pass\nelse:\n pass",
+ # If-Elif-Else
+ "if v:pass",
+ "if a:\n pass\nelif b:\n pass",
+ "if a:\n pass\nelse:\n pass",
+ "if a:\n pass\nelif b:\n pass\nelse:\n pass",
+ "if a:\n pass\nelif b:\n pass\nelif b:\n pass\nelif b:\n pass\nelse:\n pass",
+ # With
+ "with x: pass",
+ "with x, y: pass",
+ "with x as y: pass",
+ "with x as y, z as q: pass",
+ "with (x as y): pass",
+ "with (x, y): pass",
+ # Raise
+ "raise",
+ "raise Exception('string')",
+ "raise Exception",
+ "raise Exception('string') from None",
+ # TryExcept
+ "try:\n pass\nexcept Exception:\n pass",
+ "try:\n pass\nexcept Exception as exc:\n pass",
+ # TryFinally
+ "try:\n pass\nfinally:\n pass",
+ # TryStarExcept
+ "try:\n pass\nexcept* Exception:\n pass",
+ "try:\n pass\nexcept* Exception as exc:\n pass",
+ # TryExceptFinallyElse
+ "try:\n pass\nexcept Exception:\n pass\nelse: pass\nfinally:\n pass",
+ "try:\n pass\nexcept Exception as exc:\n pass\nelse: pass\nfinally:\n pass",
+ "try:\n pass\nexcept* Exception as exc:\n pass\nelse: pass\nfinally:\n pass",
+ # Assert
+ "assert v",
+ # Assert with message
+ "assert v, 'message'",
+ # Import
+ "import sys",
+ "import foo as bar",
+ # ImportFrom
+ "from sys import x as y",
+ "from sys import v",
+ # Global
+ "global v",
+ # Expr
+ "1",
+ # Pass,
+ "pass",
+ # Break
+ "for v in v:break",
+ # Continue
+ "for v in v:continue",
+ # for statements with naked tuples (see http://bugs.python.org/issue6704)
+ "for a,b in c: pass",
+ "for (a,b) in c: pass",
+ "for [a,b] in c: pass",
+ # Multiline generator expression (test for .lineno & .col_offset)
+ """(
+ (
+ Aa
+ ,
+ Bb
+ )
+ for
+ Aa
+ ,
+ Bb in Cc
+ )""",
+ # dictcomp
+ "{a : b for w in x for m in p if g}",
+ # dictcomp with naked tuple
+ "{a : b for v,w in x}",
+ # setcomp
+ "{r for l in x if g}",
+ # setcomp with naked tuple
+ "{r for l,m in x}",
+ # AsyncFunctionDef
+ "async def f():\n 'async function'\n await something()",
+ # AsyncFor
+ "async def f():\n async for e in i: 1\n else: 2",
+ # AsyncWith
+ "async def f():\n async with a as b: 1",
+ # PEP 448: Additional Unpacking Generalizations
+ "{**{1:2}, 2:3}",
+ "{*{1, 2}, 3}",
+ # Function with yield (from)
+ "def f(): yield 1",
+ "def f(): yield from []",
+ # Asynchronous comprehensions
+ "async def f():\n [i async for b in c]",
+ # Decorated FunctionDef
+ "@deco1\n@deco2()\n@deco3(1)\ndef f(): pass",
+ # Decorated AsyncFunctionDef
+ "@deco1\n@deco2()\n@deco3(1)\nasync def f(): pass",
+ # Decorated ClassDef
+ "@deco1\n@deco2()\n@deco3(1)\nclass C: pass",
+ # Decorator with generator argument
+ "@deco(a for a in b)\ndef f(): pass",
+ # Decorator with attribute
+ "@a.b.c\ndef f(): pass",
+ # Simple assignment expression
+ "(a := 1)",
+ # Assignment expression in if statement
+ "if a := foo(): pass",
+ # Assignment expression in while
+ "while a := foo(): pass",
+ # Positional-only arguments
+ "def f(a, /,): pass",
+ "def f(a, /, c, d, e): pass",
+ "def f(a, /, c, *, d, e): pass",
+ "def f(a, /, c, *, d, e, **kwargs): pass",
+ # Positional-only arguments with defaults
+ "def f(a=1, /,): pass",
+ "def f(a=1, /, b=2, c=4): pass",
+ "def f(a=1, /, b=2, *, c=4): pass",
+ "def f(a=1, /, b=2, *, c): pass",
+ "def f(a=1, /, b=2, *, c=4, **kwargs): pass",
+ "def f(a=1, /, b=2, *, c, **kwargs): pass",
+ # Type aliases
+ "type X = int",
+ "type X[T] = int",
+ "type X[T, *Ts, **P] = (T, Ts, P)",
+ "type X[T: int, *Ts, **P] = (T, Ts, P)",
+ "type X[T: (int, str), *Ts, **P] = (T, Ts, P)",
+ "type X[T: int = 1, *Ts = 2, **P =3] = (T, Ts, P)",
+ # Generic classes
+ "class X[T]: pass",
+ "class X[T, *Ts, **P]: pass",
+ "class X[T: int, *Ts, **P]: pass",
+ "class X[T: (int, str), *Ts, **P]: pass",
+ "class X[T: int = 1, *Ts = 2, **P = 3]: pass",
+ # Generic functions
+ "def f[T](): pass",
+ "def f[T, *Ts, **P](): pass",
+ "def f[T: int, *Ts, **P](): pass",
+ "def f[T: (int, str), *Ts, **P](): pass",
+ "def f[T: int = 1, *Ts = 2, **P = 3](): pass",
+ # Match
+ "match x:\n\tcase 1:\n\t\tpass",
+ # Match with _
+ "match x:\n\tcase 1:\n\t\tpass\n\tcase _:\n\t\tpass",
+]
+
+# These are compiled through "single"
+# because of overlap with "eval", it just tests what
+# can't be tested with "eval"
+single_tests = [
+ "1+2"
+]
+
+# These are compiled through "eval"
+# It should test all expressions
+eval_tests = [
+ # Constant(value=None)
+ "None",
+ # True
+ "True",
+ # False
+ "False",
+ # BoolOp
+ "a and b",
+ "a or b",
+ # BinOp
+ "a + b",
+ "a - b",
+ "a * b",
+ "a / b",
+ "a @ b",
+ "a // b",
+ "a ** b",
+ "a % b",
+ "a >> b",
+ "a << b",
+ "a ^ b",
+ "a | b",
+ "a & b",
+ # UnaryOp
+ "not v",
+ "+v",
+ "-v",
+ "~v",
+ # Lambda
+ "lambda:None",
+ # Dict
+ "{ 1:2 }",
+ # Empty dict
+ "{}",
+ # Set
+ "{None,}",
+ # Multiline dict (test for .lineno & .col_offset)
+ """{
+ 1
+ :
+ 2
+ }""",
+ # Multiline list
+ """[
+ 1
+ ,
+ 1
+ ]""",
+ # Multiline tuple
+ """(
+ 1
+ ,
+ )""",
+ # Multiline set
+ """{
+ 1
+ ,
+ 1
+ }""",
+ # ListComp
+ "[a for b in c if d]",
+ # GeneratorExp
+ "(a for b in c if d)",
+ # SetComp
+ "{a for b in c if d}",
+ # DictComp
+ "{k: v for k, v in c if d}",
+ # Comprehensions with multiple for targets
+ "[(a,b) for a,b in c]",
+ "[(a,b) for (a,b) in c]",
+ "[(a,b) for [a,b] in c]",
+ "{(a,b) for a,b in c}",
+ "{(a,b) for (a,b) in c}",
+ "{(a,b) for [a,b] in c}",
+ "((a,b) for a,b in c)",
+ "((a,b) for (a,b) in c)",
+ "((a,b) for [a,b] in c)",
+ # Async comprehensions - async comprehensions can't work outside an asynchronous function
+ #
+ # Yield - yield expressions can't work outside a function
+ #
+ # Compare
+ "1 < 2 < 3",
+ "a == b",
+ "a <= b",
+ "a >= b",
+ "a != b",
+ "a is b",
+ "a is not b",
+ "a in b",
+ "a not in b",
+ # Call without argument
+ "f()",
+ # Call
+ "f(1,2,c=3,*d,**e)",
+ # Call with multi-character starred
+ "f(*[0, 1])",
+ # Call with a generator argument
+ "f(a for a in b)",
+ # Constant(value=int())
+ "10",
+ # Complex num
+ "1j",
+ # Constant(value=str())
+ "'string'",
+ # Attribute
+ "a.b",
+ # Subscript
+ "a[b:c]",
+ # Name
+ "v",
+ # List
+ "[1,2,3]",
+ # Empty list
+ "[]",
+ # Tuple
+ "1,2,3",
+ # Tuple
+ "(1,2,3)",
+ # Empty tuple
+ "()",
+ # Combination
+ "a.b.c.d(a.b[1:2])",
+ # Slice
+ "[5][1:]",
+ "[5][:1]",
+ "[5][::1]",
+ "[5][1:1:1]",
+ # IfExp
+ "foo() if x else bar()",
+ # JoinedStr and FormattedValue
+ "f'{a}'",
+ "f'{a:.2f}'",
+ "f'{a!r}'",
+ "f'foo({a})'",
+]
+
+
+def main():
+ if __name__ != '__main__':
+ return
+ if sys.argv[1:] == ['-g']:
+ for statements, kind in ((exec_tests, "exec"), (single_tests, "single"),
+ (eval_tests, "eval")):
+ print(kind+"_results = [")
+ for statement in statements:
+ tree = ast.parse(statement, "?", kind)
+ print("%r," % (to_tuple(tree),))
+ print("]")
+ print("main()")
+ raise SystemExit
+ unittest.main()
+
+#### EVERYTHING BELOW IS GENERATED BY python Lib/test/test_ast/snippets.py -g #####
+exec_results = [
+('Module', [('Expr', (1, 0, 1, 18), ('Constant', (1, 0, 1, 18), 'module docstring', None))], []),
+('Module', [('FunctionDef', (1, 0, 1, 13), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 9, 1, 13))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 29), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (1, 9, 1, 29), ('Constant', (1, 9, 1, 29), 'function docstring', None))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 14), 'f', ('arguments', [], [('arg', (1, 6, 1, 7), 'a', None, None)], None, [], [], None, []), [('Pass', (1, 10, 1, 14))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 16), 'f', ('arguments', [], [('arg', (1, 6, 1, 7), 'a', None, None)], None, [], [], None, [('Constant', (1, 8, 1, 9), 0, None)]), [('Pass', (1, 12, 1, 16))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 18), 'f', ('arguments', [], [], ('arg', (1, 7, 1, 11), 'args', None, None), [], [], None, []), [('Pass', (1, 14, 1, 18))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 23), 'f', ('arguments', [], [], ('arg', (1, 7, 1, 16), 'args', ('Starred', (1, 13, 1, 16), ('Name', (1, 14, 1, 16), 'Ts', ('Load',)), ('Load',)), None), [], [], None, []), [('Pass', (1, 19, 1, 23))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 36), 'f', ('arguments', [], [], ('arg', (1, 7, 1, 29), 'args', ('Starred', (1, 13, 1, 29), ('Subscript', (1, 14, 1, 29), ('Name', (1, 14, 1, 19), 'tuple', ('Load',)), ('Tuple', (1, 20, 1, 28), [('Name', (1, 20, 1, 23), 'int', ('Load',)), ('Constant', (1, 25, 1, 28), Ellipsis, None)], ('Load',)), ('Load',)), ('Load',)), None), [], [], None, []), [('Pass', (1, 32, 1, 36))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 36), 'f', ('arguments', [], [], ('arg', (1, 7, 1, 29), 'args', ('Starred', (1, 13, 1, 29), ('Subscript', (1, 14, 1, 29), ('Name', (1, 14, 1, 19), 'tuple', ('Load',)), ('Tuple', (1, 20, 1, 28), [('Name', (1, 20, 1, 23), 'int', ('Load',)), ('Starred', (1, 25, 1, 28), ('Name', (1, 26, 1, 28), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), ('Load',)), None), [], [], None, []), [('Pass', (1, 32, 1, 36))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 21), 'f', ('arguments', [], [], None, [], [], ('arg', (1, 8, 1, 14), 'kwargs', None, None), []), [('Pass', (1, 17, 1, 21))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 71), 'f', ('arguments', [], [('arg', (1, 6, 1, 7), 'a', None, None), ('arg', (1, 9, 1, 10), 'b', None, None), ('arg', (1, 14, 1, 15), 'c', None, None), ('arg', (1, 22, 1, 23), 'd', None, None), ('arg', (1, 28, 1, 29), 'e', None, None)], ('arg', (1, 35, 1, 39), 'args', None, None), [('arg', (1, 41, 1, 42), 'f', None, None)], [('Constant', (1, 43, 1, 45), 42, None)], ('arg', (1, 49, 1, 55), 'kwargs', None, None), [('Constant', (1, 11, 1, 12), 1, None), ('Constant', (1, 16, 1, 20), None, None), ('List', (1, 24, 1, 26), [], ('Load',)), ('Dict', (1, 30, 1, 32), [], [])]), [('Expr', (1, 58, 1, 71), ('Constant', (1, 58, 1, 71), 'doc for f()', None))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 27), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 23, 1, 27))], [], ('Subscript', (1, 11, 1, 21), ('Name', (1, 11, 1, 16), 'tuple', ('Load',)), ('Tuple', (1, 17, 1, 20), [('Starred', (1, 17, 1, 20), ('Name', (1, 18, 1, 20), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 32), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 28, 1, 32))], [], ('Subscript', (1, 11, 1, 26), ('Name', (1, 11, 1, 16), 'tuple', ('Load',)), ('Tuple', (1, 17, 1, 25), [('Name', (1, 17, 1, 20), 'int', ('Load',)), ('Starred', (1, 22, 1, 25), ('Name', (1, 23, 1, 25), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 45), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 41, 1, 45))], [], ('Subscript', (1, 11, 1, 39), ('Name', (1, 11, 1, 16), 'tuple', ('Load',)), ('Tuple', (1, 17, 1, 38), [('Name', (1, 17, 1, 20), 'int', ('Load',)), ('Starred', (1, 22, 1, 38), ('Subscript', (1, 23, 1, 38), ('Name', (1, 23, 1, 28), 'tuple', ('Load',)), ('Tuple', (1, 29, 1, 37), [('Name', (1, 29, 1, 32), 'int', ('Load',)), ('Constant', (1, 34, 1, 37), Ellipsis, None)], ('Load',)), ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, [])], []),
+('Module', [('ClassDef', (1, 0, 1, 12), 'C', [], [], [('Pass', (1, 8, 1, 12))], [], [])], []),
+('Module', [('ClassDef', (1, 0, 1, 32), 'C', [], [], [('Expr', (1, 9, 1, 32), ('Constant', (1, 9, 1, 32), 'docstring for class C', None))], [], [])], []),
+('Module', [('ClassDef', (1, 0, 1, 21), 'C', [('Name', (1, 8, 1, 14), 'object', ('Load',))], [], [('Pass', (1, 17, 1, 21))], [], [])], []),
+('Module', [('ClassDef', (1, 0, 1, 19), 'C', [('Name', (1, 8, 1, 9), 'A', ('Load',)), ('Name', (1, 11, 1, 12), 'B', ('Load',))], [], [('Pass', (1, 15, 1, 19))], [], [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 16), 'f', ('arguments', [], [], None, [], [], None, []), [('Return', (1, 8, 1, 16), ('Constant', (1, 15, 1, 16), 1, None))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 14), 'f', ('arguments', [], [], None, [], [], None, []), [('Return', (1, 8, 1, 14), None)], [], None, None, [])], []),
+('Module', [('Delete', (1, 0, 1, 5), [('Name', (1, 4, 1, 5), 'v', ('Del',))])], []),
+('Module', [('Assign', (1, 0, 1, 5), [('Name', (1, 0, 1, 1), 'v', ('Store',))], ('Constant', (1, 4, 1, 5), 1, None), None)], []),
+('Module', [('Assign', (1, 0, 1, 7), [('Tuple', (1, 0, 1, 3), [('Name', (1, 0, 1, 1), 'a', ('Store',)), ('Name', (1, 2, 1, 3), 'b', ('Store',))], ('Store',))], ('Name', (1, 6, 1, 7), 'c', ('Load',)), None)], []),
+('Module', [('Assign', (1, 0, 1, 9), [('Tuple', (1, 0, 1, 5), [('Name', (1, 1, 1, 2), 'a', ('Store',)), ('Name', (1, 3, 1, 4), 'b', ('Store',))], ('Store',))], ('Name', (1, 8, 1, 9), 'c', ('Load',)), None)], []),
+('Module', [('Assign', (1, 0, 1, 9), [('List', (1, 0, 1, 5), [('Name', (1, 1, 1, 2), 'a', ('Store',)), ('Name', (1, 3, 1, 4), 'b', ('Store',))], ('Store',))], ('Name', (1, 8, 1, 9), 'c', ('Load',)), None)], []),
+('Module', [('Assign', (1, 0, 1, 8), [('Subscript', (1, 0, 1, 4), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Name', (1, 2, 1, 3), 'b', ('Load',)), ('Store',))], ('Name', (1, 7, 1, 8), 'c', ('Load',)), None)], []),
+('Module', [('AnnAssign', (1, 0, 1, 13), ('Name', (1, 0, 1, 1), 'x', ('Store',)), ('Subscript', (1, 3, 1, 13), ('Name', (1, 3, 1, 8), 'tuple', ('Load',)), ('Tuple', (1, 9, 1, 12), [('Starred', (1, 9, 1, 12), ('Name', (1, 10, 1, 12), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, 1)], []),
+('Module', [('AnnAssign', (1, 0, 1, 18), ('Name', (1, 0, 1, 1), 'x', ('Store',)), ('Subscript', (1, 3, 1, 18), ('Name', (1, 3, 1, 8), 'tuple', ('Load',)), ('Tuple', (1, 9, 1, 17), [('Name', (1, 9, 1, 12), 'int', ('Load',)), ('Starred', (1, 14, 1, 17), ('Name', (1, 15, 1, 17), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, 1)], []),
+('Module', [('AnnAssign', (1, 0, 1, 31), ('Name', (1, 0, 1, 1), 'x', ('Store',)), ('Subscript', (1, 3, 1, 31), ('Name', (1, 3, 1, 8), 'tuple', ('Load',)), ('Tuple', (1, 9, 1, 30), [('Name', (1, 9, 1, 12), 'int', ('Load',)), ('Starred', (1, 14, 1, 30), ('Subscript', (1, 15, 1, 30), ('Name', (1, 15, 1, 20), 'tuple', ('Load',)), ('Tuple', (1, 21, 1, 29), [('Name', (1, 21, 1, 24), 'str', ('Load',)), ('Constant', (1, 26, 1, 29), Ellipsis, None)], ('Load',)), ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, 1)], []),
+('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('Add',), ('Constant', (1, 5, 1, 6), 1, None))], []),
+('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('Sub',), ('Constant', (1, 5, 1, 6), 1, None))], []),
+('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('Mult',), ('Constant', (1, 5, 1, 6), 1, None))], []),
+('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('MatMult',), ('Constant', (1, 5, 1, 6), 1, None))], []),
+('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('Div',), ('Constant', (1, 5, 1, 6), 1, None))], []),
+('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('Mod',), ('Constant', (1, 5, 1, 6), 1, None))], []),
+('Module', [('AugAssign', (1, 0, 1, 7), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('Pow',), ('Constant', (1, 6, 1, 7), 1, None))], []),
+('Module', [('AugAssign', (1, 0, 1, 7), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('LShift',), ('Constant', (1, 6, 1, 7), 1, None))], []),
+('Module', [('AugAssign', (1, 0, 1, 7), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('RShift',), ('Constant', (1, 6, 1, 7), 1, None))], []),
+('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('BitOr',), ('Constant', (1, 5, 1, 6), 1, None))], []),
+('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('BitXor',), ('Constant', (1, 5, 1, 6), 1, None))], []),
+('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('BitAnd',), ('Constant', (1, 5, 1, 6), 1, None))], []),
+('Module', [('AugAssign', (1, 0, 1, 7), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('FloorDiv',), ('Constant', (1, 6, 1, 7), 1, None))], []),
+('Module', [('For', (1, 0, 1, 15), ('Name', (1, 4, 1, 5), 'v', ('Store',)), ('Name', (1, 9, 1, 10), 'v', ('Load',)), [('Pass', (1, 11, 1, 15))], [], None)], []),
+('Module', [('For', (1, 0, 4, 6), ('Name', (1, 4, 1, 5), 'v', ('Store',)), ('Name', (1, 9, 1, 10), 'v', ('Load',)), [('Pass', (2, 2, 2, 6))], [('Pass', (4, 2, 4, 6))], None)], []),
+('Module', [('While', (1, 0, 1, 12), ('Name', (1, 6, 1, 7), 'v', ('Load',)), [('Pass', (1, 8, 1, 12))], [])], []),
+('Module', [('While', (1, 0, 4, 6), ('Name', (1, 6, 1, 7), 'v', ('Load',)), [('Pass', (2, 2, 2, 6))], [('Pass', (4, 2, 4, 6))])], []),
+('Module', [('If', (1, 0, 1, 9), ('Name', (1, 3, 1, 4), 'v', ('Load',)), [('Pass', (1, 5, 1, 9))], [])], []),
+('Module', [('If', (1, 0, 4, 6), ('Name', (1, 3, 1, 4), 'a', ('Load',)), [('Pass', (2, 2, 2, 6))], [('If', (3, 0, 4, 6), ('Name', (3, 5, 3, 6), 'b', ('Load',)), [('Pass', (4, 2, 4, 6))], [])])], []),
+('Module', [('If', (1, 0, 4, 6), ('Name', (1, 3, 1, 4), 'a', ('Load',)), [('Pass', (2, 2, 2, 6))], [('Pass', (4, 2, 4, 6))])], []),
+('Module', [('If', (1, 0, 6, 6), ('Name', (1, 3, 1, 4), 'a', ('Load',)), [('Pass', (2, 2, 2, 6))], [('If', (3, 0, 6, 6), ('Name', (3, 5, 3, 6), 'b', ('Load',)), [('Pass', (4, 2, 4, 6))], [('Pass', (6, 2, 6, 6))])])], []),
+('Module', [('If', (1, 0, 10, 6), ('Name', (1, 3, 1, 4), 'a', ('Load',)), [('Pass', (2, 2, 2, 6))], [('If', (3, 0, 10, 6), ('Name', (3, 5, 3, 6), 'b', ('Load',)), [('Pass', (4, 2, 4, 6))], [('If', (5, 0, 10, 6), ('Name', (5, 5, 5, 6), 'b', ('Load',)), [('Pass', (6, 2, 6, 6))], [('If', (7, 0, 10, 6), ('Name', (7, 5, 7, 6), 'b', ('Load',)), [('Pass', (8, 2, 8, 6))], [('Pass', (10, 2, 10, 6))])])])])], []),
+('Module', [('With', (1, 0, 1, 12), [('withitem', ('Name', (1, 5, 1, 6), 'x', ('Load',)), None)], [('Pass', (1, 8, 1, 12))], None)], []),
+('Module', [('With', (1, 0, 1, 15), [('withitem', ('Name', (1, 5, 1, 6), 'x', ('Load',)), None), ('withitem', ('Name', (1, 8, 1, 9), 'y', ('Load',)), None)], [('Pass', (1, 11, 1, 15))], None)], []),
+('Module', [('With', (1, 0, 1, 17), [('withitem', ('Name', (1, 5, 1, 6), 'x', ('Load',)), ('Name', (1, 10, 1, 11), 'y', ('Store',)))], [('Pass', (1, 13, 1, 17))], None)], []),
+('Module', [('With', (1, 0, 1, 25), [('withitem', ('Name', (1, 5, 1, 6), 'x', ('Load',)), ('Name', (1, 10, 1, 11), 'y', ('Store',))), ('withitem', ('Name', (1, 13, 1, 14), 'z', ('Load',)), ('Name', (1, 18, 1, 19), 'q', ('Store',)))], [('Pass', (1, 21, 1, 25))], None)], []),
+('Module', [('With', (1, 0, 1, 19), [('withitem', ('Name', (1, 6, 1, 7), 'x', ('Load',)), ('Name', (1, 11, 1, 12), 'y', ('Store',)))], [('Pass', (1, 15, 1, 19))], None)], []),
+('Module', [('With', (1, 0, 1, 17), [('withitem', ('Name', (1, 6, 1, 7), 'x', ('Load',)), None), ('withitem', ('Name', (1, 9, 1, 10), 'y', ('Load',)), None)], [('Pass', (1, 13, 1, 17))], None)], []),
+('Module', [('Raise', (1, 0, 1, 5), None, None)], []),
+('Module', [('Raise', (1, 0, 1, 25), ('Call', (1, 6, 1, 25), ('Name', (1, 6, 1, 15), 'Exception', ('Load',)), [('Constant', (1, 16, 1, 24), 'string', None)], []), None)], []),
+('Module', [('Raise', (1, 0, 1, 15), ('Name', (1, 6, 1, 15), 'Exception', ('Load',)), None)], []),
+('Module', [('Raise', (1, 0, 1, 35), ('Call', (1, 6, 1, 25), ('Name', (1, 6, 1, 15), 'Exception', ('Load',)), [('Constant', (1, 16, 1, 24), 'string', None)], []), ('Constant', (1, 31, 1, 35), None, None))], []),
+('Module', [('Try', (1, 0, 4, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 7, 3, 16), 'Exception', ('Load',)), None, [('Pass', (4, 2, 4, 6))])], [], [])], []),
+('Module', [('Try', (1, 0, 4, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 7, 3, 16), 'Exception', ('Load',)), 'exc', [('Pass', (4, 2, 4, 6))])], [], [])], []),
+('Module', [('Try', (1, 0, 4, 6), [('Pass', (2, 2, 2, 6))], [], [], [('Pass', (4, 2, 4, 6))])], []),
+('Module', [('TryStar', (1, 0, 4, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 8, 3, 17), 'Exception', ('Load',)), None, [('Pass', (4, 2, 4, 6))])], [], [])], []),
+('Module', [('TryStar', (1, 0, 4, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 8, 3, 17), 'Exception', ('Load',)), 'exc', [('Pass', (4, 2, 4, 6))])], [], [])], []),
+('Module', [('Try', (1, 0, 7, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 7, 3, 16), 'Exception', ('Load',)), None, [('Pass', (4, 2, 4, 6))])], [('Pass', (5, 7, 5, 11))], [('Pass', (7, 2, 7, 6))])], []),
+('Module', [('Try', (1, 0, 7, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 7, 3, 16), 'Exception', ('Load',)), 'exc', [('Pass', (4, 2, 4, 6))])], [('Pass', (5, 7, 5, 11))], [('Pass', (7, 2, 7, 6))])], []),
+('Module', [('TryStar', (1, 0, 7, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 8, 3, 17), 'Exception', ('Load',)), 'exc', [('Pass', (4, 2, 4, 6))])], [('Pass', (5, 7, 5, 11))], [('Pass', (7, 2, 7, 6))])], []),
+('Module', [('Assert', (1, 0, 1, 8), ('Name', (1, 7, 1, 8), 'v', ('Load',)), None)], []),
+('Module', [('Assert', (1, 0, 1, 19), ('Name', (1, 7, 1, 8), 'v', ('Load',)), ('Constant', (1, 10, 1, 19), 'message', None))], []),
+('Module', [('Import', (1, 0, 1, 10), [('alias', (1, 7, 1, 10), 'sys', None)])], []),
+('Module', [('Import', (1, 0, 1, 17), [('alias', (1, 7, 1, 17), 'foo', 'bar')])], []),
+('Module', [('ImportFrom', (1, 0, 1, 22), 'sys', [('alias', (1, 16, 1, 22), 'x', 'y')], 0)], []),
+('Module', [('ImportFrom', (1, 0, 1, 17), 'sys', [('alias', (1, 16, 1, 17), 'v', None)], 0)], []),
+('Module', [('Global', (1, 0, 1, 8), ['v'])], []),
+('Module', [('Expr', (1, 0, 1, 1), ('Constant', (1, 0, 1, 1), 1, None))], []),
+('Module', [('Pass', (1, 0, 1, 4))], []),
+('Module', [('For', (1, 0, 1, 16), ('Name', (1, 4, 1, 5), 'v', ('Store',)), ('Name', (1, 9, 1, 10), 'v', ('Load',)), [('Break', (1, 11, 1, 16))], [], None)], []),
+('Module', [('For', (1, 0, 1, 19), ('Name', (1, 4, 1, 5), 'v', ('Store',)), ('Name', (1, 9, 1, 10), 'v', ('Load',)), [('Continue', (1, 11, 1, 19))], [], None)], []),
+('Module', [('For', (1, 0, 1, 18), ('Tuple', (1, 4, 1, 7), [('Name', (1, 4, 1, 5), 'a', ('Store',)), ('Name', (1, 6, 1, 7), 'b', ('Store',))], ('Store',)), ('Name', (1, 11, 1, 12), 'c', ('Load',)), [('Pass', (1, 14, 1, 18))], [], None)], []),
+('Module', [('For', (1, 0, 1, 20), ('Tuple', (1, 4, 1, 9), [('Name', (1, 5, 1, 6), 'a', ('Store',)), ('Name', (1, 7, 1, 8), 'b', ('Store',))], ('Store',)), ('Name', (1, 13, 1, 14), 'c', ('Load',)), [('Pass', (1, 16, 1, 20))], [], None)], []),
+('Module', [('For', (1, 0, 1, 20), ('List', (1, 4, 1, 9), [('Name', (1, 5, 1, 6), 'a', ('Store',)), ('Name', (1, 7, 1, 8), 'b', ('Store',))], ('Store',)), ('Name', (1, 13, 1, 14), 'c', ('Load',)), [('Pass', (1, 16, 1, 20))], [], None)], []),
+('Module', [('Expr', (1, 0, 11, 5), ('GeneratorExp', (1, 0, 11, 5), ('Tuple', (2, 4, 6, 5), [('Name', (3, 4, 3, 6), 'Aa', ('Load',)), ('Name', (5, 7, 5, 9), 'Bb', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (8, 4, 10, 6), [('Name', (8, 4, 8, 6), 'Aa', ('Store',)), ('Name', (10, 4, 10, 6), 'Bb', ('Store',))], ('Store',)), ('Name', (10, 10, 10, 12), 'Cc', ('Load',)), [], 0)]))], []),
+('Module', [('Expr', (1, 0, 1, 34), ('DictComp', (1, 0, 1, 34), ('Name', (1, 1, 1, 2), 'a', ('Load',)), ('Name', (1, 5, 1, 6), 'b', ('Load',)), [('comprehension', ('Name', (1, 11, 1, 12), 'w', ('Store',)), ('Name', (1, 16, 1, 17), 'x', ('Load',)), [], 0), ('comprehension', ('Name', (1, 22, 1, 23), 'm', ('Store',)), ('Name', (1, 27, 1, 28), 'p', ('Load',)), [('Name', (1, 32, 1, 33), 'g', ('Load',))], 0)]))], []),
+('Module', [('Expr', (1, 0, 1, 20), ('DictComp', (1, 0, 1, 20), ('Name', (1, 1, 1, 2), 'a', ('Load',)), ('Name', (1, 5, 1, 6), 'b', ('Load',)), [('comprehension', ('Tuple', (1, 11, 1, 14), [('Name', (1, 11, 1, 12), 'v', ('Store',)), ('Name', (1, 13, 1, 14), 'w', ('Store',))], ('Store',)), ('Name', (1, 18, 1, 19), 'x', ('Load',)), [], 0)]))], []),
+('Module', [('Expr', (1, 0, 1, 19), ('SetComp', (1, 0, 1, 19), ('Name', (1, 1, 1, 2), 'r', ('Load',)), [('comprehension', ('Name', (1, 7, 1, 8), 'l', ('Store',)), ('Name', (1, 12, 1, 13), 'x', ('Load',)), [('Name', (1, 17, 1, 18), 'g', ('Load',))], 0)]))], []),
+('Module', [('Expr', (1, 0, 1, 16), ('SetComp', (1, 0, 1, 16), ('Name', (1, 1, 1, 2), 'r', ('Load',)), [('comprehension', ('Tuple', (1, 7, 1, 10), [('Name', (1, 7, 1, 8), 'l', ('Store',)), ('Name', (1, 9, 1, 10), 'm', ('Store',))], ('Store',)), ('Name', (1, 14, 1, 15), 'x', ('Load',)), [], 0)]))], []),
+('Module', [('AsyncFunctionDef', (1, 0, 3, 18), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (2, 1, 2, 17), ('Constant', (2, 1, 2, 17), 'async function', None)), ('Expr', (3, 1, 3, 18), ('Await', (3, 1, 3, 18), ('Call', (3, 7, 3, 18), ('Name', (3, 7, 3, 16), 'something', ('Load',)), [], [])))], [], None, None, [])], []),
+('Module', [('AsyncFunctionDef', (1, 0, 3, 8), 'f', ('arguments', [], [], None, [], [], None, []), [('AsyncFor', (2, 1, 3, 8), ('Name', (2, 11, 2, 12), 'e', ('Store',)), ('Name', (2, 16, 2, 17), 'i', ('Load',)), [('Expr', (2, 19, 2, 20), ('Constant', (2, 19, 2, 20), 1, None))], [('Expr', (3, 7, 3, 8), ('Constant', (3, 7, 3, 8), 2, None))], None)], [], None, None, [])], []),
+('Module', [('AsyncFunctionDef', (1, 0, 2, 21), 'f', ('arguments', [], [], None, [], [], None, []), [('AsyncWith', (2, 1, 2, 21), [('withitem', ('Name', (2, 12, 2, 13), 'a', ('Load',)), ('Name', (2, 17, 2, 18), 'b', ('Store',)))], [('Expr', (2, 20, 2, 21), ('Constant', (2, 20, 2, 21), 1, None))], None)], [], None, None, [])], []),
+('Module', [('Expr', (1, 0, 1, 14), ('Dict', (1, 0, 1, 14), [None, ('Constant', (1, 10, 1, 11), 2, None)], [('Dict', (1, 3, 1, 8), [('Constant', (1, 4, 1, 5), 1, None)], [('Constant', (1, 6, 1, 7), 2, None)]), ('Constant', (1, 12, 1, 13), 3, None)]))], []),
+('Module', [('Expr', (1, 0, 1, 12), ('Set', (1, 0, 1, 12), [('Starred', (1, 1, 1, 8), ('Set', (1, 2, 1, 8), [('Constant', (1, 3, 1, 4), 1, None), ('Constant', (1, 6, 1, 7), 2, None)]), ('Load',)), ('Constant', (1, 10, 1, 11), 3, None)]))], []),
+('Module', [('FunctionDef', (1, 0, 1, 16), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (1, 9, 1, 16), ('Yield', (1, 9, 1, 16), ('Constant', (1, 15, 1, 16), 1, None)))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 22), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (1, 9, 1, 22), ('YieldFrom', (1, 9, 1, 22), ('List', (1, 20, 1, 22), [], ('Load',))))], [], None, None, [])], []),
+('Module', [('AsyncFunctionDef', (1, 0, 2, 21), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (2, 1, 2, 21), ('ListComp', (2, 1, 2, 21), ('Name', (2, 2, 2, 3), 'i', ('Load',)), [('comprehension', ('Name', (2, 14, 2, 15), 'b', ('Store',)), ('Name', (2, 19, 2, 20), 'c', ('Load',)), [], 1)]))], [], None, None, [])], []),
+('Module', [('FunctionDef', (4, 0, 4, 13), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (4, 9, 4, 13))], [('Name', (1, 1, 1, 6), 'deco1', ('Load',)), ('Call', (2, 1, 2, 8), ('Name', (2, 1, 2, 6), 'deco2', ('Load',)), [], []), ('Call', (3, 1, 3, 9), ('Name', (3, 1, 3, 6), 'deco3', ('Load',)), [('Constant', (3, 7, 3, 8), 1, None)], [])], None, None, [])], []),
+('Module', [('AsyncFunctionDef', (4, 0, 4, 19), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (4, 15, 4, 19))], [('Name', (1, 1, 1, 6), 'deco1', ('Load',)), ('Call', (2, 1, 2, 8), ('Name', (2, 1, 2, 6), 'deco2', ('Load',)), [], []), ('Call', (3, 1, 3, 9), ('Name', (3, 1, 3, 6), 'deco3', ('Load',)), [('Constant', (3, 7, 3, 8), 1, None)], [])], None, None, [])], []),
+('Module', [('ClassDef', (4, 0, 4, 13), 'C', [], [], [('Pass', (4, 9, 4, 13))], [('Name', (1, 1, 1, 6), 'deco1', ('Load',)), ('Call', (2, 1, 2, 8), ('Name', (2, 1, 2, 6), 'deco2', ('Load',)), [], []), ('Call', (3, 1, 3, 9), ('Name', (3, 1, 3, 6), 'deco3', ('Load',)), [('Constant', (3, 7, 3, 8), 1, None)], [])], [])], []),
+('Module', [('FunctionDef', (2, 0, 2, 13), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (2, 9, 2, 13))], [('Call', (1, 1, 1, 19), ('Name', (1, 1, 1, 5), 'deco', ('Load',)), [('GeneratorExp', (1, 5, 1, 19), ('Name', (1, 6, 1, 7), 'a', ('Load',)), [('comprehension', ('Name', (1, 12, 1, 13), 'a', ('Store',)), ('Name', (1, 17, 1, 18), 'b', ('Load',)), [], 0)])], [])], None, None, [])], []),
+('Module', [('FunctionDef', (2, 0, 2, 13), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (2, 9, 2, 13))], [('Attribute', (1, 1, 1, 6), ('Attribute', (1, 1, 1, 4), ('Name', (1, 1, 1, 2), 'a', ('Load',)), 'b', ('Load',)), 'c', ('Load',))], None, None, [])], []),
+('Module', [('Expr', (1, 0, 1, 8), ('NamedExpr', (1, 1, 1, 7), ('Name', (1, 1, 1, 2), 'a', ('Store',)), ('Constant', (1, 6, 1, 7), 1, None)))], []),
+('Module', [('If', (1, 0, 1, 19), ('NamedExpr', (1, 3, 1, 13), ('Name', (1, 3, 1, 4), 'a', ('Store',)), ('Call', (1, 8, 1, 13), ('Name', (1, 8, 1, 11), 'foo', ('Load',)), [], [])), [('Pass', (1, 15, 1, 19))], [])], []),
+('Module', [('While', (1, 0, 1, 22), ('NamedExpr', (1, 6, 1, 16), ('Name', (1, 6, 1, 7), 'a', ('Store',)), ('Call', (1, 11, 1, 16), ('Name', (1, 11, 1, 14), 'foo', ('Load',)), [], [])), [('Pass', (1, 18, 1, 22))], [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 18), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [], None, [], [], None, []), [('Pass', (1, 14, 1, 18))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 26), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 12, 1, 13), 'c', None, None), ('arg', (1, 15, 1, 16), 'd', None, None), ('arg', (1, 18, 1, 19), 'e', None, None)], None, [], [], None, []), [('Pass', (1, 22, 1, 26))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 29), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 12, 1, 13), 'c', None, None)], None, [('arg', (1, 18, 1, 19), 'd', None, None), ('arg', (1, 21, 1, 22), 'e', None, None)], [None, None], None, []), [('Pass', (1, 25, 1, 29))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 39), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 12, 1, 13), 'c', None, None)], None, [('arg', (1, 18, 1, 19), 'd', None, None), ('arg', (1, 21, 1, 22), 'e', None, None)], [None, None], ('arg', (1, 26, 1, 32), 'kwargs', None, None), []), [('Pass', (1, 35, 1, 39))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 20), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [], None, [], [], None, [('Constant', (1, 8, 1, 9), 1, None)]), [('Pass', (1, 16, 1, 20))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 29), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None), ('arg', (1, 19, 1, 20), 'c', None, None)], None, [], [], None, [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None), ('Constant', (1, 21, 1, 22), 4, None)]), [('Pass', (1, 25, 1, 29))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 32), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [('Constant', (1, 24, 1, 25), 4, None)], None, [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 28, 1, 32))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 30), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [None], None, [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 26, 1, 30))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 42), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [('Constant', (1, 24, 1, 25), 4, None)], ('arg', (1, 29, 1, 35), 'kwargs', None, None), [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 38, 1, 42))], [], None, None, [])], []),
+('Module', [('FunctionDef', (1, 0, 1, 40), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [None], ('arg', (1, 27, 1, 33), 'kwargs', None, None), [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 36, 1, 40))], [], None, None, [])], []),
+('Module', [('TypeAlias', (1, 0, 1, 12), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [], ('Name', (1, 9, 1, 12), 'int', ('Load',)))], []),
+('Module', [('TypeAlias', (1, 0, 1, 15), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 8), 'T', None, None)], ('Name', (1, 12, 1, 15), 'int', ('Load',)))], []),
+('Module', [('TypeAlias', (1, 0, 1, 32), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 8), 'T', None, None), ('TypeVarTuple', (1, 10, 1, 13), 'Ts', None), ('ParamSpec', (1, 15, 1, 18), 'P', None)], ('Tuple', (1, 22, 1, 32), [('Name', (1, 23, 1, 24), 'T', ('Load',)), ('Name', (1, 26, 1, 28), 'Ts', ('Load',)), ('Name', (1, 30, 1, 31), 'P', ('Load',))], ('Load',)))], []),
+('Module', [('TypeAlias', (1, 0, 1, 37), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 13), 'T', ('Name', (1, 10, 1, 13), 'int', ('Load',)), None), ('TypeVarTuple', (1, 15, 1, 18), 'Ts', None), ('ParamSpec', (1, 20, 1, 23), 'P', None)], ('Tuple', (1, 27, 1, 37), [('Name', (1, 28, 1, 29), 'T', ('Load',)), ('Name', (1, 31, 1, 33), 'Ts', ('Load',)), ('Name', (1, 35, 1, 36), 'P', ('Load',))], ('Load',)))], []),
+('Module', [('TypeAlias', (1, 0, 1, 44), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 20), 'T', ('Tuple', (1, 10, 1, 20), [('Name', (1, 11, 1, 14), 'int', ('Load',)), ('Name', (1, 16, 1, 19), 'str', ('Load',))], ('Load',)), None), ('TypeVarTuple', (1, 22, 1, 25), 'Ts', None), ('ParamSpec', (1, 27, 1, 30), 'P', None)], ('Tuple', (1, 34, 1, 44), [('Name', (1, 35, 1, 36), 'T', ('Load',)), ('Name', (1, 38, 1, 40), 'Ts', ('Load',)), ('Name', (1, 42, 1, 43), 'P', ('Load',))], ('Load',)))], []),
+('Module', [('TypeAlias', (1, 0, 1, 48), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 17), 'T', ('Name', (1, 10, 1, 13), 'int', ('Load',)), ('Constant', (1, 16, 1, 17), 1, None)), ('TypeVarTuple', (1, 19, 1, 26), 'Ts', ('Constant', (1, 25, 1, 26), 2, None)), ('ParamSpec', (1, 28, 1, 34), 'P', ('Constant', (1, 33, 1, 34), 3, None))], ('Tuple', (1, 38, 1, 48), [('Name', (1, 39, 1, 40), 'T', ('Load',)), ('Name', (1, 42, 1, 44), 'Ts', ('Load',)), ('Name', (1, 46, 1, 47), 'P', ('Load',))], ('Load',)))], []),
+('Module', [('ClassDef', (1, 0, 1, 16), 'X', [], [], [('Pass', (1, 12, 1, 16))], [], [('TypeVar', (1, 8, 1, 9), 'T', None, None)])], []),
+('Module', [('ClassDef', (1, 0, 1, 26), 'X', [], [], [('Pass', (1, 22, 1, 26))], [], [('TypeVar', (1, 8, 1, 9), 'T', None, None), ('TypeVarTuple', (1, 11, 1, 14), 'Ts', None), ('ParamSpec', (1, 16, 1, 19), 'P', None)])], []),
+('Module', [('ClassDef', (1, 0, 1, 31), 'X', [], [], [('Pass', (1, 27, 1, 31))], [], [('TypeVar', (1, 8, 1, 14), 'T', ('Name', (1, 11, 1, 14), 'int', ('Load',)), None), ('TypeVarTuple', (1, 16, 1, 19), 'Ts', None), ('ParamSpec', (1, 21, 1, 24), 'P', None)])], []),
+('Module', [('ClassDef', (1, 0, 1, 38), 'X', [], [], [('Pass', (1, 34, 1, 38))], [], [('TypeVar', (1, 8, 1, 21), 'T', ('Tuple', (1, 11, 1, 21), [('Name', (1, 12, 1, 15), 'int', ('Load',)), ('Name', (1, 17, 1, 20), 'str', ('Load',))], ('Load',)), None), ('TypeVarTuple', (1, 23, 1, 26), 'Ts', None), ('ParamSpec', (1, 28, 1, 31), 'P', None)])], []),
+('Module', [('ClassDef', (1, 0, 1, 43), 'X', [], [], [('Pass', (1, 39, 1, 43))], [], [('TypeVar', (1, 8, 1, 18), 'T', ('Name', (1, 11, 1, 14), 'int', ('Load',)), ('Constant', (1, 17, 1, 18), 1, None)), ('TypeVarTuple', (1, 20, 1, 27), 'Ts', ('Constant', (1, 26, 1, 27), 2, None)), ('ParamSpec', (1, 29, 1, 36), 'P', ('Constant', (1, 35, 1, 36), 3, None))])], []),
+('Module', [('FunctionDef', (1, 0, 1, 16), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 12, 1, 16))], [], None, None, [('TypeVar', (1, 6, 1, 7), 'T', None, None)])], []),
+('Module', [('FunctionDef', (1, 0, 1, 26), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 22, 1, 26))], [], None, None, [('TypeVar', (1, 6, 1, 7), 'T', None, None), ('TypeVarTuple', (1, 9, 1, 12), 'Ts', None), ('ParamSpec', (1, 14, 1, 17), 'P', None)])], []),
+('Module', [('FunctionDef', (1, 0, 1, 31), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 27, 1, 31))], [], None, None, [('TypeVar', (1, 6, 1, 12), 'T', ('Name', (1, 9, 1, 12), 'int', ('Load',)), None), ('TypeVarTuple', (1, 14, 1, 17), 'Ts', None), ('ParamSpec', (1, 19, 1, 22), 'P', None)])], []),
+('Module', [('FunctionDef', (1, 0, 1, 38), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 34, 1, 38))], [], None, None, [('TypeVar', (1, 6, 1, 19), 'T', ('Tuple', (1, 9, 1, 19), [('Name', (1, 10, 1, 13), 'int', ('Load',)), ('Name', (1, 15, 1, 18), 'str', ('Load',))], ('Load',)), None), ('TypeVarTuple', (1, 21, 1, 24), 'Ts', None), ('ParamSpec', (1, 26, 1, 29), 'P', None)])], []),
+('Module', [('FunctionDef', (1, 0, 1, 43), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 39, 1, 43))], [], None, None, [('TypeVar', (1, 6, 1, 16), 'T', ('Name', (1, 9, 1, 12), 'int', ('Load',)), ('Constant', (1, 15, 1, 16), 1, None)), ('TypeVarTuple', (1, 18, 1, 25), 'Ts', ('Constant', (1, 24, 1, 25), 2, None)), ('ParamSpec', (1, 27, 1, 34), 'P', ('Constant', (1, 33, 1, 34), 3, None))])], []),
+('Module', [('Match', (1, 0, 3, 6), ('Name', (1, 6, 1, 7), 'x', ('Load',)), [('match_case', ('MatchValue', (2, 6, 2, 7), ('Constant', (2, 6, 2, 7), 1, None)), None, [('Pass', (3, 2, 3, 6))])])], []),
+('Module', [('Match', (1, 0, 5, 6), ('Name', (1, 6, 1, 7), 'x', ('Load',)), [('match_case', ('MatchValue', (2, 6, 2, 7), ('Constant', (2, 6, 2, 7), 1, None)), None, [('Pass', (3, 2, 3, 6))]), ('match_case', ('MatchAs', (4, 6, 4, 7), None, None), None, [('Pass', (5, 2, 5, 6))])])], []),
+]
+single_results = [
+('Interactive', [('Expr', (1, 0, 1, 3), ('BinOp', (1, 0, 1, 3), ('Constant', (1, 0, 1, 1), 1, None), ('Add',), ('Constant', (1, 2, 1, 3), 2, None)))]),
+]
+eval_results = [
+('Expression', ('Constant', (1, 0, 1, 4), None, None)),
+('Expression', ('Constant', (1, 0, 1, 4), True, None)),
+('Expression', ('Constant', (1, 0, 1, 5), False, None)),
+('Expression', ('BoolOp', (1, 0, 1, 7), ('And',), [('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Name', (1, 6, 1, 7), 'b', ('Load',))])),
+('Expression', ('BoolOp', (1, 0, 1, 6), ('Or',), [('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Name', (1, 5, 1, 6), 'b', ('Load',))])),
+('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Add',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
+('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Sub',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
+('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Mult',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
+('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Div',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
+('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('MatMult',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
+('Expression', ('BinOp', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('FloorDiv',), ('Name', (1, 5, 1, 6), 'b', ('Load',)))),
+('Expression', ('BinOp', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Pow',), ('Name', (1, 5, 1, 6), 'b', ('Load',)))),
+('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Mod',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
+('Expression', ('BinOp', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('RShift',), ('Name', (1, 5, 1, 6), 'b', ('Load',)))),
+('Expression', ('BinOp', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('LShift',), ('Name', (1, 5, 1, 6), 'b', ('Load',)))),
+('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('BitXor',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
+('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('BitOr',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
+('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('BitAnd',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
+('Expression', ('UnaryOp', (1, 0, 1, 5), ('Not',), ('Name', (1, 4, 1, 5), 'v', ('Load',)))),
+('Expression', ('UnaryOp', (1, 0, 1, 2), ('UAdd',), ('Name', (1, 1, 1, 2), 'v', ('Load',)))),
+('Expression', ('UnaryOp', (1, 0, 1, 2), ('USub',), ('Name', (1, 1, 1, 2), 'v', ('Load',)))),
+('Expression', ('UnaryOp', (1, 0, 1, 2), ('Invert',), ('Name', (1, 1, 1, 2), 'v', ('Load',)))),
+('Expression', ('Lambda', (1, 0, 1, 11), ('arguments', [], [], None, [], [], None, []), ('Constant', (1, 7, 1, 11), None, None))),
+('Expression', ('Dict', (1, 0, 1, 7), [('Constant', (1, 2, 1, 3), 1, None)], [('Constant', (1, 4, 1, 5), 2, None)])),
+('Expression', ('Dict', (1, 0, 1, 2), [], [])),
+('Expression', ('Set', (1, 0, 1, 7), [('Constant', (1, 1, 1, 5), None, None)])),
+('Expression', ('Dict', (1, 0, 5, 6), [('Constant', (2, 6, 2, 7), 1, None)], [('Constant', (4, 10, 4, 11), 2, None)])),
+('Expression', ('List', (1, 0, 5, 6), [('Constant', (2, 6, 2, 7), 1, None), ('Constant', (4, 8, 4, 9), 1, None)], ('Load',))),
+('Expression', ('Tuple', (1, 0, 4, 6), [('Constant', (2, 6, 2, 7), 1, None)], ('Load',))),
+('Expression', ('Set', (1, 0, 5, 6), [('Constant', (2, 6, 2, 7), 1, None), ('Constant', (4, 8, 4, 9), 1, None)])),
+('Expression', ('ListComp', (1, 0, 1, 19), ('Name', (1, 1, 1, 2), 'a', ('Load',)), [('comprehension', ('Name', (1, 7, 1, 8), 'b', ('Store',)), ('Name', (1, 12, 1, 13), 'c', ('Load',)), [('Name', (1, 17, 1, 18), 'd', ('Load',))], 0)])),
+('Expression', ('GeneratorExp', (1, 0, 1, 19), ('Name', (1, 1, 1, 2), 'a', ('Load',)), [('comprehension', ('Name', (1, 7, 1, 8), 'b', ('Store',)), ('Name', (1, 12, 1, 13), 'c', ('Load',)), [('Name', (1, 17, 1, 18), 'd', ('Load',))], 0)])),
+('Expression', ('SetComp', (1, 0, 1, 19), ('Name', (1, 1, 1, 2), 'a', ('Load',)), [('comprehension', ('Name', (1, 7, 1, 8), 'b', ('Store',)), ('Name', (1, 12, 1, 13), 'c', ('Load',)), [('Name', (1, 17, 1, 18), 'd', ('Load',))], 0)])),
+('Expression', ('DictComp', (1, 0, 1, 25), ('Name', (1, 1, 1, 2), 'k', ('Load',)), ('Name', (1, 4, 1, 5), 'v', ('Load',)), [('comprehension', ('Tuple', (1, 10, 1, 14), [('Name', (1, 10, 1, 11), 'k', ('Store',)), ('Name', (1, 13, 1, 14), 'v', ('Store',))], ('Store',)), ('Name', (1, 18, 1, 19), 'c', ('Load',)), [('Name', (1, 23, 1, 24), 'd', ('Load',))], 0)])),
+('Expression', ('ListComp', (1, 0, 1, 20), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 11, 1, 14), [('Name', (1, 11, 1, 12), 'a', ('Store',)), ('Name', (1, 13, 1, 14), 'b', ('Store',))], ('Store',)), ('Name', (1, 18, 1, 19), 'c', ('Load',)), [], 0)])),
+('Expression', ('ListComp', (1, 0, 1, 22), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 11, 1, 16), [('Name', (1, 12, 1, 13), 'a', ('Store',)), ('Name', (1, 14, 1, 15), 'b', ('Store',))], ('Store',)), ('Name', (1, 20, 1, 21), 'c', ('Load',)), [], 0)])),
+('Expression', ('ListComp', (1, 0, 1, 22), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('List', (1, 11, 1, 16), [('Name', (1, 12, 1, 13), 'a', ('Store',)), ('Name', (1, 14, 1, 15), 'b', ('Store',))], ('Store',)), ('Name', (1, 20, 1, 21), 'c', ('Load',)), [], 0)])),
+('Expression', ('SetComp', (1, 0, 1, 20), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 11, 1, 14), [('Name', (1, 11, 1, 12), 'a', ('Store',)), ('Name', (1, 13, 1, 14), 'b', ('Store',))], ('Store',)), ('Name', (1, 18, 1, 19), 'c', ('Load',)), [], 0)])),
+('Expression', ('SetComp', (1, 0, 1, 22), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 11, 1, 16), [('Name', (1, 12, 1, 13), 'a', ('Store',)), ('Name', (1, 14, 1, 15), 'b', ('Store',))], ('Store',)), ('Name', (1, 20, 1, 21), 'c', ('Load',)), [], 0)])),
+('Expression', ('SetComp', (1, 0, 1, 22), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('List', (1, 11, 1, 16), [('Name', (1, 12, 1, 13), 'a', ('Store',)), ('Name', (1, 14, 1, 15), 'b', ('Store',))], ('Store',)), ('Name', (1, 20, 1, 21), 'c', ('Load',)), [], 0)])),
+('Expression', ('GeneratorExp', (1, 0, 1, 20), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 11, 1, 14), [('Name', (1, 11, 1, 12), 'a', ('Store',)), ('Name', (1, 13, 1, 14), 'b', ('Store',))], ('Store',)), ('Name', (1, 18, 1, 19), 'c', ('Load',)), [], 0)])),
+('Expression', ('GeneratorExp', (1, 0, 1, 22), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 11, 1, 16), [('Name', (1, 12, 1, 13), 'a', ('Store',)), ('Name', (1, 14, 1, 15), 'b', ('Store',))], ('Store',)), ('Name', (1, 20, 1, 21), 'c', ('Load',)), [], 0)])),
+('Expression', ('GeneratorExp', (1, 0, 1, 22), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('List', (1, 11, 1, 16), [('Name', (1, 12, 1, 13), 'a', ('Store',)), ('Name', (1, 14, 1, 15), 'b', ('Store',))], ('Store',)), ('Name', (1, 20, 1, 21), 'c', ('Load',)), [], 0)])),
+('Expression', ('Compare', (1, 0, 1, 9), ('Constant', (1, 0, 1, 1), 1, None), [('Lt',), ('Lt',)], [('Constant', (1, 4, 1, 5), 2, None), ('Constant', (1, 8, 1, 9), 3, None)])),
+('Expression', ('Compare', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), [('Eq',)], [('Name', (1, 5, 1, 6), 'b', ('Load',))])),
+('Expression', ('Compare', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), [('LtE',)], [('Name', (1, 5, 1, 6), 'b', ('Load',))])),
+('Expression', ('Compare', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), [('GtE',)], [('Name', (1, 5, 1, 6), 'b', ('Load',))])),
+('Expression', ('Compare', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), [('NotEq',)], [('Name', (1, 5, 1, 6), 'b', ('Load',))])),
+('Expression', ('Compare', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), [('Is',)], [('Name', (1, 5, 1, 6), 'b', ('Load',))])),
+('Expression', ('Compare', (1, 0, 1, 10), ('Name', (1, 0, 1, 1), 'a', ('Load',)), [('IsNot',)], [('Name', (1, 9, 1, 10), 'b', ('Load',))])),
+('Expression', ('Compare', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), [('In',)], [('Name', (1, 5, 1, 6), 'b', ('Load',))])),
+('Expression', ('Compare', (1, 0, 1, 10), ('Name', (1, 0, 1, 1), 'a', ('Load',)), [('NotIn',)], [('Name', (1, 9, 1, 10), 'b', ('Load',))])),
+('Expression', ('Call', (1, 0, 1, 3), ('Name', (1, 0, 1, 1), 'f', ('Load',)), [], [])),
+('Expression', ('Call', (1, 0, 1, 17), ('Name', (1, 0, 1, 1), 'f', ('Load',)), [('Constant', (1, 2, 1, 3), 1, None), ('Constant', (1, 4, 1, 5), 2, None), ('Starred', (1, 10, 1, 12), ('Name', (1, 11, 1, 12), 'd', ('Load',)), ('Load',))], [('keyword', (1, 6, 1, 9), 'c', ('Constant', (1, 8, 1, 9), 3, None)), ('keyword', (1, 13, 1, 16), None, ('Name', (1, 15, 1, 16), 'e', ('Load',)))])),
+('Expression', ('Call', (1, 0, 1, 10), ('Name', (1, 0, 1, 1), 'f', ('Load',)), [('Starred', (1, 2, 1, 9), ('List', (1, 3, 1, 9), [('Constant', (1, 4, 1, 5), 0, None), ('Constant', (1, 7, 1, 8), 1, None)], ('Load',)), ('Load',))], [])),
+('Expression', ('Call', (1, 0, 1, 15), ('Name', (1, 0, 1, 1), 'f', ('Load',)), [('GeneratorExp', (1, 1, 1, 15), ('Name', (1, 2, 1, 3), 'a', ('Load',)), [('comprehension', ('Name', (1, 8, 1, 9), 'a', ('Store',)), ('Name', (1, 13, 1, 14), 'b', ('Load',)), [], 0)])], [])),
+('Expression', ('Constant', (1, 0, 1, 2), 10, None)),
+('Expression', ('Constant', (1, 0, 1, 2), 1j, None)),
+('Expression', ('Constant', (1, 0, 1, 8), 'string', None)),
+('Expression', ('Attribute', (1, 0, 1, 3), ('Name', (1, 0, 1, 1), 'a', ('Load',)), 'b', ('Load',))),
+('Expression', ('Subscript', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Slice', (1, 2, 1, 5), ('Name', (1, 2, 1, 3), 'b', ('Load',)), ('Name', (1, 4, 1, 5), 'c', ('Load',)), None), ('Load',))),
+('Expression', ('Name', (1, 0, 1, 1), 'v', ('Load',))),
+('Expression', ('List', (1, 0, 1, 7), [('Constant', (1, 1, 1, 2), 1, None), ('Constant', (1, 3, 1, 4), 2, None), ('Constant', (1, 5, 1, 6), 3, None)], ('Load',))),
+('Expression', ('List', (1, 0, 1, 2), [], ('Load',))),
+('Expression', ('Tuple', (1, 0, 1, 5), [('Constant', (1, 0, 1, 1), 1, None), ('Constant', (1, 2, 1, 3), 2, None), ('Constant', (1, 4, 1, 5), 3, None)], ('Load',))),
+('Expression', ('Tuple', (1, 0, 1, 7), [('Constant', (1, 1, 1, 2), 1, None), ('Constant', (1, 3, 1, 4), 2, None), ('Constant', (1, 5, 1, 6), 3, None)], ('Load',))),
+('Expression', ('Tuple', (1, 0, 1, 2), [], ('Load',))),
+('Expression', ('Call', (1, 0, 1, 17), ('Attribute', (1, 0, 1, 7), ('Attribute', (1, 0, 1, 5), ('Attribute', (1, 0, 1, 3), ('Name', (1, 0, 1, 1), 'a', ('Load',)), 'b', ('Load',)), 'c', ('Load',)), 'd', ('Load',)), [('Subscript', (1, 8, 1, 16), ('Attribute', (1, 8, 1, 11), ('Name', (1, 8, 1, 9), 'a', ('Load',)), 'b', ('Load',)), ('Slice', (1, 12, 1, 15), ('Constant', (1, 12, 1, 13), 1, None), ('Constant', (1, 14, 1, 15), 2, None), None), ('Load',))], [])),
+('Expression', ('Subscript', (1, 0, 1, 7), ('List', (1, 0, 1, 3), [('Constant', (1, 1, 1, 2), 5, None)], ('Load',)), ('Slice', (1, 4, 1, 6), ('Constant', (1, 4, 1, 5), 1, None), None, None), ('Load',))),
+('Expression', ('Subscript', (1, 0, 1, 7), ('List', (1, 0, 1, 3), [('Constant', (1, 1, 1, 2), 5, None)], ('Load',)), ('Slice', (1, 4, 1, 6), None, ('Constant', (1, 5, 1, 6), 1, None), None), ('Load',))),
+('Expression', ('Subscript', (1, 0, 1, 8), ('List', (1, 0, 1, 3), [('Constant', (1, 1, 1, 2), 5, None)], ('Load',)), ('Slice', (1, 4, 1, 7), None, None, ('Constant', (1, 6, 1, 7), 1, None)), ('Load',))),
+('Expression', ('Subscript', (1, 0, 1, 10), ('List', (1, 0, 1, 3), [('Constant', (1, 1, 1, 2), 5, None)], ('Load',)), ('Slice', (1, 4, 1, 9), ('Constant', (1, 4, 1, 5), 1, None), ('Constant', (1, 6, 1, 7), 1, None), ('Constant', (1, 8, 1, 9), 1, None)), ('Load',))),
+('Expression', ('IfExp', (1, 0, 1, 21), ('Name', (1, 9, 1, 10), 'x', ('Load',)), ('Call', (1, 0, 1, 5), ('Name', (1, 0, 1, 3), 'foo', ('Load',)), [], []), ('Call', (1, 16, 1, 21), ('Name', (1, 16, 1, 19), 'bar', ('Load',)), [], []))),
+('Expression', ('JoinedStr', (1, 0, 1, 6), [('FormattedValue', (1, 2, 1, 5), ('Name', (1, 3, 1, 4), 'a', ('Load',)), -1, None)])),
+('Expression', ('JoinedStr', (1, 0, 1, 10), [('FormattedValue', (1, 2, 1, 9), ('Name', (1, 3, 1, 4), 'a', ('Load',)), -1, ('JoinedStr', (1, 4, 1, 8), [('Constant', (1, 5, 1, 8), '.2f', None)]))])),
+('Expression', ('JoinedStr', (1, 0, 1, 8), [('FormattedValue', (1, 2, 1, 7), ('Name', (1, 3, 1, 4), 'a', ('Load',)), 114, None)])),
+('Expression', ('JoinedStr', (1, 0, 1, 11), [('Constant', (1, 2, 1, 6), 'foo(', None), ('FormattedValue', (1, 6, 1, 9), ('Name', (1, 7, 1, 8), 'a', ('Load',)), -1, None), ('Constant', (1, 9, 1, 10), ')', None)])),
+]
+main()
diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast/test_ast.py
similarity index 68%
rename from Lib/test/test_ast.py
rename to Lib/test/test_ast/test_ast.py
index 55725ec36fd3a7..0a3edef4678546 100644
--- a/Lib/test/test_ast.py
+++ b/Lib/test/test_ast/test_ast.py
@@ -19,389 +19,16 @@
from test import support
from test.support import os_helper, script_helper
from test.support.ast_helper import ASTTestMixin
+from test.test_ast.utils import to_tuple
+from test.test_ast.snippets import (
+ eval_tests, eval_results, exec_tests, exec_results, single_tests, single_results
+)
-def to_tuple(t):
- if t is None or isinstance(t, (str, int, complex, float, bytes)) or t is Ellipsis:
- return t
- elif isinstance(t, list):
- return [to_tuple(e) for e in t]
- result = [t.__class__.__name__]
- if hasattr(t, 'lineno') and hasattr(t, 'col_offset'):
- result.append((t.lineno, t.col_offset))
- if hasattr(t, 'end_lineno') and hasattr(t, 'end_col_offset'):
- result[-1] += (t.end_lineno, t.end_col_offset)
- if t._fields is None:
- return tuple(result)
- for f in t._fields:
- result.append(to_tuple(getattr(t, f)))
- return tuple(result)
STDLIB = os.path.dirname(ast.__file__)
STDLIB_FILES = [fn for fn in os.listdir(STDLIB) if fn.endswith(".py")]
STDLIB_FILES.extend(["test/test_grammar.py", "test/test_unpack_ex.py"])
-# These tests are compiled through "exec"
-# There should be at least one test per statement
-exec_tests = [
- # Module docstring
- "'module docstring'",
- # FunctionDef
- "def f(): pass",
- # FunctionDef with docstring
- "def f(): 'function docstring'",
- # FunctionDef with arg
- "def f(a): pass",
- # FunctionDef with arg and default value
- "def f(a=0): pass",
- # FunctionDef with varargs
- "def f(*args): pass",
- # FunctionDef with varargs as TypeVarTuple
- "def f(*args: *Ts): pass",
- # FunctionDef with varargs as unpacked Tuple
- "def f(*args: *tuple[int, ...]): pass",
- # FunctionDef with varargs as unpacked Tuple *and* TypeVarTuple
- "def f(*args: *tuple[int, *Ts]): pass",
- # FunctionDef with kwargs
- "def f(**kwargs): pass",
- # FunctionDef with all kind of args and docstring
- "def f(a, b=1, c=None, d=[], e={}, *args, f=42, **kwargs): 'doc for f()'",
- # FunctionDef with type annotation on return involving unpacking
- "def f() -> tuple[*Ts]: pass",
- "def f() -> tuple[int, *Ts]: pass",
- "def f() -> tuple[int, *tuple[int, ...]]: pass",
- # ClassDef
- "class C:pass",
- # ClassDef with docstring
- "class C: 'docstring for class C'",
- # ClassDef, new style class
- "class C(object): pass",
- # Classdef with multiple bases
- "class C(A, B): pass",
- # Return
- "def f():return 1",
- "def f():return",
- # Delete
- "del v",
- # Assign
- "v = 1",
- "a,b = c",
- "(a,b) = c",
- "[a,b] = c",
- "a[b] = c",
- # AnnAssign with unpacked types
- "x: tuple[*Ts]",
- "x: tuple[int, *Ts]",
- "x: tuple[int, *tuple[str, ...]]",
- # AugAssign
- "v += 1",
- "v -= 1",
- "v *= 1",
- "v @= 1",
- "v /= 1",
- "v %= 1",
- "v **= 1",
- "v <<= 1",
- "v >>= 1",
- "v |= 1",
- "v ^= 1",
- "v &= 1",
- "v //= 1",
- # For
- "for v in v:pass",
- # For-Else
- "for v in v:\n pass\nelse:\n pass",
- # While
- "while v:pass",
- # While-Else
- "while v:\n pass\nelse:\n pass",
- # If-Elif-Else
- "if v:pass",
- "if a:\n pass\nelif b:\n pass",
- "if a:\n pass\nelse:\n pass",
- "if a:\n pass\nelif b:\n pass\nelse:\n pass",
- "if a:\n pass\nelif b:\n pass\nelif b:\n pass\nelif b:\n pass\nelse:\n pass",
- # With
- "with x: pass",
- "with x, y: pass",
- "with x as y: pass",
- "with x as y, z as q: pass",
- "with (x as y): pass",
- "with (x, y): pass",
- # Raise
- "raise",
- "raise Exception('string')",
- "raise Exception",
- "raise Exception('string') from None",
- # TryExcept
- "try:\n pass\nexcept Exception:\n pass",
- "try:\n pass\nexcept Exception as exc:\n pass",
- # TryFinally
- "try:\n pass\nfinally:\n pass",
- # TryStarExcept
- "try:\n pass\nexcept* Exception:\n pass",
- "try:\n pass\nexcept* Exception as exc:\n pass",
- # TryExceptFinallyElse
- "try:\n pass\nexcept Exception:\n pass\nelse: pass\nfinally:\n pass",
- "try:\n pass\nexcept Exception as exc:\n pass\nelse: pass\nfinally:\n pass",
- "try:\n pass\nexcept* Exception as exc:\n pass\nelse: pass\nfinally:\n pass",
- # Assert
- "assert v",
- # Assert with message
- "assert v, 'message'",
- # Import
- "import sys",
- "import foo as bar",
- # ImportFrom
- "from sys import x as y",
- "from sys import v",
- # Global
- "global v",
- # Expr
- "1",
- # Pass,
- "pass",
- # Break
- "for v in v:break",
- # Continue
- "for v in v:continue",
- # for statements with naked tuples (see http://bugs.python.org/issue6704)
- "for a,b in c: pass",
- "for (a,b) in c: pass",
- "for [a,b] in c: pass",
- # Multiline generator expression (test for .lineno & .col_offset)
- """(
- (
- Aa
- ,
- Bb
- )
- for
- Aa
- ,
- Bb in Cc
- )""",
- # dictcomp
- "{a : b for w in x for m in p if g}",
- # dictcomp with naked tuple
- "{a : b for v,w in x}",
- # setcomp
- "{r for l in x if g}",
- # setcomp with naked tuple
- "{r for l,m in x}",
- # AsyncFunctionDef
- "async def f():\n 'async function'\n await something()",
- # AsyncFor
- "async def f():\n async for e in i: 1\n else: 2",
- # AsyncWith
- "async def f():\n async with a as b: 1",
- # PEP 448: Additional Unpacking Generalizations
- "{**{1:2}, 2:3}",
- "{*{1, 2}, 3}",
- # Function with yield (from)
- "def f(): yield 1",
- "def f(): yield from []",
- # Asynchronous comprehensions
- "async def f():\n [i async for b in c]",
- # Decorated FunctionDef
- "@deco1\n@deco2()\n@deco3(1)\ndef f(): pass",
- # Decorated AsyncFunctionDef
- "@deco1\n@deco2()\n@deco3(1)\nasync def f(): pass",
- # Decorated ClassDef
- "@deco1\n@deco2()\n@deco3(1)\nclass C: pass",
- # Decorator with generator argument
- "@deco(a for a in b)\ndef f(): pass",
- # Decorator with attribute
- "@a.b.c\ndef f(): pass",
- # Simple assignment expression
- "(a := 1)",
- # Assignment expression in if statement
- "if a := foo(): pass",
- # Assignment expression in while
- "while a := foo(): pass",
- # Positional-only arguments
- "def f(a, /,): pass",
- "def f(a, /, c, d, e): pass",
- "def f(a, /, c, *, d, e): pass",
- "def f(a, /, c, *, d, e, **kwargs): pass",
- # Positional-only arguments with defaults
- "def f(a=1, /,): pass",
- "def f(a=1, /, b=2, c=4): pass",
- "def f(a=1, /, b=2, *, c=4): pass",
- "def f(a=1, /, b=2, *, c): pass",
- "def f(a=1, /, b=2, *, c=4, **kwargs): pass",
- "def f(a=1, /, b=2, *, c, **kwargs): pass",
- # Type aliases
- "type X = int",
- "type X[T] = int",
- "type X[T, *Ts, **P] = (T, Ts, P)",
- "type X[T: int, *Ts, **P] = (T, Ts, P)",
- "type X[T: (int, str), *Ts, **P] = (T, Ts, P)",
- "type X[T: int = 1, *Ts = 2, **P =3] = (T, Ts, P)",
- # Generic classes
- "class X[T]: pass",
- "class X[T, *Ts, **P]: pass",
- "class X[T: int, *Ts, **P]: pass",
- "class X[T: (int, str), *Ts, **P]: pass",
- "class X[T: int = 1, *Ts = 2, **P = 3]: pass",
- # Generic functions
- "def f[T](): pass",
- "def f[T, *Ts, **P](): pass",
- "def f[T: int, *Ts, **P](): pass",
- "def f[T: (int, str), *Ts, **P](): pass",
- "def f[T: int = 1, *Ts = 2, **P = 3](): pass",
- # Match
- "match x:\n\tcase 1:\n\t\tpass",
- # Match with _
- "match x:\n\tcase 1:\n\t\tpass\n\tcase _:\n\t\tpass",
-]
-
-# These are compiled through "single"
-# because of overlap with "eval", it just tests what
-# can't be tested with "eval"
-single_tests = [
- "1+2"
-]
-
-# These are compiled through "eval"
-# It should test all expressions
-eval_tests = [
- # Constant(value=None)
- "None",
- # True
- "True",
- # False
- "False",
- # BoolOp
- "a and b",
- "a or b",
- # BinOp
- "a + b",
- "a - b",
- "a * b",
- "a / b",
- "a @ b",
- "a // b",
- "a ** b",
- "a % b",
- "a >> b",
- "a << b",
- "a ^ b",
- "a | b",
- "a & b",
- # UnaryOp
- "not v",
- "+v",
- "-v",
- "~v",
- # Lambda
- "lambda:None",
- # Dict
- "{ 1:2 }",
- # Empty dict
- "{}",
- # Set
- "{None,}",
- # Multiline dict (test for .lineno & .col_offset)
- """{
- 1
- :
- 2
- }""",
- # Multiline list
- """[
- 1
- ,
- 1
- ]""",
- # Multiline tuple
- """(
- 1
- ,
- )""",
- # Multiline set
- """{
- 1
- ,
- 1
- }""",
- # ListComp
- "[a for b in c if d]",
- # GeneratorExp
- "(a for b in c if d)",
- # SetComp
- "{a for b in c if d}",
- # DictComp
- "{k: v for k, v in c if d}",
- # Comprehensions with multiple for targets
- "[(a,b) for a,b in c]",
- "[(a,b) for (a,b) in c]",
- "[(a,b) for [a,b] in c]",
- "{(a,b) for a,b in c}",
- "{(a,b) for (a,b) in c}",
- "{(a,b) for [a,b] in c}",
- "((a,b) for a,b in c)",
- "((a,b) for (a,b) in c)",
- "((a,b) for [a,b] in c)",
- # Async comprehensions - async comprehensions can't work outside an asynchronous function
- #
- # Yield - yield expressions can't work outside a function
- #
- # Compare
- "1 < 2 < 3",
- "a == b",
- "a <= b",
- "a >= b",
- "a != b",
- "a is b",
- "a is not b",
- "a in b",
- "a not in b",
- # Call without argument
- "f()",
- # Call
- "f(1,2,c=3,*d,**e)",
- # Call with multi-character starred
- "f(*[0, 1])",
- # Call with a generator argument
- "f(a for a in b)",
- # Constant(value=int())
- "10",
- # Complex num
- "1j",
- # Constant(value=str())
- "'string'",
- # Attribute
- "a.b",
- # Subscript
- "a[b:c]",
- # Name
- "v",
- # List
- "[1,2,3]",
- # Empty list
- "[]",
- # Tuple
- "1,2,3",
- # Tuple
- "(1,2,3)",
- # Empty tuple
- "()",
- # Combination
- "a.b.c.d(a.b[1:2])",
- # Slice
- "[5][1:]",
- "[5][:1]",
- "[5][::1]",
- "[5][1:1:1]",
- # IfExp
- "foo() if x else bar()",
- # JoinedStr and FormattedValue
- "f'{a}'",
- "f'{a:.2f}'",
- "f'{a!r}'",
- "f'foo({a})'",
-]
-
class AST_Tests(unittest.TestCase):
maxDiff = None
@@ -3408,238 +3035,3 @@ def test_cli_file_input(self):
self.assertEqual(expected.splitlines(),
res.out.decode("utf8").splitlines())
self.assertEqual(res.rc, 0)
-
-
-def main():
- if __name__ != '__main__':
- return
- if sys.argv[1:] == ['-g']:
- for statements, kind in ((exec_tests, "exec"), (single_tests, "single"),
- (eval_tests, "eval")):
- print(kind+"_results = [")
- for statement in statements:
- tree = ast.parse(statement, "?", kind)
- print("%r," % (to_tuple(tree),))
- print("]")
- print("main()")
- raise SystemExit
- unittest.main()
-
-#### EVERYTHING BELOW IS GENERATED BY python Lib/test/test_ast.py -g #####
-exec_results = [
-('Module', [('Expr', (1, 0, 1, 18), ('Constant', (1, 0, 1, 18), 'module docstring', None))], []),
-('Module', [('FunctionDef', (1, 0, 1, 13), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 9, 1, 13))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 29), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (1, 9, 1, 29), ('Constant', (1, 9, 1, 29), 'function docstring', None))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 14), 'f', ('arguments', [], [('arg', (1, 6, 1, 7), 'a', None, None)], None, [], [], None, []), [('Pass', (1, 10, 1, 14))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 16), 'f', ('arguments', [], [('arg', (1, 6, 1, 7), 'a', None, None)], None, [], [], None, [('Constant', (1, 8, 1, 9), 0, None)]), [('Pass', (1, 12, 1, 16))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 18), 'f', ('arguments', [], [], ('arg', (1, 7, 1, 11), 'args', None, None), [], [], None, []), [('Pass', (1, 14, 1, 18))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 23), 'f', ('arguments', [], [], ('arg', (1, 7, 1, 16), 'args', ('Starred', (1, 13, 1, 16), ('Name', (1, 14, 1, 16), 'Ts', ('Load',)), ('Load',)), None), [], [], None, []), [('Pass', (1, 19, 1, 23))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 36), 'f', ('arguments', [], [], ('arg', (1, 7, 1, 29), 'args', ('Starred', (1, 13, 1, 29), ('Subscript', (1, 14, 1, 29), ('Name', (1, 14, 1, 19), 'tuple', ('Load',)), ('Tuple', (1, 20, 1, 28), [('Name', (1, 20, 1, 23), 'int', ('Load',)), ('Constant', (1, 25, 1, 28), Ellipsis, None)], ('Load',)), ('Load',)), ('Load',)), None), [], [], None, []), [('Pass', (1, 32, 1, 36))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 36), 'f', ('arguments', [], [], ('arg', (1, 7, 1, 29), 'args', ('Starred', (1, 13, 1, 29), ('Subscript', (1, 14, 1, 29), ('Name', (1, 14, 1, 19), 'tuple', ('Load',)), ('Tuple', (1, 20, 1, 28), [('Name', (1, 20, 1, 23), 'int', ('Load',)), ('Starred', (1, 25, 1, 28), ('Name', (1, 26, 1, 28), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), ('Load',)), None), [], [], None, []), [('Pass', (1, 32, 1, 36))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 21), 'f', ('arguments', [], [], None, [], [], ('arg', (1, 8, 1, 14), 'kwargs', None, None), []), [('Pass', (1, 17, 1, 21))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 71), 'f', ('arguments', [], [('arg', (1, 6, 1, 7), 'a', None, None), ('arg', (1, 9, 1, 10), 'b', None, None), ('arg', (1, 14, 1, 15), 'c', None, None), ('arg', (1, 22, 1, 23), 'd', None, None), ('arg', (1, 28, 1, 29), 'e', None, None)], ('arg', (1, 35, 1, 39), 'args', None, None), [('arg', (1, 41, 1, 42), 'f', None, None)], [('Constant', (1, 43, 1, 45), 42, None)], ('arg', (1, 49, 1, 55), 'kwargs', None, None), [('Constant', (1, 11, 1, 12), 1, None), ('Constant', (1, 16, 1, 20), None, None), ('List', (1, 24, 1, 26), [], ('Load',)), ('Dict', (1, 30, 1, 32), [], [])]), [('Expr', (1, 58, 1, 71), ('Constant', (1, 58, 1, 71), 'doc for f()', None))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 27), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 23, 1, 27))], [], ('Subscript', (1, 11, 1, 21), ('Name', (1, 11, 1, 16), 'tuple', ('Load',)), ('Tuple', (1, 17, 1, 20), [('Starred', (1, 17, 1, 20), ('Name', (1, 18, 1, 20), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 32), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 28, 1, 32))], [], ('Subscript', (1, 11, 1, 26), ('Name', (1, 11, 1, 16), 'tuple', ('Load',)), ('Tuple', (1, 17, 1, 25), [('Name', (1, 17, 1, 20), 'int', ('Load',)), ('Starred', (1, 22, 1, 25), ('Name', (1, 23, 1, 25), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 45), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 41, 1, 45))], [], ('Subscript', (1, 11, 1, 39), ('Name', (1, 11, 1, 16), 'tuple', ('Load',)), ('Tuple', (1, 17, 1, 38), [('Name', (1, 17, 1, 20), 'int', ('Load',)), ('Starred', (1, 22, 1, 38), ('Subscript', (1, 23, 1, 38), ('Name', (1, 23, 1, 28), 'tuple', ('Load',)), ('Tuple', (1, 29, 1, 37), [('Name', (1, 29, 1, 32), 'int', ('Load',)), ('Constant', (1, 34, 1, 37), Ellipsis, None)], ('Load',)), ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, [])], []),
-('Module', [('ClassDef', (1, 0, 1, 12), 'C', [], [], [('Pass', (1, 8, 1, 12))], [], [])], []),
-('Module', [('ClassDef', (1, 0, 1, 32), 'C', [], [], [('Expr', (1, 9, 1, 32), ('Constant', (1, 9, 1, 32), 'docstring for class C', None))], [], [])], []),
-('Module', [('ClassDef', (1, 0, 1, 21), 'C', [('Name', (1, 8, 1, 14), 'object', ('Load',))], [], [('Pass', (1, 17, 1, 21))], [], [])], []),
-('Module', [('ClassDef', (1, 0, 1, 19), 'C', [('Name', (1, 8, 1, 9), 'A', ('Load',)), ('Name', (1, 11, 1, 12), 'B', ('Load',))], [], [('Pass', (1, 15, 1, 19))], [], [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 16), 'f', ('arguments', [], [], None, [], [], None, []), [('Return', (1, 8, 1, 16), ('Constant', (1, 15, 1, 16), 1, None))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 14), 'f', ('arguments', [], [], None, [], [], None, []), [('Return', (1, 8, 1, 14), None)], [], None, None, [])], []),
-('Module', [('Delete', (1, 0, 1, 5), [('Name', (1, 4, 1, 5), 'v', ('Del',))])], []),
-('Module', [('Assign', (1, 0, 1, 5), [('Name', (1, 0, 1, 1), 'v', ('Store',))], ('Constant', (1, 4, 1, 5), 1, None), None)], []),
-('Module', [('Assign', (1, 0, 1, 7), [('Tuple', (1, 0, 1, 3), [('Name', (1, 0, 1, 1), 'a', ('Store',)), ('Name', (1, 2, 1, 3), 'b', ('Store',))], ('Store',))], ('Name', (1, 6, 1, 7), 'c', ('Load',)), None)], []),
-('Module', [('Assign', (1, 0, 1, 9), [('Tuple', (1, 0, 1, 5), [('Name', (1, 1, 1, 2), 'a', ('Store',)), ('Name', (1, 3, 1, 4), 'b', ('Store',))], ('Store',))], ('Name', (1, 8, 1, 9), 'c', ('Load',)), None)], []),
-('Module', [('Assign', (1, 0, 1, 9), [('List', (1, 0, 1, 5), [('Name', (1, 1, 1, 2), 'a', ('Store',)), ('Name', (1, 3, 1, 4), 'b', ('Store',))], ('Store',))], ('Name', (1, 8, 1, 9), 'c', ('Load',)), None)], []),
-('Module', [('Assign', (1, 0, 1, 8), [('Subscript', (1, 0, 1, 4), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Name', (1, 2, 1, 3), 'b', ('Load',)), ('Store',))], ('Name', (1, 7, 1, 8), 'c', ('Load',)), None)], []),
-('Module', [('AnnAssign', (1, 0, 1, 13), ('Name', (1, 0, 1, 1), 'x', ('Store',)), ('Subscript', (1, 3, 1, 13), ('Name', (1, 3, 1, 8), 'tuple', ('Load',)), ('Tuple', (1, 9, 1, 12), [('Starred', (1, 9, 1, 12), ('Name', (1, 10, 1, 12), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, 1)], []),
-('Module', [('AnnAssign', (1, 0, 1, 18), ('Name', (1, 0, 1, 1), 'x', ('Store',)), ('Subscript', (1, 3, 1, 18), ('Name', (1, 3, 1, 8), 'tuple', ('Load',)), ('Tuple', (1, 9, 1, 17), [('Name', (1, 9, 1, 12), 'int', ('Load',)), ('Starred', (1, 14, 1, 17), ('Name', (1, 15, 1, 17), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, 1)], []),
-('Module', [('AnnAssign', (1, 0, 1, 31), ('Name', (1, 0, 1, 1), 'x', ('Store',)), ('Subscript', (1, 3, 1, 31), ('Name', (1, 3, 1, 8), 'tuple', ('Load',)), ('Tuple', (1, 9, 1, 30), [('Name', (1, 9, 1, 12), 'int', ('Load',)), ('Starred', (1, 14, 1, 30), ('Subscript', (1, 15, 1, 30), ('Name', (1, 15, 1, 20), 'tuple', ('Load',)), ('Tuple', (1, 21, 1, 29), [('Name', (1, 21, 1, 24), 'str', ('Load',)), ('Constant', (1, 26, 1, 29), Ellipsis, None)], ('Load',)), ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, 1)], []),
-('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('Add',), ('Constant', (1, 5, 1, 6), 1, None))], []),
-('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('Sub',), ('Constant', (1, 5, 1, 6), 1, None))], []),
-('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('Mult',), ('Constant', (1, 5, 1, 6), 1, None))], []),
-('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('MatMult',), ('Constant', (1, 5, 1, 6), 1, None))], []),
-('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('Div',), ('Constant', (1, 5, 1, 6), 1, None))], []),
-('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('Mod',), ('Constant', (1, 5, 1, 6), 1, None))], []),
-('Module', [('AugAssign', (1, 0, 1, 7), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('Pow',), ('Constant', (1, 6, 1, 7), 1, None))], []),
-('Module', [('AugAssign', (1, 0, 1, 7), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('LShift',), ('Constant', (1, 6, 1, 7), 1, None))], []),
-('Module', [('AugAssign', (1, 0, 1, 7), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('RShift',), ('Constant', (1, 6, 1, 7), 1, None))], []),
-('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('BitOr',), ('Constant', (1, 5, 1, 6), 1, None))], []),
-('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('BitXor',), ('Constant', (1, 5, 1, 6), 1, None))], []),
-('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('BitAnd',), ('Constant', (1, 5, 1, 6), 1, None))], []),
-('Module', [('AugAssign', (1, 0, 1, 7), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('FloorDiv',), ('Constant', (1, 6, 1, 7), 1, None))], []),
-('Module', [('For', (1, 0, 1, 15), ('Name', (1, 4, 1, 5), 'v', ('Store',)), ('Name', (1, 9, 1, 10), 'v', ('Load',)), [('Pass', (1, 11, 1, 15))], [], None)], []),
-('Module', [('For', (1, 0, 4, 6), ('Name', (1, 4, 1, 5), 'v', ('Store',)), ('Name', (1, 9, 1, 10), 'v', ('Load',)), [('Pass', (2, 2, 2, 6))], [('Pass', (4, 2, 4, 6))], None)], []),
-('Module', [('While', (1, 0, 1, 12), ('Name', (1, 6, 1, 7), 'v', ('Load',)), [('Pass', (1, 8, 1, 12))], [])], []),
-('Module', [('While', (1, 0, 4, 6), ('Name', (1, 6, 1, 7), 'v', ('Load',)), [('Pass', (2, 2, 2, 6))], [('Pass', (4, 2, 4, 6))])], []),
-('Module', [('If', (1, 0, 1, 9), ('Name', (1, 3, 1, 4), 'v', ('Load',)), [('Pass', (1, 5, 1, 9))], [])], []),
-('Module', [('If', (1, 0, 4, 6), ('Name', (1, 3, 1, 4), 'a', ('Load',)), [('Pass', (2, 2, 2, 6))], [('If', (3, 0, 4, 6), ('Name', (3, 5, 3, 6), 'b', ('Load',)), [('Pass', (4, 2, 4, 6))], [])])], []),
-('Module', [('If', (1, 0, 4, 6), ('Name', (1, 3, 1, 4), 'a', ('Load',)), [('Pass', (2, 2, 2, 6))], [('Pass', (4, 2, 4, 6))])], []),
-('Module', [('If', (1, 0, 6, 6), ('Name', (1, 3, 1, 4), 'a', ('Load',)), [('Pass', (2, 2, 2, 6))], [('If', (3, 0, 6, 6), ('Name', (3, 5, 3, 6), 'b', ('Load',)), [('Pass', (4, 2, 4, 6))], [('Pass', (6, 2, 6, 6))])])], []),
-('Module', [('If', (1, 0, 10, 6), ('Name', (1, 3, 1, 4), 'a', ('Load',)), [('Pass', (2, 2, 2, 6))], [('If', (3, 0, 10, 6), ('Name', (3, 5, 3, 6), 'b', ('Load',)), [('Pass', (4, 2, 4, 6))], [('If', (5, 0, 10, 6), ('Name', (5, 5, 5, 6), 'b', ('Load',)), [('Pass', (6, 2, 6, 6))], [('If', (7, 0, 10, 6), ('Name', (7, 5, 7, 6), 'b', ('Load',)), [('Pass', (8, 2, 8, 6))], [('Pass', (10, 2, 10, 6))])])])])], []),
-('Module', [('With', (1, 0, 1, 12), [('withitem', ('Name', (1, 5, 1, 6), 'x', ('Load',)), None)], [('Pass', (1, 8, 1, 12))], None)], []),
-('Module', [('With', (1, 0, 1, 15), [('withitem', ('Name', (1, 5, 1, 6), 'x', ('Load',)), None), ('withitem', ('Name', (1, 8, 1, 9), 'y', ('Load',)), None)], [('Pass', (1, 11, 1, 15))], None)], []),
-('Module', [('With', (1, 0, 1, 17), [('withitem', ('Name', (1, 5, 1, 6), 'x', ('Load',)), ('Name', (1, 10, 1, 11), 'y', ('Store',)))], [('Pass', (1, 13, 1, 17))], None)], []),
-('Module', [('With', (1, 0, 1, 25), [('withitem', ('Name', (1, 5, 1, 6), 'x', ('Load',)), ('Name', (1, 10, 1, 11), 'y', ('Store',))), ('withitem', ('Name', (1, 13, 1, 14), 'z', ('Load',)), ('Name', (1, 18, 1, 19), 'q', ('Store',)))], [('Pass', (1, 21, 1, 25))], None)], []),
-('Module', [('With', (1, 0, 1, 19), [('withitem', ('Name', (1, 6, 1, 7), 'x', ('Load',)), ('Name', (1, 11, 1, 12), 'y', ('Store',)))], [('Pass', (1, 15, 1, 19))], None)], []),
-('Module', [('With', (1, 0, 1, 17), [('withitem', ('Name', (1, 6, 1, 7), 'x', ('Load',)), None), ('withitem', ('Name', (1, 9, 1, 10), 'y', ('Load',)), None)], [('Pass', (1, 13, 1, 17))], None)], []),
-('Module', [('Raise', (1, 0, 1, 5), None, None)], []),
-('Module', [('Raise', (1, 0, 1, 25), ('Call', (1, 6, 1, 25), ('Name', (1, 6, 1, 15), 'Exception', ('Load',)), [('Constant', (1, 16, 1, 24), 'string', None)], []), None)], []),
-('Module', [('Raise', (1, 0, 1, 15), ('Name', (1, 6, 1, 15), 'Exception', ('Load',)), None)], []),
-('Module', [('Raise', (1, 0, 1, 35), ('Call', (1, 6, 1, 25), ('Name', (1, 6, 1, 15), 'Exception', ('Load',)), [('Constant', (1, 16, 1, 24), 'string', None)], []), ('Constant', (1, 31, 1, 35), None, None))], []),
-('Module', [('Try', (1, 0, 4, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 7, 3, 16), 'Exception', ('Load',)), None, [('Pass', (4, 2, 4, 6))])], [], [])], []),
-('Module', [('Try', (1, 0, 4, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 7, 3, 16), 'Exception', ('Load',)), 'exc', [('Pass', (4, 2, 4, 6))])], [], [])], []),
-('Module', [('Try', (1, 0, 4, 6), [('Pass', (2, 2, 2, 6))], [], [], [('Pass', (4, 2, 4, 6))])], []),
-('Module', [('TryStar', (1, 0, 4, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 8, 3, 17), 'Exception', ('Load',)), None, [('Pass', (4, 2, 4, 6))])], [], [])], []),
-('Module', [('TryStar', (1, 0, 4, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 8, 3, 17), 'Exception', ('Load',)), 'exc', [('Pass', (4, 2, 4, 6))])], [], [])], []),
-('Module', [('Try', (1, 0, 7, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 7, 3, 16), 'Exception', ('Load',)), None, [('Pass', (4, 2, 4, 6))])], [('Pass', (5, 7, 5, 11))], [('Pass', (7, 2, 7, 6))])], []),
-('Module', [('Try', (1, 0, 7, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 7, 3, 16), 'Exception', ('Load',)), 'exc', [('Pass', (4, 2, 4, 6))])], [('Pass', (5, 7, 5, 11))], [('Pass', (7, 2, 7, 6))])], []),
-('Module', [('TryStar', (1, 0, 7, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 8, 3, 17), 'Exception', ('Load',)), 'exc', [('Pass', (4, 2, 4, 6))])], [('Pass', (5, 7, 5, 11))], [('Pass', (7, 2, 7, 6))])], []),
-('Module', [('Assert', (1, 0, 1, 8), ('Name', (1, 7, 1, 8), 'v', ('Load',)), None)], []),
-('Module', [('Assert', (1, 0, 1, 19), ('Name', (1, 7, 1, 8), 'v', ('Load',)), ('Constant', (1, 10, 1, 19), 'message', None))], []),
-('Module', [('Import', (1, 0, 1, 10), [('alias', (1, 7, 1, 10), 'sys', None)])], []),
-('Module', [('Import', (1, 0, 1, 17), [('alias', (1, 7, 1, 17), 'foo', 'bar')])], []),
-('Module', [('ImportFrom', (1, 0, 1, 22), 'sys', [('alias', (1, 16, 1, 22), 'x', 'y')], 0)], []),
-('Module', [('ImportFrom', (1, 0, 1, 17), 'sys', [('alias', (1, 16, 1, 17), 'v', None)], 0)], []),
-('Module', [('Global', (1, 0, 1, 8), ['v'])], []),
-('Module', [('Expr', (1, 0, 1, 1), ('Constant', (1, 0, 1, 1), 1, None))], []),
-('Module', [('Pass', (1, 0, 1, 4))], []),
-('Module', [('For', (1, 0, 1, 16), ('Name', (1, 4, 1, 5), 'v', ('Store',)), ('Name', (1, 9, 1, 10), 'v', ('Load',)), [('Break', (1, 11, 1, 16))], [], None)], []),
-('Module', [('For', (1, 0, 1, 19), ('Name', (1, 4, 1, 5), 'v', ('Store',)), ('Name', (1, 9, 1, 10), 'v', ('Load',)), [('Continue', (1, 11, 1, 19))], [], None)], []),
-('Module', [('For', (1, 0, 1, 18), ('Tuple', (1, 4, 1, 7), [('Name', (1, 4, 1, 5), 'a', ('Store',)), ('Name', (1, 6, 1, 7), 'b', ('Store',))], ('Store',)), ('Name', (1, 11, 1, 12), 'c', ('Load',)), [('Pass', (1, 14, 1, 18))], [], None)], []),
-('Module', [('For', (1, 0, 1, 20), ('Tuple', (1, 4, 1, 9), [('Name', (1, 5, 1, 6), 'a', ('Store',)), ('Name', (1, 7, 1, 8), 'b', ('Store',))], ('Store',)), ('Name', (1, 13, 1, 14), 'c', ('Load',)), [('Pass', (1, 16, 1, 20))], [], None)], []),
-('Module', [('For', (1, 0, 1, 20), ('List', (1, 4, 1, 9), [('Name', (1, 5, 1, 6), 'a', ('Store',)), ('Name', (1, 7, 1, 8), 'b', ('Store',))], ('Store',)), ('Name', (1, 13, 1, 14), 'c', ('Load',)), [('Pass', (1, 16, 1, 20))], [], None)], []),
-('Module', [('Expr', (1, 0, 11, 5), ('GeneratorExp', (1, 0, 11, 5), ('Tuple', (2, 4, 6, 5), [('Name', (3, 4, 3, 6), 'Aa', ('Load',)), ('Name', (5, 7, 5, 9), 'Bb', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (8, 4, 10, 6), [('Name', (8, 4, 8, 6), 'Aa', ('Store',)), ('Name', (10, 4, 10, 6), 'Bb', ('Store',))], ('Store',)), ('Name', (10, 10, 10, 12), 'Cc', ('Load',)), [], 0)]))], []),
-('Module', [('Expr', (1, 0, 1, 34), ('DictComp', (1, 0, 1, 34), ('Name', (1, 1, 1, 2), 'a', ('Load',)), ('Name', (1, 5, 1, 6), 'b', ('Load',)), [('comprehension', ('Name', (1, 11, 1, 12), 'w', ('Store',)), ('Name', (1, 16, 1, 17), 'x', ('Load',)), [], 0), ('comprehension', ('Name', (1, 22, 1, 23), 'm', ('Store',)), ('Name', (1, 27, 1, 28), 'p', ('Load',)), [('Name', (1, 32, 1, 33), 'g', ('Load',))], 0)]))], []),
-('Module', [('Expr', (1, 0, 1, 20), ('DictComp', (1, 0, 1, 20), ('Name', (1, 1, 1, 2), 'a', ('Load',)), ('Name', (1, 5, 1, 6), 'b', ('Load',)), [('comprehension', ('Tuple', (1, 11, 1, 14), [('Name', (1, 11, 1, 12), 'v', ('Store',)), ('Name', (1, 13, 1, 14), 'w', ('Store',))], ('Store',)), ('Name', (1, 18, 1, 19), 'x', ('Load',)), [], 0)]))], []),
-('Module', [('Expr', (1, 0, 1, 19), ('SetComp', (1, 0, 1, 19), ('Name', (1, 1, 1, 2), 'r', ('Load',)), [('comprehension', ('Name', (1, 7, 1, 8), 'l', ('Store',)), ('Name', (1, 12, 1, 13), 'x', ('Load',)), [('Name', (1, 17, 1, 18), 'g', ('Load',))], 0)]))], []),
-('Module', [('Expr', (1, 0, 1, 16), ('SetComp', (1, 0, 1, 16), ('Name', (1, 1, 1, 2), 'r', ('Load',)), [('comprehension', ('Tuple', (1, 7, 1, 10), [('Name', (1, 7, 1, 8), 'l', ('Store',)), ('Name', (1, 9, 1, 10), 'm', ('Store',))], ('Store',)), ('Name', (1, 14, 1, 15), 'x', ('Load',)), [], 0)]))], []),
-('Module', [('AsyncFunctionDef', (1, 0, 3, 18), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (2, 1, 2, 17), ('Constant', (2, 1, 2, 17), 'async function', None)), ('Expr', (3, 1, 3, 18), ('Await', (3, 1, 3, 18), ('Call', (3, 7, 3, 18), ('Name', (3, 7, 3, 16), 'something', ('Load',)), [], [])))], [], None, None, [])], []),
-('Module', [('AsyncFunctionDef', (1, 0, 3, 8), 'f', ('arguments', [], [], None, [], [], None, []), [('AsyncFor', (2, 1, 3, 8), ('Name', (2, 11, 2, 12), 'e', ('Store',)), ('Name', (2, 16, 2, 17), 'i', ('Load',)), [('Expr', (2, 19, 2, 20), ('Constant', (2, 19, 2, 20), 1, None))], [('Expr', (3, 7, 3, 8), ('Constant', (3, 7, 3, 8), 2, None))], None)], [], None, None, [])], []),
-('Module', [('AsyncFunctionDef', (1, 0, 2, 21), 'f', ('arguments', [], [], None, [], [], None, []), [('AsyncWith', (2, 1, 2, 21), [('withitem', ('Name', (2, 12, 2, 13), 'a', ('Load',)), ('Name', (2, 17, 2, 18), 'b', ('Store',)))], [('Expr', (2, 20, 2, 21), ('Constant', (2, 20, 2, 21), 1, None))], None)], [], None, None, [])], []),
-('Module', [('Expr', (1, 0, 1, 14), ('Dict', (1, 0, 1, 14), [None, ('Constant', (1, 10, 1, 11), 2, None)], [('Dict', (1, 3, 1, 8), [('Constant', (1, 4, 1, 5), 1, None)], [('Constant', (1, 6, 1, 7), 2, None)]), ('Constant', (1, 12, 1, 13), 3, None)]))], []),
-('Module', [('Expr', (1, 0, 1, 12), ('Set', (1, 0, 1, 12), [('Starred', (1, 1, 1, 8), ('Set', (1, 2, 1, 8), [('Constant', (1, 3, 1, 4), 1, None), ('Constant', (1, 6, 1, 7), 2, None)]), ('Load',)), ('Constant', (1, 10, 1, 11), 3, None)]))], []),
-('Module', [('FunctionDef', (1, 0, 1, 16), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (1, 9, 1, 16), ('Yield', (1, 9, 1, 16), ('Constant', (1, 15, 1, 16), 1, None)))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 22), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (1, 9, 1, 22), ('YieldFrom', (1, 9, 1, 22), ('List', (1, 20, 1, 22), [], ('Load',))))], [], None, None, [])], []),
-('Module', [('AsyncFunctionDef', (1, 0, 2, 21), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (2, 1, 2, 21), ('ListComp', (2, 1, 2, 21), ('Name', (2, 2, 2, 3), 'i', ('Load',)), [('comprehension', ('Name', (2, 14, 2, 15), 'b', ('Store',)), ('Name', (2, 19, 2, 20), 'c', ('Load',)), [], 1)]))], [], None, None, [])], []),
-('Module', [('FunctionDef', (4, 0, 4, 13), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (4, 9, 4, 13))], [('Name', (1, 1, 1, 6), 'deco1', ('Load',)), ('Call', (2, 1, 2, 8), ('Name', (2, 1, 2, 6), 'deco2', ('Load',)), [], []), ('Call', (3, 1, 3, 9), ('Name', (3, 1, 3, 6), 'deco3', ('Load',)), [('Constant', (3, 7, 3, 8), 1, None)], [])], None, None, [])], []),
-('Module', [('AsyncFunctionDef', (4, 0, 4, 19), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (4, 15, 4, 19))], [('Name', (1, 1, 1, 6), 'deco1', ('Load',)), ('Call', (2, 1, 2, 8), ('Name', (2, 1, 2, 6), 'deco2', ('Load',)), [], []), ('Call', (3, 1, 3, 9), ('Name', (3, 1, 3, 6), 'deco3', ('Load',)), [('Constant', (3, 7, 3, 8), 1, None)], [])], None, None, [])], []),
-('Module', [('ClassDef', (4, 0, 4, 13), 'C', [], [], [('Pass', (4, 9, 4, 13))], [('Name', (1, 1, 1, 6), 'deco1', ('Load',)), ('Call', (2, 1, 2, 8), ('Name', (2, 1, 2, 6), 'deco2', ('Load',)), [], []), ('Call', (3, 1, 3, 9), ('Name', (3, 1, 3, 6), 'deco3', ('Load',)), [('Constant', (3, 7, 3, 8), 1, None)], [])], [])], []),
-('Module', [('FunctionDef', (2, 0, 2, 13), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (2, 9, 2, 13))], [('Call', (1, 1, 1, 19), ('Name', (1, 1, 1, 5), 'deco', ('Load',)), [('GeneratorExp', (1, 5, 1, 19), ('Name', (1, 6, 1, 7), 'a', ('Load',)), [('comprehension', ('Name', (1, 12, 1, 13), 'a', ('Store',)), ('Name', (1, 17, 1, 18), 'b', ('Load',)), [], 0)])], [])], None, None, [])], []),
-('Module', [('FunctionDef', (2, 0, 2, 13), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (2, 9, 2, 13))], [('Attribute', (1, 1, 1, 6), ('Attribute', (1, 1, 1, 4), ('Name', (1, 1, 1, 2), 'a', ('Load',)), 'b', ('Load',)), 'c', ('Load',))], None, None, [])], []),
-('Module', [('Expr', (1, 0, 1, 8), ('NamedExpr', (1, 1, 1, 7), ('Name', (1, 1, 1, 2), 'a', ('Store',)), ('Constant', (1, 6, 1, 7), 1, None)))], []),
-('Module', [('If', (1, 0, 1, 19), ('NamedExpr', (1, 3, 1, 13), ('Name', (1, 3, 1, 4), 'a', ('Store',)), ('Call', (1, 8, 1, 13), ('Name', (1, 8, 1, 11), 'foo', ('Load',)), [], [])), [('Pass', (1, 15, 1, 19))], [])], []),
-('Module', [('While', (1, 0, 1, 22), ('NamedExpr', (1, 6, 1, 16), ('Name', (1, 6, 1, 7), 'a', ('Store',)), ('Call', (1, 11, 1, 16), ('Name', (1, 11, 1, 14), 'foo', ('Load',)), [], [])), [('Pass', (1, 18, 1, 22))], [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 18), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [], None, [], [], None, []), [('Pass', (1, 14, 1, 18))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 26), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 12, 1, 13), 'c', None, None), ('arg', (1, 15, 1, 16), 'd', None, None), ('arg', (1, 18, 1, 19), 'e', None, None)], None, [], [], None, []), [('Pass', (1, 22, 1, 26))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 29), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 12, 1, 13), 'c', None, None)], None, [('arg', (1, 18, 1, 19), 'd', None, None), ('arg', (1, 21, 1, 22), 'e', None, None)], [None, None], None, []), [('Pass', (1, 25, 1, 29))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 39), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 12, 1, 13), 'c', None, None)], None, [('arg', (1, 18, 1, 19), 'd', None, None), ('arg', (1, 21, 1, 22), 'e', None, None)], [None, None], ('arg', (1, 26, 1, 32), 'kwargs', None, None), []), [('Pass', (1, 35, 1, 39))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 20), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [], None, [], [], None, [('Constant', (1, 8, 1, 9), 1, None)]), [('Pass', (1, 16, 1, 20))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 29), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None), ('arg', (1, 19, 1, 20), 'c', None, None)], None, [], [], None, [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None), ('Constant', (1, 21, 1, 22), 4, None)]), [('Pass', (1, 25, 1, 29))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 32), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [('Constant', (1, 24, 1, 25), 4, None)], None, [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 28, 1, 32))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 30), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [None], None, [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 26, 1, 30))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 42), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [('Constant', (1, 24, 1, 25), 4, None)], ('arg', (1, 29, 1, 35), 'kwargs', None, None), [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 38, 1, 42))], [], None, None, [])], []),
-('Module', [('FunctionDef', (1, 0, 1, 40), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [None], ('arg', (1, 27, 1, 33), 'kwargs', None, None), [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 36, 1, 40))], [], None, None, [])], []),
-('Module', [('TypeAlias', (1, 0, 1, 12), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [], ('Name', (1, 9, 1, 12), 'int', ('Load',)))], []),
-('Module', [('TypeAlias', (1, 0, 1, 15), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 8), 'T', None, None)], ('Name', (1, 12, 1, 15), 'int', ('Load',)))], []),
-('Module', [('TypeAlias', (1, 0, 1, 32), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 8), 'T', None, None), ('TypeVarTuple', (1, 10, 1, 13), 'Ts', None), ('ParamSpec', (1, 15, 1, 18), 'P', None)], ('Tuple', (1, 22, 1, 32), [('Name', (1, 23, 1, 24), 'T', ('Load',)), ('Name', (1, 26, 1, 28), 'Ts', ('Load',)), ('Name', (1, 30, 1, 31), 'P', ('Load',))], ('Load',)))], []),
-('Module', [('TypeAlias', (1, 0, 1, 37), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 13), 'T', ('Name', (1, 10, 1, 13), 'int', ('Load',)), None), ('TypeVarTuple', (1, 15, 1, 18), 'Ts', None), ('ParamSpec', (1, 20, 1, 23), 'P', None)], ('Tuple', (1, 27, 1, 37), [('Name', (1, 28, 1, 29), 'T', ('Load',)), ('Name', (1, 31, 1, 33), 'Ts', ('Load',)), ('Name', (1, 35, 1, 36), 'P', ('Load',))], ('Load',)))], []),
-('Module', [('TypeAlias', (1, 0, 1, 44), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 20), 'T', ('Tuple', (1, 10, 1, 20), [('Name', (1, 11, 1, 14), 'int', ('Load',)), ('Name', (1, 16, 1, 19), 'str', ('Load',))], ('Load',)), None), ('TypeVarTuple', (1, 22, 1, 25), 'Ts', None), ('ParamSpec', (1, 27, 1, 30), 'P', None)], ('Tuple', (1, 34, 1, 44), [('Name', (1, 35, 1, 36), 'T', ('Load',)), ('Name', (1, 38, 1, 40), 'Ts', ('Load',)), ('Name', (1, 42, 1, 43), 'P', ('Load',))], ('Load',)))], []),
-('Module', [('TypeAlias', (1, 0, 1, 48), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 17), 'T', ('Name', (1, 10, 1, 13), 'int', ('Load',)), ('Constant', (1, 16, 1, 17), 1, None)), ('TypeVarTuple', (1, 19, 1, 26), 'Ts', ('Constant', (1, 25, 1, 26), 2, None)), ('ParamSpec', (1, 28, 1, 34), 'P', ('Constant', (1, 33, 1, 34), 3, None))], ('Tuple', (1, 38, 1, 48), [('Name', (1, 39, 1, 40), 'T', ('Load',)), ('Name', (1, 42, 1, 44), 'Ts', ('Load',)), ('Name', (1, 46, 1, 47), 'P', ('Load',))], ('Load',)))], []),
-('Module', [('ClassDef', (1, 0, 1, 16), 'X', [], [], [('Pass', (1, 12, 1, 16))], [], [('TypeVar', (1, 8, 1, 9), 'T', None, None)])], []),
-('Module', [('ClassDef', (1, 0, 1, 26), 'X', [], [], [('Pass', (1, 22, 1, 26))], [], [('TypeVar', (1, 8, 1, 9), 'T', None, None), ('TypeVarTuple', (1, 11, 1, 14), 'Ts', None), ('ParamSpec', (1, 16, 1, 19), 'P', None)])], []),
-('Module', [('ClassDef', (1, 0, 1, 31), 'X', [], [], [('Pass', (1, 27, 1, 31))], [], [('TypeVar', (1, 8, 1, 14), 'T', ('Name', (1, 11, 1, 14), 'int', ('Load',)), None), ('TypeVarTuple', (1, 16, 1, 19), 'Ts', None), ('ParamSpec', (1, 21, 1, 24), 'P', None)])], []),
-('Module', [('ClassDef', (1, 0, 1, 38), 'X', [], [], [('Pass', (1, 34, 1, 38))], [], [('TypeVar', (1, 8, 1, 21), 'T', ('Tuple', (1, 11, 1, 21), [('Name', (1, 12, 1, 15), 'int', ('Load',)), ('Name', (1, 17, 1, 20), 'str', ('Load',))], ('Load',)), None), ('TypeVarTuple', (1, 23, 1, 26), 'Ts', None), ('ParamSpec', (1, 28, 1, 31), 'P', None)])], []),
-('Module', [('ClassDef', (1, 0, 1, 43), 'X', [], [], [('Pass', (1, 39, 1, 43))], [], [('TypeVar', (1, 8, 1, 18), 'T', ('Name', (1, 11, 1, 14), 'int', ('Load',)), ('Constant', (1, 17, 1, 18), 1, None)), ('TypeVarTuple', (1, 20, 1, 27), 'Ts', ('Constant', (1, 26, 1, 27), 2, None)), ('ParamSpec', (1, 29, 1, 36), 'P', ('Constant', (1, 35, 1, 36), 3, None))])], []),
-('Module', [('FunctionDef', (1, 0, 1, 16), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 12, 1, 16))], [], None, None, [('TypeVar', (1, 6, 1, 7), 'T', None, None)])], []),
-('Module', [('FunctionDef', (1, 0, 1, 26), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 22, 1, 26))], [], None, None, [('TypeVar', (1, 6, 1, 7), 'T', None, None), ('TypeVarTuple', (1, 9, 1, 12), 'Ts', None), ('ParamSpec', (1, 14, 1, 17), 'P', None)])], []),
-('Module', [('FunctionDef', (1, 0, 1, 31), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 27, 1, 31))], [], None, None, [('TypeVar', (1, 6, 1, 12), 'T', ('Name', (1, 9, 1, 12), 'int', ('Load',)), None), ('TypeVarTuple', (1, 14, 1, 17), 'Ts', None), ('ParamSpec', (1, 19, 1, 22), 'P', None)])], []),
-('Module', [('FunctionDef', (1, 0, 1, 38), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 34, 1, 38))], [], None, None, [('TypeVar', (1, 6, 1, 19), 'T', ('Tuple', (1, 9, 1, 19), [('Name', (1, 10, 1, 13), 'int', ('Load',)), ('Name', (1, 15, 1, 18), 'str', ('Load',))], ('Load',)), None), ('TypeVarTuple', (1, 21, 1, 24), 'Ts', None), ('ParamSpec', (1, 26, 1, 29), 'P', None)])], []),
-('Module', [('FunctionDef', (1, 0, 1, 43), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 39, 1, 43))], [], None, None, [('TypeVar', (1, 6, 1, 16), 'T', ('Name', (1, 9, 1, 12), 'int', ('Load',)), ('Constant', (1, 15, 1, 16), 1, None)), ('TypeVarTuple', (1, 18, 1, 25), 'Ts', ('Constant', (1, 24, 1, 25), 2, None)), ('ParamSpec', (1, 27, 1, 34), 'P', ('Constant', (1, 33, 1, 34), 3, None))])], []),
-('Module', [('Match', (1, 0, 3, 6), ('Name', (1, 6, 1, 7), 'x', ('Load',)), [('match_case', ('MatchValue', (2, 6, 2, 7), ('Constant', (2, 6, 2, 7), 1, None)), None, [('Pass', (3, 2, 3, 6))])])], []),
-('Module', [('Match', (1, 0, 5, 6), ('Name', (1, 6, 1, 7), 'x', ('Load',)), [('match_case', ('MatchValue', (2, 6, 2, 7), ('Constant', (2, 6, 2, 7), 1, None)), None, [('Pass', (3, 2, 3, 6))]), ('match_case', ('MatchAs', (4, 6, 4, 7), None, None), None, [('Pass', (5, 2, 5, 6))])])], []),
-]
-single_results = [
-('Interactive', [('Expr', (1, 0, 1, 3), ('BinOp', (1, 0, 1, 3), ('Constant', (1, 0, 1, 1), 1, None), ('Add',), ('Constant', (1, 2, 1, 3), 2, None)))]),
-]
-eval_results = [
-('Expression', ('Constant', (1, 0, 1, 4), None, None)),
-('Expression', ('Constant', (1, 0, 1, 4), True, None)),
-('Expression', ('Constant', (1, 0, 1, 5), False, None)),
-('Expression', ('BoolOp', (1, 0, 1, 7), ('And',), [('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Name', (1, 6, 1, 7), 'b', ('Load',))])),
-('Expression', ('BoolOp', (1, 0, 1, 6), ('Or',), [('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Name', (1, 5, 1, 6), 'b', ('Load',))])),
-('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Add',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
-('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Sub',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
-('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Mult',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
-('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Div',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
-('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('MatMult',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
-('Expression', ('BinOp', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('FloorDiv',), ('Name', (1, 5, 1, 6), 'b', ('Load',)))),
-('Expression', ('BinOp', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Pow',), ('Name', (1, 5, 1, 6), 'b', ('Load',)))),
-('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Mod',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
-('Expression', ('BinOp', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('RShift',), ('Name', (1, 5, 1, 6), 'b', ('Load',)))),
-('Expression', ('BinOp', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('LShift',), ('Name', (1, 5, 1, 6), 'b', ('Load',)))),
-('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('BitXor',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
-('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('BitOr',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
-('Expression', ('BinOp', (1, 0, 1, 5), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('BitAnd',), ('Name', (1, 4, 1, 5), 'b', ('Load',)))),
-('Expression', ('UnaryOp', (1, 0, 1, 5), ('Not',), ('Name', (1, 4, 1, 5), 'v', ('Load',)))),
-('Expression', ('UnaryOp', (1, 0, 1, 2), ('UAdd',), ('Name', (1, 1, 1, 2), 'v', ('Load',)))),
-('Expression', ('UnaryOp', (1, 0, 1, 2), ('USub',), ('Name', (1, 1, 1, 2), 'v', ('Load',)))),
-('Expression', ('UnaryOp', (1, 0, 1, 2), ('Invert',), ('Name', (1, 1, 1, 2), 'v', ('Load',)))),
-('Expression', ('Lambda', (1, 0, 1, 11), ('arguments', [], [], None, [], [], None, []), ('Constant', (1, 7, 1, 11), None, None))),
-('Expression', ('Dict', (1, 0, 1, 7), [('Constant', (1, 2, 1, 3), 1, None)], [('Constant', (1, 4, 1, 5), 2, None)])),
-('Expression', ('Dict', (1, 0, 1, 2), [], [])),
-('Expression', ('Set', (1, 0, 1, 7), [('Constant', (1, 1, 1, 5), None, None)])),
-('Expression', ('Dict', (1, 0, 5, 6), [('Constant', (2, 6, 2, 7), 1, None)], [('Constant', (4, 10, 4, 11), 2, None)])),
-('Expression', ('List', (1, 0, 5, 6), [('Constant', (2, 6, 2, 7), 1, None), ('Constant', (4, 8, 4, 9), 1, None)], ('Load',))),
-('Expression', ('Tuple', (1, 0, 4, 6), [('Constant', (2, 6, 2, 7), 1, None)], ('Load',))),
-('Expression', ('Set', (1, 0, 5, 6), [('Constant', (2, 6, 2, 7), 1, None), ('Constant', (4, 8, 4, 9), 1, None)])),
-('Expression', ('ListComp', (1, 0, 1, 19), ('Name', (1, 1, 1, 2), 'a', ('Load',)), [('comprehension', ('Name', (1, 7, 1, 8), 'b', ('Store',)), ('Name', (1, 12, 1, 13), 'c', ('Load',)), [('Name', (1, 17, 1, 18), 'd', ('Load',))], 0)])),
-('Expression', ('GeneratorExp', (1, 0, 1, 19), ('Name', (1, 1, 1, 2), 'a', ('Load',)), [('comprehension', ('Name', (1, 7, 1, 8), 'b', ('Store',)), ('Name', (1, 12, 1, 13), 'c', ('Load',)), [('Name', (1, 17, 1, 18), 'd', ('Load',))], 0)])),
-('Expression', ('SetComp', (1, 0, 1, 19), ('Name', (1, 1, 1, 2), 'a', ('Load',)), [('comprehension', ('Name', (1, 7, 1, 8), 'b', ('Store',)), ('Name', (1, 12, 1, 13), 'c', ('Load',)), [('Name', (1, 17, 1, 18), 'd', ('Load',))], 0)])),
-('Expression', ('DictComp', (1, 0, 1, 25), ('Name', (1, 1, 1, 2), 'k', ('Load',)), ('Name', (1, 4, 1, 5), 'v', ('Load',)), [('comprehension', ('Tuple', (1, 10, 1, 14), [('Name', (1, 10, 1, 11), 'k', ('Store',)), ('Name', (1, 13, 1, 14), 'v', ('Store',))], ('Store',)), ('Name', (1, 18, 1, 19), 'c', ('Load',)), [('Name', (1, 23, 1, 24), 'd', ('Load',))], 0)])),
-('Expression', ('ListComp', (1, 0, 1, 20), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 11, 1, 14), [('Name', (1, 11, 1, 12), 'a', ('Store',)), ('Name', (1, 13, 1, 14), 'b', ('Store',))], ('Store',)), ('Name', (1, 18, 1, 19), 'c', ('Load',)), [], 0)])),
-('Expression', ('ListComp', (1, 0, 1, 22), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 11, 1, 16), [('Name', (1, 12, 1, 13), 'a', ('Store',)), ('Name', (1, 14, 1, 15), 'b', ('Store',))], ('Store',)), ('Name', (1, 20, 1, 21), 'c', ('Load',)), [], 0)])),
-('Expression', ('ListComp', (1, 0, 1, 22), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('List', (1, 11, 1, 16), [('Name', (1, 12, 1, 13), 'a', ('Store',)), ('Name', (1, 14, 1, 15), 'b', ('Store',))], ('Store',)), ('Name', (1, 20, 1, 21), 'c', ('Load',)), [], 0)])),
-('Expression', ('SetComp', (1, 0, 1, 20), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 11, 1, 14), [('Name', (1, 11, 1, 12), 'a', ('Store',)), ('Name', (1, 13, 1, 14), 'b', ('Store',))], ('Store',)), ('Name', (1, 18, 1, 19), 'c', ('Load',)), [], 0)])),
-('Expression', ('SetComp', (1, 0, 1, 22), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 11, 1, 16), [('Name', (1, 12, 1, 13), 'a', ('Store',)), ('Name', (1, 14, 1, 15), 'b', ('Store',))], ('Store',)), ('Name', (1, 20, 1, 21), 'c', ('Load',)), [], 0)])),
-('Expression', ('SetComp', (1, 0, 1, 22), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('List', (1, 11, 1, 16), [('Name', (1, 12, 1, 13), 'a', ('Store',)), ('Name', (1, 14, 1, 15), 'b', ('Store',))], ('Store',)), ('Name', (1, 20, 1, 21), 'c', ('Load',)), [], 0)])),
-('Expression', ('GeneratorExp', (1, 0, 1, 20), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 11, 1, 14), [('Name', (1, 11, 1, 12), 'a', ('Store',)), ('Name', (1, 13, 1, 14), 'b', ('Store',))], ('Store',)), ('Name', (1, 18, 1, 19), 'c', ('Load',)), [], 0)])),
-('Expression', ('GeneratorExp', (1, 0, 1, 22), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 11, 1, 16), [('Name', (1, 12, 1, 13), 'a', ('Store',)), ('Name', (1, 14, 1, 15), 'b', ('Store',))], ('Store',)), ('Name', (1, 20, 1, 21), 'c', ('Load',)), [], 0)])),
-('Expression', ('GeneratorExp', (1, 0, 1, 22), ('Tuple', (1, 1, 1, 6), [('Name', (1, 2, 1, 3), 'a', ('Load',)), ('Name', (1, 4, 1, 5), 'b', ('Load',))], ('Load',)), [('comprehension', ('List', (1, 11, 1, 16), [('Name', (1, 12, 1, 13), 'a', ('Store',)), ('Name', (1, 14, 1, 15), 'b', ('Store',))], ('Store',)), ('Name', (1, 20, 1, 21), 'c', ('Load',)), [], 0)])),
-('Expression', ('Compare', (1, 0, 1, 9), ('Constant', (1, 0, 1, 1), 1, None), [('Lt',), ('Lt',)], [('Constant', (1, 4, 1, 5), 2, None), ('Constant', (1, 8, 1, 9), 3, None)])),
-('Expression', ('Compare', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), [('Eq',)], [('Name', (1, 5, 1, 6), 'b', ('Load',))])),
-('Expression', ('Compare', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), [('LtE',)], [('Name', (1, 5, 1, 6), 'b', ('Load',))])),
-('Expression', ('Compare', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), [('GtE',)], [('Name', (1, 5, 1, 6), 'b', ('Load',))])),
-('Expression', ('Compare', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), [('NotEq',)], [('Name', (1, 5, 1, 6), 'b', ('Load',))])),
-('Expression', ('Compare', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), [('Is',)], [('Name', (1, 5, 1, 6), 'b', ('Load',))])),
-('Expression', ('Compare', (1, 0, 1, 10), ('Name', (1, 0, 1, 1), 'a', ('Load',)), [('IsNot',)], [('Name', (1, 9, 1, 10), 'b', ('Load',))])),
-('Expression', ('Compare', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), [('In',)], [('Name', (1, 5, 1, 6), 'b', ('Load',))])),
-('Expression', ('Compare', (1, 0, 1, 10), ('Name', (1, 0, 1, 1), 'a', ('Load',)), [('NotIn',)], [('Name', (1, 9, 1, 10), 'b', ('Load',))])),
-('Expression', ('Call', (1, 0, 1, 3), ('Name', (1, 0, 1, 1), 'f', ('Load',)), [], [])),
-('Expression', ('Call', (1, 0, 1, 17), ('Name', (1, 0, 1, 1), 'f', ('Load',)), [('Constant', (1, 2, 1, 3), 1, None), ('Constant', (1, 4, 1, 5), 2, None), ('Starred', (1, 10, 1, 12), ('Name', (1, 11, 1, 12), 'd', ('Load',)), ('Load',))], [('keyword', (1, 6, 1, 9), 'c', ('Constant', (1, 8, 1, 9), 3, None)), ('keyword', (1, 13, 1, 16), None, ('Name', (1, 15, 1, 16), 'e', ('Load',)))])),
-('Expression', ('Call', (1, 0, 1, 10), ('Name', (1, 0, 1, 1), 'f', ('Load',)), [('Starred', (1, 2, 1, 9), ('List', (1, 3, 1, 9), [('Constant', (1, 4, 1, 5), 0, None), ('Constant', (1, 7, 1, 8), 1, None)], ('Load',)), ('Load',))], [])),
-('Expression', ('Call', (1, 0, 1, 15), ('Name', (1, 0, 1, 1), 'f', ('Load',)), [('GeneratorExp', (1, 1, 1, 15), ('Name', (1, 2, 1, 3), 'a', ('Load',)), [('comprehension', ('Name', (1, 8, 1, 9), 'a', ('Store',)), ('Name', (1, 13, 1, 14), 'b', ('Load',)), [], 0)])], [])),
-('Expression', ('Constant', (1, 0, 1, 2), 10, None)),
-('Expression', ('Constant', (1, 0, 1, 2), 1j, None)),
-('Expression', ('Constant', (1, 0, 1, 8), 'string', None)),
-('Expression', ('Attribute', (1, 0, 1, 3), ('Name', (1, 0, 1, 1), 'a', ('Load',)), 'b', ('Load',))),
-('Expression', ('Subscript', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Slice', (1, 2, 1, 5), ('Name', (1, 2, 1, 3), 'b', ('Load',)), ('Name', (1, 4, 1, 5), 'c', ('Load',)), None), ('Load',))),
-('Expression', ('Name', (1, 0, 1, 1), 'v', ('Load',))),
-('Expression', ('List', (1, 0, 1, 7), [('Constant', (1, 1, 1, 2), 1, None), ('Constant', (1, 3, 1, 4), 2, None), ('Constant', (1, 5, 1, 6), 3, None)], ('Load',))),
-('Expression', ('List', (1, 0, 1, 2), [], ('Load',))),
-('Expression', ('Tuple', (1, 0, 1, 5), [('Constant', (1, 0, 1, 1), 1, None), ('Constant', (1, 2, 1, 3), 2, None), ('Constant', (1, 4, 1, 5), 3, None)], ('Load',))),
-('Expression', ('Tuple', (1, 0, 1, 7), [('Constant', (1, 1, 1, 2), 1, None), ('Constant', (1, 3, 1, 4), 2, None), ('Constant', (1, 5, 1, 6), 3, None)], ('Load',))),
-('Expression', ('Tuple', (1, 0, 1, 2), [], ('Load',))),
-('Expression', ('Call', (1, 0, 1, 17), ('Attribute', (1, 0, 1, 7), ('Attribute', (1, 0, 1, 5), ('Attribute', (1, 0, 1, 3), ('Name', (1, 0, 1, 1), 'a', ('Load',)), 'b', ('Load',)), 'c', ('Load',)), 'd', ('Load',)), [('Subscript', (1, 8, 1, 16), ('Attribute', (1, 8, 1, 11), ('Name', (1, 8, 1, 9), 'a', ('Load',)), 'b', ('Load',)), ('Slice', (1, 12, 1, 15), ('Constant', (1, 12, 1, 13), 1, None), ('Constant', (1, 14, 1, 15), 2, None), None), ('Load',))], [])),
-('Expression', ('Subscript', (1, 0, 1, 7), ('List', (1, 0, 1, 3), [('Constant', (1, 1, 1, 2), 5, None)], ('Load',)), ('Slice', (1, 4, 1, 6), ('Constant', (1, 4, 1, 5), 1, None), None, None), ('Load',))),
-('Expression', ('Subscript', (1, 0, 1, 7), ('List', (1, 0, 1, 3), [('Constant', (1, 1, 1, 2), 5, None)], ('Load',)), ('Slice', (1, 4, 1, 6), None, ('Constant', (1, 5, 1, 6), 1, None), None), ('Load',))),
-('Expression', ('Subscript', (1, 0, 1, 8), ('List', (1, 0, 1, 3), [('Constant', (1, 1, 1, 2), 5, None)], ('Load',)), ('Slice', (1, 4, 1, 7), None, None, ('Constant', (1, 6, 1, 7), 1, None)), ('Load',))),
-('Expression', ('Subscript', (1, 0, 1, 10), ('List', (1, 0, 1, 3), [('Constant', (1, 1, 1, 2), 5, None)], ('Load',)), ('Slice', (1, 4, 1, 9), ('Constant', (1, 4, 1, 5), 1, None), ('Constant', (1, 6, 1, 7), 1, None), ('Constant', (1, 8, 1, 9), 1, None)), ('Load',))),
-('Expression', ('IfExp', (1, 0, 1, 21), ('Name', (1, 9, 1, 10), 'x', ('Load',)), ('Call', (1, 0, 1, 5), ('Name', (1, 0, 1, 3), 'foo', ('Load',)), [], []), ('Call', (1, 16, 1, 21), ('Name', (1, 16, 1, 19), 'bar', ('Load',)), [], []))),
-('Expression', ('JoinedStr', (1, 0, 1, 6), [('FormattedValue', (1, 2, 1, 5), ('Name', (1, 3, 1, 4), 'a', ('Load',)), -1, None)])),
-('Expression', ('JoinedStr', (1, 0, 1, 10), [('FormattedValue', (1, 2, 1, 9), ('Name', (1, 3, 1, 4), 'a', ('Load',)), -1, ('JoinedStr', (1, 4, 1, 8), [('Constant', (1, 5, 1, 8), '.2f', None)]))])),
-('Expression', ('JoinedStr', (1, 0, 1, 8), [('FormattedValue', (1, 2, 1, 7), ('Name', (1, 3, 1, 4), 'a', ('Load',)), 114, None)])),
-('Expression', ('JoinedStr', (1, 0, 1, 11), [('Constant', (1, 2, 1, 6), 'foo(', None), ('FormattedValue', (1, 6, 1, 9), ('Name', (1, 7, 1, 8), 'a', ('Load',)), -1, None), ('Constant', (1, 9, 1, 10), ')', None)])),
-]
-main()
diff --git a/Lib/test/test_ast/utils.py b/Lib/test/test_ast/utils.py
new file mode 100644
index 00000000000000..145e89ee94e935
--- /dev/null
+++ b/Lib/test/test_ast/utils.py
@@ -0,0 +1,15 @@
+def to_tuple(t):
+ if t is None or isinstance(t, (str, int, complex, float, bytes)) or t is Ellipsis:
+ return t
+ elif isinstance(t, list):
+ return [to_tuple(e) for e in t]
+ result = [t.__class__.__name__]
+ if hasattr(t, 'lineno') and hasattr(t, 'col_offset'):
+ result.append((t.lineno, t.col_offset))
+ if hasattr(t, 'end_lineno') and hasattr(t, 'end_col_offset'):
+ result[-1] += (t.end_lineno, t.end_col_offset)
+ if t._fields is None:
+ return tuple(result)
+ for f in t._fields:
+ result.append(to_tuple(getattr(t, f)))
+ return tuple(result)
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 9ea7bc49be316c..5608e593ac9aca 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -2366,6 +2366,7 @@ LIBSUBDIRS= asyncio \
__phello__
TESTSUBDIRS= idlelib/idle_test \
test \
+ test/test_ast \
test/archivetestdata \
test/audiodata \
test/certdata \