From 079dd5ee39815e330b157369d74830403a11e343 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 14 Aug 2026 14:00:45 +0200 Subject: [PATCH 1/7] gh-155742: Get PyBytes_AS_STRING() as `const char*` (#155751) Use "const char*" type instead of "char*" to store PyBytes_AS_STRING(): treat bytes objects as immutable. --- Include/internal/pycore_code.h | 2 +- Modules/_sqlite/blob.c | 2 +- Modules/socketmodule.c | 2 +- Objects/stringlib/codecs.h | 2 +- Objects/unicodeobject.c | 2 +- Parser/action_helpers.c | 4 ++-- Parser/string_parser.c | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 32242f89b812e69..293c1ea4414e23e 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -205,7 +205,7 @@ _PyLocals_GetKind(PyObject *kinds, int i) { assert(PyBytes_Check(kinds)); assert(0 <= i && i < PyBytes_GET_SIZE(kinds)); - char *ptr = PyBytes_AS_STRING(kinds); + const char *ptr = PyBytes_AS_STRING(kinds); return (_PyLocals_Kind)(ptr[i]); } diff --git a/Modules/_sqlite/blob.c b/Modules/_sqlite/blob.c index a9a98b40bf60b8f..43cee9e0f308df7 100644 --- a/Modules/_sqlite/blob.c +++ b/Modules/_sqlite/blob.c @@ -472,7 +472,7 @@ subscript_slice(pysqlite_Blob *self, PyObject *item) return NULL; } char *res_buf = PyBytesWriter_GetData(writer); - char *blob_buf = PyBytes_AS_STRING(blob); + const char *blob_buf = PyBytes_AS_STRING(blob); size_t cur; Py_ssize_t i; diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index a01569017937620..9051420179aea11 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1811,7 +1811,7 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto) (in particular, numeric IP addresses). */ struct maybe_idna { PyObject *obj; - char *buf; + const char *buf; }; static void diff --git a/Objects/stringlib/codecs.h b/Objects/stringlib/codecs.h index 9e53fab842909a0..6ea7ef1c9a92bf4 100644 --- a/Objects/stringlib/codecs.h +++ b/Objects/stringlib/codecs.h @@ -406,7 +406,7 @@ STRINGLIB(utf8_encoder)(PyObject *unicode, writer->overallocate = (newpos < size); } - char *rep_str; + const char *rep_str; Py_ssize_t rep_len; if (PyBytes_Check(rep)) { rep_str = PyBytes_AS_STRING(rep); diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 45d61c8b8b765a6..2249280d0af8110 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -7314,7 +7314,7 @@ unicode_encode_ucs1(PyObject *unicode, writer->overallocate = (newpos < size); } - char *rep_str; + const char *rep_str; Py_ssize_t rep_len; if (PyBytes_Check(rep)) { /* Directly copy bytes result to output. */ diff --git a/Parser/action_helpers.c b/Parser/action_helpers.c index 809f3c0a7b270e8..8690dca8331b5ce 100644 --- a/Parser/action_helpers.c +++ b/Parser/action_helpers.c @@ -1481,7 +1481,7 @@ expr_ty _PyPegen_decoded_constant_from_token(Parser* p, Token* tok) { } expr_ty _PyPegen_constant_from_token(Parser* p, Token* tok) { - char* bstr = PyBytes_AsString(tok->bytes); + const char* bstr = PyBytes_AsString(tok->bytes); if (bstr == NULL) { return NULL; } @@ -1499,7 +1499,7 @@ expr_ty _PyPegen_constant_from_token(Parser* p, Token* tok) { } expr_ty _PyPegen_constant_from_string(Parser* p, Token* tok) { - char* the_str = PyBytes_AsString(tok->bytes); + const char* the_str = PyBytes_AsString(tok->bytes); if (the_str == NULL) { return NULL; } diff --git a/Parser/string_parser.c b/Parser/string_parser.c index b164dfbc81a9339..431d2703d88e2c4 100644 --- a/Parser/string_parser.c +++ b/Parser/string_parser.c @@ -70,7 +70,7 @@ warn_invalid_escape_sequence(Parser *p, const char* buffer, const char *first_in char first_quote = 0; if (lineno == t->lineno) { int quote_count = 0; - char* tok = PyBytes_AsString(t->bytes); + const char* tok = PyBytes_AsString(t->bytes); for (int i = 0; i < PyBytes_Size(t->bytes); i++) { if (tok[i] == '\'' || tok[i] == '\"') { if (quote_count == 0) { From b8e23da18a5ddd86f85f7654e3b4565c82623456 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 14 Aug 2026 14:33:01 +0200 Subject: [PATCH 2/7] gh-155742: Test PyBytesWriter_FinishWithSize() with negative size (#155783) Add test_finish_with_size() to test_capi.test_bytes. --- Lib/test/test_capi/test_bytes.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Lib/test/test_capi/test_bytes.py b/Lib/test/test_capi/test_bytes.py index c591e986bbce35f..d20e5016f969c2f 100644 --- a/Lib/test/test_capi/test_bytes.py +++ b/Lib/test/test_capi/test_bytes.py @@ -315,10 +315,16 @@ def test_create(self): self.assertEqual(writer.get_size(), 3) self.assertEqual(writer.finish(), self.result_type(b'abc')) + def test_finish_with_size(self): + # Test PyBytesWriter_FinishWithSize() writer = self.create_writer(10, b'abc') self.assertEqual(writer.get_size(), 10) self.assertEqual(writer.finish_with_size(3), self.result_type(b'abc')) + writer = self.create_writer(3, b'abc') + with self.assertRaises(SystemError): + writer.finish_with_size(-3) + def test_write_bytes(self): # Test PyBytesWriter_WriteBytes() writer = self.create_writer() From 4af81d9215a8f267c5caeecf98c789999c108f80 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 14 Aug 2026 14:41:31 +0200 Subject: [PATCH 3/7] improve test_copy_reduce and test_deepcopy_reduce coverage (#154182) * improve test_copy_reduce and test_deepcopy_reduce coverage * add assert __reduce_ex__ not called * change reduce params in test copy and deepcopy reduce_ex * change reduce_ex params in test copy and deepcopy reduce --------- Co-authored-by: Tomas R. --- Lib/test/test_copy.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py index 9455c9e00514ca4..13b7ce29565deed 100644 --- a/Lib/test/test_copy.py +++ b/Lib/test/test_copy.py @@ -60,7 +60,7 @@ class C(object): def __reduce_ex__(self, proto): c.append(1) return "" - def __reduce__(self): + def __reduce__(*args): self.fail("shouldn't call this") c = [] x = C() @@ -70,9 +70,15 @@ def __reduce__(self): def test_copy_reduce(self): class C(object): + def __reduce_ex__(*args): + self.fail("shouldn't call this") def __reduce__(self): c.append(1) return "" + def __getattribute__(self, name): + if name == "__reduce_ex__": + raise AttributeError(name) + return object.__getattribute__(self, name) c = [] x = C() y = copy.copy(x) @@ -329,7 +335,7 @@ class C(object): def __reduce_ex__(self, proto): c.append(1) return "" - def __reduce__(self): + def __reduce__(*args): self.fail("shouldn't call this") c = [] x = C() @@ -339,9 +345,15 @@ def __reduce__(self): def test_deepcopy_reduce(self): class C(object): + def __reduce_ex__(*args): + self.fail("shouldn't call this") def __reduce__(self): c.append(1) return "" + def __getattribute__(self, name): + if name == "__reduce_ex__": + raise AttributeError(name) + return object.__getattribute__(self, name) c = [] x = C() y = copy.deepcopy(x) From 6611f4df80bbceb4b2fdf9d132c7652eacbd7743 Mon Sep 17 00:00:00 2001 From: Victorien <65306057+Viicos@users.noreply.github.com> Date: Fri, 14 Aug 2026 14:58:16 +0200 Subject: [PATCH 4/7] Fix typo in `annotationlib` documentation (#155776) --- Doc/library/annotationlib.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/annotationlib.rst b/Doc/library/annotationlib.rst index a90754620b59fdf..5b22353ea08e647 100644 --- a/Doc/library/annotationlib.rst +++ b/Doc/library/annotationlib.rst @@ -235,8 +235,8 @@ Functions annotate functions that support the :attr:`~Format.STRING` format but do not have access to the code creating the annotations. - For example, this is used to implement the :attr:`~Format.STRING` for - :class:`typing.TypedDict` classes created through the functional syntax: + For example, this is used to implement the :attr:`~Format.STRING` format + for :class:`typing.TypedDict` classes created through the functional syntax: .. doctest:: From a7b2e1f4e07718e1e047dd7683b488e239688ceb Mon Sep 17 00:00:00 2001 From: Sreehari Annam Date: Fri, 14 Aug 2026 11:13:19 -0400 Subject: [PATCH 5/7] gh-135736: call exception handler for all BaseExceptions in asyncio.TaskGroup (#154538) --- Lib/asyncio/taskgroups.py | 12 +++++++ Lib/test/test_asyncio/test_taskgroups.py | 33 +++++++++++++++++++ ...-07-23-12-27-52.gh-issue-135736.hkuGvu.rst | 5 +++ 3 files changed, 50 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-07-23-12-27-52.gh-issue-135736.hkuGvu.rst diff --git a/Lib/asyncio/taskgroups.py b/Lib/asyncio/taskgroups.py index a431b45a19489d5..1b77d6d317e9375 100644 --- a/Lib/asyncio/taskgroups.py +++ b/Lib/asyncio/taskgroups.py @@ -140,6 +140,18 @@ async def _aexit(self, et, exc): assert not self._tasks if self._base_error is not None: + # self._base_error (SystemExit or KeyboardInterrupt) is about + # to propagate out of this method, which discards any other + # collected task errors silently. Report them instead of + # losing them. See gh-135736. + for suppressed_exc in self._errors: + self._loop.call_exception_handler({ + 'message': 'TaskGroup task exception was not ' + 'propagated because the TaskGroup body ' + 'is being closed with a BaseException', + 'exception': suppressed_exc, + 'task_group': self, + }) try: raise self._base_error finally: diff --git a/Lib/test/test_asyncio/test_taskgroups.py b/Lib/test/test_asyncio/test_taskgroups.py index bc246400b83e9b2..f5b7daf9e2ce1d0 100644 --- a/Lib/test/test_asyncio/test_taskgroups.py +++ b/Lib/test/test_asyncio/test_taskgroups.py @@ -608,6 +608,39 @@ async def runner(): get_error_types(cm.exception), {MyBaseExc, ZeroDivisionError} ) + async def test_taskgroup_20b(self): + # Same setup as test_taskgroup_20 (a KeyboardInterrupt from the + # "async with" body itself, alongside a sibling task's exception): + # raising self._base_error out of _aexit() discards self._errors + # silently. The sibling's exception can't be raised alongside + # the KeyboardInterrupt (only one exception can propagate), but + # it must be reported via the loop's exception handler instead + # of being discarded silently. See gh-135736. + async def crash_soon(): + await asyncio.sleep(0.1) + 1 / 0 + + async def nested(): + try: + await asyncio.sleep(10) + finally: + raise KeyboardInterrupt + + async def runner(): + async with taskgroups.TaskGroup() as g: + g.create_task(crash_soon()) + await nested() + + contexts = [] + loop = asyncio.get_running_loop() + loop.set_exception_handler(lambda loop, context: contexts.append(context)) + + with self.assertRaises(KeyboardInterrupt): + await runner() + + self.assertEqual(len(contexts), 1) + self.assertIsInstance(contexts[0]['exception'], ZeroDivisionError) + async def _test_taskgroup_21(self): # This test doesn't work as asyncio, currently, doesn't # correctly propagate KeyboardInterrupt (or SystemExit) -- diff --git a/Misc/NEWS.d/next/Library/2026-07-23-12-27-52.gh-issue-135736.hkuGvu.rst b/Misc/NEWS.d/next/Library/2026-07-23-12-27-52.gh-issue-135736.hkuGvu.rst new file mode 100644 index 000000000000000..76a8b7105f75850 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-23-12-27-52.gh-issue-135736.hkuGvu.rst @@ -0,0 +1,5 @@ +Fix :class:`asyncio.TaskGroup` silently discarding errors from sibling +tasks whenever the ``async with`` block exits with a :exc:`SystemExit` or +:exc:`KeyboardInterrupt`. These errors are now reported via +:meth:`loop.call_exception_handler() ` +instead of being lost. From e5556cdf71f7f5561fcaca21f6e680ebdd44cffb Mon Sep 17 00:00:00 2001 From: stevens Date: Fri, 14 Aug 2026 23:33:42 +0800 Subject: [PATCH 6/7] gh-155621: Fix unrepresentable defaults in socket.sendmsg() signature (#155623) --- Lib/test/test_inspect/test_inspect.py | 2 +- Modules/clinic/socketmodule.c.h | 5 ++--- Modules/socketmodule.c | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 76c346cd89eac04..ff7475447e95a03 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -6340,7 +6340,7 @@ def test_socket_module_has_signatures(self): # depends on the number of the arguments. methods_no_signature = {'ioctl', 'sendto', 'setsockopt'} # These have parameters with unrepresentable default values. - methods_unsupported_signature = {'listen', 'sendmsg', 'sendmsg_afalg'} + methods_unsupported_signature = {'listen', 'sendmsg_afalg'} defined = vars(socket.SocketType).keys() self._test_module_has_signatures(socket, no_signature, unsupported_signature, diff --git a/Modules/clinic/socketmodule.c.h b/Modules/clinic/socketmodule.c.h index 00e5fb4cde3aefd..f89b91b9b997532 100644 --- a/Modules/clinic/socketmodule.c.h +++ b/Modules/clinic/socketmodule.c.h @@ -1018,8 +1018,7 @@ _socket_socket_sendall(PyObject *s, PyObject *const *args, Py_ssize_t nargs) #if defined(CMSG_LEN) PyDoc_STRVAR(_socket_socket_sendmsg__doc__, -"sendmsg($self, buffers, ancdata=, flags=0,\n" -" address=, /)\n" +"sendmsg($self, buffers, ancdata=(), flags=0, address=None, /)\n" "--\n" "\n" "Send normal and ancillary data to the socket.\n" @@ -2479,4 +2478,4 @@ _socket_CMSG_SPACE(PyObject *module, PyObject *arg) #ifndef _SOCKET_CMSG_SPACE_METHODDEF #define _SOCKET_CMSG_SPACE_METHODDEF #endif /* !defined(_SOCKET_CMSG_SPACE_METHODDEF) */ -/*[clinic end generated code: output=133662a8c304b1b7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=acc30d6fdeb54e90 input=a9049054013a1b77]*/ diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 9051420179aea11..70d3738b176cda2 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -4990,9 +4990,9 @@ sock_sendmsg_impl(PySocketSockObject *s, void *data) _socket.socket.sendmsg self as s: self(type="PySocketSockObject *") buffers as data_arg: object - ancdata as cmsg_arg: object = NULL + ancdata as cmsg_arg: object(c_default="NULL") = () flags: int = 0 - address as addr_arg: object = NULL + address as addr_arg: object(c_default="NULL") = None / Send normal and ancillary data to the socket. @@ -5016,7 +5016,7 @@ static PyObject * _socket_socket_sendmsg_impl(PySocketSockObject *s, PyObject *data_arg, PyObject *cmsg_arg, int flags, PyObject *addr_arg) -/*[clinic end generated code: output=3b4cb1110644ce39 input=8ae408971a3aa329]*/ +/*[clinic end generated code: output=3b4cb1110644ce39 input=5fdf9b49bed3848b]*/ { Py_ssize_t i, ndatabufs = 0, ncmsgs, ncmsgbufs = 0; From 09b63170e339cae3c10d2a3fe0f115b5305f8e3f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 14 Aug 2026 18:09:07 +0200 Subject: [PATCH 7/7] gh-155742: Use PyBytesWriter in Python/assemble.c (#155747) Replace soft deprecated PyBytes_FromStringAndSize() with PyBytesWriter. --- Python/assemble.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/Python/assemble.c b/Python/assemble.c index 8bcb4c7bf3a9aa4..4bbebe30299906a 100644 --- a/Python/assemble.c +++ b/Python/assemble.c @@ -54,7 +54,8 @@ struct assembler { int a_except_table_off; /* offset into exception table */ /* Location Info */ int a_lineno; /* lineno of last emitted instruction */ - PyObject* a_linetable; /* bytes containing location info */ + PyBytesWriter *a_linetable_writer; /* writer containing location info */ + PyObject *a_linetable; /* bytes object containing location info */ int a_location_off; /* offset of last written location info frame */ }; @@ -63,15 +64,12 @@ assemble_init(struct assembler *a, int firstlineno) { memset(a, 0, sizeof(struct assembler)); a->a_lineno = firstlineno; - a->a_linetable = NULL; - a->a_location_off = 0; - a->a_except_table = NULL; a->a_bytecode = PyBytes_FromStringAndSize(NULL, DEFAULT_CODE_SIZE); if (a->a_bytecode == NULL) { goto error; } - a->a_linetable = PyBytes_FromStringAndSize(NULL, DEFAULT_CNOTAB_SIZE); - if (a->a_linetable == NULL) { + a->a_linetable_writer = PyBytesWriter_Create(DEFAULT_CNOTAB_SIZE); + if (a->a_linetable_writer == NULL) { goto error; } a->a_except_table = PyBytes_FromStringAndSize(NULL, DEFAULT_LNOTAB_SIZE); @@ -81,7 +79,7 @@ assemble_init(struct assembler *a, int firstlineno) return SUCCESS; error: Py_CLEAR(a->a_bytecode); - Py_CLEAR(a->a_linetable); + PyBytesWriter_Discard(a->a_linetable_writer); Py_CLEAR(a->a_except_table); return ERROR; } @@ -90,6 +88,7 @@ static void assemble_free(struct assembler *a) { Py_XDECREF(a->a_bytecode); + PyBytesWriter_Discard(a->a_linetable_writer); Py_XDECREF(a->a_linetable); Py_XDECREF(a->a_except_table); } @@ -195,7 +194,8 @@ assemble_exception_table(struct assembler *a, instr_sequence *instrs) static void write_location_byte(struct assembler* a, int val) { - PyBytes_AS_STRING(a->a_linetable)[a->a_location_off] = val&255; + uint8_t *linetable = PyBytesWriter_GetData(a->a_linetable_writer); + linetable[a->a_location_off] = val & 255; a->a_location_off++; } @@ -203,8 +203,8 @@ write_location_byte(struct assembler* a, int val) static uint8_t * location_pointer(struct assembler* a) { - return (uint8_t *)PyBytes_AS_STRING(a->a_linetable) + - a->a_location_off; + uint8_t *linetable = PyBytesWriter_GetData(a->a_linetable_writer); + return linetable + a->a_location_off; } static void @@ -285,10 +285,10 @@ write_location_info_no_column(struct assembler* a, int length, int line_delta) static int write_location_info_entry(struct assembler* a, location loc, int isize) { - Py_ssize_t len = PyBytes_GET_SIZE(a->a_linetable); + Py_ssize_t len = PyBytesWriter_GetSize(a->a_linetable_writer); if (a->a_location_off + THEORETICAL_MAX_ENTRY_SIZE >= len) { assert(len > THEORETICAL_MAX_ENTRY_SIZE); - RETURN_IF_ERROR(_PyBytes_Resize(&a->a_linetable, len*2)); + RETURN_IF_ERROR(PyBytesWriter_Resize(a->a_linetable_writer, len * 2)); } if (loc.lineno == NO_LOCATION.lineno) { write_location_info_none(a, isize); @@ -447,7 +447,12 @@ assemble_emit(struct assembler *a, instr_sequence *instrs, RETURN_IF_ERROR(_PyBytes_Resize(&a->a_except_table, a->a_except_table_off)); RETURN_IF_ERROR(_PyCompile_ConstCacheMergeOne(const_cache, &a->a_except_table)); - RETURN_IF_ERROR(_PyBytes_Resize(&a->a_linetable, a->a_location_off)); + a->a_linetable = PyBytesWriter_FinishWithSize(a->a_linetable_writer, + a->a_location_off); + a->a_linetable_writer = NULL; + if (a->a_linetable == NULL) { + return ERROR; + } RETURN_IF_ERROR(_PyCompile_ConstCacheMergeOne(const_cache, &a->a_linetable)); RETURN_IF_ERROR(_PyBytes_Resize(&a->a_bytecode, a->a_offset * sizeof(_Py_CODEUNIT)));