aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorshadchin <shadchin@yandex-team.com>2025-02-16 15:28:01 +0300
committershadchin <shadchin@yandex-team.com>2025-02-16 16:03:50 +0300
commita27b6a96fdc5ca444428ddef4823d0486dcdccb9 (patch)
treeadde5c24d9ea37e1634e7972e27e682a820ab941
parent7a3958c3c6de324baab9dba4bd4eb808c1b839a6 (diff)
downloadydb-a27b6a96fdc5ca444428ddef4823d0486dcdccb9.tar.gz
Update Python 3 to 3.12.9
commit_hash:c8651982d81e18f18e037fb247cc6ae53c4fa7f1
-rw-r--r--contrib/tools/python3/.yandex_meta/__init__.py1
-rw-r--r--contrib/tools/python3/.yandex_meta/override.nix4
-rw-r--r--contrib/tools/python3/Include/cpython/pytime.h2
-rw-r--r--contrib/tools/python3/Include/internal/pycore_object.h5
-rw-r--r--contrib/tools/python3/Include/internal/pycore_pyerrors.h12
-rw-r--r--contrib/tools/python3/Include/patchlevel.h4
-rw-r--r--contrib/tools/python3/Include/pyconfig-linux.h11
-rw-r--r--contrib/tools/python3/Include/pyconfig-osx-arm64.h11
-rw-r--r--contrib/tools/python3/Include/pymacro.h9
-rw-r--r--contrib/tools/python3/Include/tracemalloc.h11
-rw-r--r--contrib/tools/python3/Lib/_pydatetime.py1
-rw-r--r--contrib/tools/python3/Lib/_pydecimal.py4
-rw-r--r--contrib/tools/python3/Lib/_strptime.py2
-rw-r--r--contrib/tools/python3/Lib/ast.py11
-rw-r--r--contrib/tools/python3/Lib/asyncio/base_events.py11
-rw-r--r--contrib/tools/python3/Lib/asyncio/locks.py2
-rw-r--r--contrib/tools/python3/Lib/asyncio/runners.py1
-rw-r--r--contrib/tools/python3/Lib/asyncio/selector_events.py13
-rw-r--r--contrib/tools/python3/Lib/asyncio/staggered.py72
-rw-r--r--contrib/tools/python3/Lib/asyncio/taskgroups.py21
-rw-r--r--contrib/tools/python3/Lib/bdb.py64
-rw-r--r--contrib/tools/python3/Lib/dis.py4
-rw-r--r--contrib/tools/python3/Lib/email/_header_value_parser.py19
-rw-r--r--contrib/tools/python3/Lib/email/message.py8
-rw-r--r--contrib/tools/python3/Lib/functools.py3
-rw-r--r--contrib/tools/python3/Lib/http/client.py4
-rw-r--r--contrib/tools/python3/Lib/http/cookies.py6
-rw-r--r--contrib/tools/python3/Lib/imaplib.py11
-rw-r--r--contrib/tools/python3/Lib/importlib/resources/_common.py5
-rw-r--r--contrib/tools/python3/Lib/linecache.py13
-rw-r--r--contrib/tools/python3/Lib/multiprocessing/connection.py2
-rw-r--r--contrib/tools/python3/Lib/multiprocessing/resource_tracker.py7
-rw-r--r--contrib/tools/python3/Lib/multiprocessing/synchronize.py2
-rwxr-xr-xcontrib/tools/python3/Lib/pdb.py1
-rwxr-xr-xcontrib/tools/python3/Lib/platform.py3
-rw-r--r--contrib/tools/python3/Lib/poplib.py2
-rwxr-xr-xcontrib/tools/python3/Lib/pydoc.py39
-rw-r--r--contrib/tools/python3/Lib/pydoc_data/topics.py38
-rw-r--r--contrib/tools/python3/Lib/socket.py4
-rw-r--r--contrib/tools/python3/Lib/socketserver.py7
-rw-r--r--contrib/tools/python3/Lib/subprocess.py22
-rw-r--r--contrib/tools/python3/Lib/sysconfig.py3
-rw-r--r--contrib/tools/python3/Lib/threading.py2
-rw-r--r--contrib/tools/python3/Lib/tokenize.py14
-rw-r--r--contrib/tools/python3/Lib/typing.py6
-rw-r--r--contrib/tools/python3/Lib/urllib/parse.py20
-rw-r--r--contrib/tools/python3/Lib/urllib/request.py4
-rw-r--r--contrib/tools/python3/Lib/urllib/robotparser.py2
-rw-r--r--contrib/tools/python3/Lib/xml/dom/xmlbuilder.py12
-rw-r--r--contrib/tools/python3/Lib/ya.make4
-rw-r--r--contrib/tools/python3/Lib/zipfile/__init__.py9
-rw-r--r--contrib/tools/python3/Modules/_csv.c2
-rw-r--r--contrib/tools/python3/Modules/_ctypes/_ctypes.c92
-rw-r--r--contrib/tools/python3/Modules/_ctypes/callbacks.c60
-rw-r--r--contrib/tools/python3/Modules/_ctypes/callproc.c45
-rw-r--r--contrib/tools/python3/Modules/_hashopenssl.c1
-rw-r--r--contrib/tools/python3/Modules/_multiprocessing/semaphore.c7
-rw-r--r--contrib/tools/python3/Modules/_sqlite/connection.c5
-rw-r--r--contrib/tools/python3/Modules/_sqlite/util.c1
-rw-r--r--contrib/tools/python3/Modules/_sqlite/ya.make4
-rw-r--r--contrib/tools/python3/Modules/_ssl.c10
-rw-r--r--contrib/tools/python3/Modules/_tracemalloc.c5
-rw-r--r--contrib/tools/python3/Modules/_winapi.c2
-rw-r--r--contrib/tools/python3/Modules/arraymodule.c15
-rw-r--r--contrib/tools/python3/Modules/clinic/posixmodule.c.h6
-rw-r--r--contrib/tools/python3/Modules/main.c1
-rw-r--r--contrib/tools/python3/Modules/posixmodule.c40
-rw-r--r--contrib/tools/python3/Modules/pyexpat.c7
-rw-r--r--contrib/tools/python3/Modules/syslogmodule.c4
-rw-r--r--contrib/tools/python3/Objects/dictobject.c5
-rw-r--r--contrib/tools/python3/Objects/genobject.c33
-rw-r--r--contrib/tools/python3/Objects/iterobject.c2
-rw-r--r--contrib/tools/python3/Objects/namespaceobject.c4
-rw-r--r--contrib/tools/python3/Objects/typeobject.c15
-rw-r--r--contrib/tools/python3/Objects/unicodeobject.c7
-rw-r--r--contrib/tools/python3/Parser/action_helpers.c76
-rw-r--r--contrib/tools/python3/Parser/tokenizer.c4
-rw-r--r--contrib/tools/python3/Python/ceval.c34
-rw-r--r--contrib/tools/python3/Python/errors.c9
-rw-r--r--contrib/tools/python3/Python/generated_cases.c.h2
-rw-r--r--contrib/tools/python3/Python/pylifecycle.c9
-rw-r--r--contrib/tools/python3/Python/pythonrun.c62
-rw-r--r--contrib/tools/python3/Python/sysmodule.c7
-rw-r--r--contrib/tools/python3/Python/tracemalloc.c258
-rw-r--r--contrib/tools/python3/README.rst2
-rw-r--r--contrib/tools/python3/bin/ya.make4
-rw-r--r--contrib/tools/python3/patches/pr127876-fix-segfault.patch29
-rw-r--r--contrib/tools/python3/ya.make4
88 files changed, 876 insertions, 545 deletions
diff --git a/contrib/tools/python3/.yandex_meta/__init__.py b/contrib/tools/python3/.yandex_meta/__init__.py
index fb47a895f5..d2588d52a4 100644
--- a/contrib/tools/python3/.yandex_meta/__init__.py
+++ b/contrib/tools/python3/.yandex_meta/__init__.py
@@ -256,6 +256,7 @@ python3 = NixSourceProject(
"sys/byteorder.h",
"sys/lwp.h",
"sys/memfd.h",
+ "sys/pidfd.h",
# ifdef __VXWORKS__
"rtpLib.h",
"taskLib.h",
diff --git a/contrib/tools/python3/.yandex_meta/override.nix b/contrib/tools/python3/.yandex_meta/override.nix
index faea16f445..eec52c554b 100644
--- a/contrib/tools/python3/.yandex_meta/override.nix
+++ b/contrib/tools/python3/.yandex_meta/override.nix
@@ -1,11 +1,11 @@
pkgs: attrs: with pkgs; with attrs; rec {
- version = "3.12.8";
+ version = "3.12.9";
src = fetchFromGitHub {
owner = "python";
repo = "cpython";
rev = "v${version}";
- hash = "sha256-1Z2SMEut5YY9tTtrzPpmXcsIQKw5MGcGI4l0ysJbg28=";
+ hash = "sha256-5Hl+kKhavxihPmxVOyzUpchxiYMfYRfcjTbjiIq1i1o=";
};
patches = [];
diff --git a/contrib/tools/python3/Include/cpython/pytime.h b/contrib/tools/python3/Include/cpython/pytime.h
index 16d88d191e..46cc97bd7c 100644
--- a/contrib/tools/python3/Include/cpython/pytime.h
+++ b/contrib/tools/python3/Include/cpython/pytime.h
@@ -53,7 +53,7 @@ functions and constants
extern "C" {
#endif
-#ifdef __clang__
+#if defined(__clang__) || defined(_MSC_VER)
struct timeval;
#endif
diff --git a/contrib/tools/python3/Include/internal/pycore_object.h b/contrib/tools/python3/Include/internal/pycore_object.h
index 546f98d96d..de82d9e76f 100644
--- a/contrib/tools/python3/Include/internal/pycore_object.h
+++ b/contrib/tools/python3/Include/internal/pycore_object.h
@@ -65,6 +65,11 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n)
_Py_AddRefTotal(_PyInterpreterState_GET(), n);
#endif
op->ob_refcnt += n;
+
+ // Although the ref count was increased by `n` (which may be greater than 1)
+ // it is only a single increment (i.e. addition) operation, so only 1 refcnt
+ // increment operation is counted.
+ _Py_INCREF_STAT_INC();
}
#define _Py_RefcntAdd(op, n) _Py_RefcntAdd(_PyObject_CAST(op), n)
diff --git a/contrib/tools/python3/Include/internal/pycore_pyerrors.h b/contrib/tools/python3/Include/internal/pycore_pyerrors.h
index 4620a26964..dab41405b9 100644
--- a/contrib/tools/python3/Include/internal/pycore_pyerrors.h
+++ b/contrib/tools/python3/Include/internal/pycore_pyerrors.h
@@ -75,6 +75,18 @@ PyAPI_FUNC(void) _PyErr_SetString(
PyObject *exception,
const char *string);
+/*
+ * Set an exception with the error message decoded from the current locale
+ * encoding (LC_CTYPE).
+ *
+ * Exceptions occurring in decoding take priority over the desired exception.
+ *
+ * Exported for '_ctypes' shared extensions.
+ */
+PyAPI_FUNC(void) _PyErr_SetLocaleString(
+ PyObject *exception,
+ const char *string);
+
PyAPI_FUNC(PyObject *) _PyErr_Format(
PyThreadState *tstate,
PyObject *exception,
diff --git a/contrib/tools/python3/Include/patchlevel.h b/contrib/tools/python3/Include/patchlevel.h
index 6c597837da..3a80c5f879 100644
--- a/contrib/tools/python3/Include/patchlevel.h
+++ b/contrib/tools/python3/Include/patchlevel.h
@@ -18,12 +18,12 @@
/*--start constants--*/
#define PY_MAJOR_VERSION 3
#define PY_MINOR_VERSION 12
-#define PY_MICRO_VERSION 8
+#define PY_MICRO_VERSION 9
#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL
#define PY_RELEASE_SERIAL 0
/* Version as a string */
-#define PY_VERSION "3.12.8"
+#define PY_VERSION "3.12.9"
/*--end constants--*/
/* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2.
diff --git a/contrib/tools/python3/Include/pyconfig-linux.h b/contrib/tools/python3/Include/pyconfig-linux.h
index 2eec3b5a21..dd45eee1ad 100644
--- a/contrib/tools/python3/Include/pyconfig-linux.h
+++ b/contrib/tools/python3/Include/pyconfig-linux.h
@@ -48,10 +48,6 @@
/* Define if --enable-ipv6 is specified */
#define ENABLE_IPV6 1
-/* Define to 1 if your system stores words within floats with the most
- significant word first */
-/* #undef FLOAT_WORDS_BIGENDIAN */
-
/* Define if getpgrp() must be called as getpgrp(0). */
/* #undef GETPGRP_HAVE_ARG */
@@ -1339,6 +1335,9 @@
/* Define to 1 if you have the <sys/param.h> header file. */
#define HAVE_SYS_PARAM_H 1
+/* Define to 1 if you have the <sys/pidfd.h> header file. */
+/* #undef HAVE_SYS_PIDFD_H */
+
/* Define to 1 if you have the <sys/poll.h> header file. */
#define HAVE_SYS_POLL_H 1
@@ -1439,8 +1438,8 @@
/* Define to 1 if you have the `truncate' function. */
#define HAVE_TRUNCATE 1
-/* Define to 1 if you have the `ttyname' function. */
-#define HAVE_TTYNAME 1
+/* Define to 1 if you have the `ttyname_r' function. */
+#define HAVE_TTYNAME_R 1
/* Define to 1 if you don't have `tm_zone' but do have the external array
`tzname'. */
diff --git a/contrib/tools/python3/Include/pyconfig-osx-arm64.h b/contrib/tools/python3/Include/pyconfig-osx-arm64.h
index 81e4e3c15b..7439801d01 100644
--- a/contrib/tools/python3/Include/pyconfig-osx-arm64.h
+++ b/contrib/tools/python3/Include/pyconfig-osx-arm64.h
@@ -48,10 +48,6 @@
/* Define if --enable-ipv6 is specified */
#define ENABLE_IPV6 1
-/* Define to 1 if your system stores words within floats with the most
- significant word first */
-/* #undef FLOAT_WORDS_BIGENDIAN */
-
/* Define if getpgrp() must be called as getpgrp(0). */
/* #undef GETPGRP_HAVE_ARG */
@@ -1337,6 +1333,9 @@
/* Define to 1 if you have the <sys/param.h> header file. */
#define HAVE_SYS_PARAM_H 1
+/* Define to 1 if you have the <sys/pidfd.h> header file. */
+/* #undef HAVE_SYS_PIDFD_H */
+
/* Define to 1 if you have the <sys/poll.h> header file. */
#define HAVE_SYS_POLL_H 1
@@ -1437,8 +1436,8 @@
/* Define to 1 if you have the `truncate' function. */
#define HAVE_TRUNCATE 1
-/* Define to 1 if you have the `ttyname' function. */
-#define HAVE_TTYNAME 1
+/* Define to 1 if you have the `ttyname_r' function. */
+#define HAVE_TTYNAME_R 1
/* Define to 1 if you don't have `tm_zone' but do have the external array
`tzname'. */
diff --git a/contrib/tools/python3/Include/pymacro.h b/contrib/tools/python3/Include/pymacro.h
index d5700dc389..94e6248d28 100644
--- a/contrib/tools/python3/Include/pymacro.h
+++ b/contrib/tools/python3/Include/pymacro.h
@@ -118,6 +118,15 @@
*/
#if defined(__GNUC__) || defined(__clang__)
# define Py_UNUSED(name) _unused_ ## name __attribute__((unused))
+#elif defined(_MSC_VER)
+ // Disable warning C4100: unreferenced formal parameter,
+ // declare the parameter,
+ // restore old compiler warnings.
+# define Py_UNUSED(name) \
+ __pragma(warning(push)) \
+ __pragma(warning(suppress: 4100)) \
+ _unused_ ## name \
+ __pragma(warning(pop))
#else
# define Py_UNUSED(name) _unused_ ## name
#endif
diff --git a/contrib/tools/python3/Include/tracemalloc.h b/contrib/tools/python3/Include/tracemalloc.h
index 580027a8e3..2c88dbab7b 100644
--- a/contrib/tools/python3/Include/tracemalloc.h
+++ b/contrib/tools/python3/Include/tracemalloc.h
@@ -1,7 +1,10 @@
#ifndef Py_TRACEMALLOC_H
#define Py_TRACEMALLOC_H
-
#ifndef Py_LIMITED_API
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Track an allocated memory block in the tracemalloc module.
Return 0 on success, return -1 on error (failed to allocate memory to store
the trace).
@@ -47,7 +50,7 @@ PyAPI_FUNC(PyObject *) _PyTraceMalloc_GetTraces(void);
PyAPI_FUNC(PyObject *) _PyTraceMalloc_GetObjectTraceback(PyObject *obj);
/* Initialize tracemalloc */
-PyAPI_FUNC(int) _PyTraceMalloc_Init(void);
+PyAPI_FUNC(PyStatus) _PyTraceMalloc_Init(void);
/* Start tracemalloc */
PyAPI_FUNC(int) _PyTraceMalloc_Start(int max_nframe);
@@ -67,6 +70,8 @@ PyAPI_FUNC(PyObject *) _PyTraceMalloc_GetTracedMemory(void);
/* Set the peak size of traced memory blocks to the current size */
PyAPI_FUNC(void) _PyTraceMalloc_ResetPeak(void);
+#ifdef __cplusplus
+}
#endif
-
+#endif /* !Py_LIMITED_API */
#endif /* !Py_TRACEMALLOC_H */
diff --git a/contrib/tools/python3/Lib/_pydatetime.py b/contrib/tools/python3/Lib/_pydatetime.py
index ad6292e1e4..fc43cf0bba 100644
--- a/contrib/tools/python3/Lib/_pydatetime.py
+++ b/contrib/tools/python3/Lib/_pydatetime.py
@@ -2313,7 +2313,6 @@ datetime.resolution = timedelta(microseconds=1)
def _isoweek1monday(year):
# Helper to calculate the day number of the Monday starting week 1
- # XXX This could be done more efficiently
THURSDAY = 3
firstday = _ymd2ord(year, 1, 1)
firstweekday = (firstday + 6) % 7 # See weekday() above
diff --git a/contrib/tools/python3/Lib/_pydecimal.py b/contrib/tools/python3/Lib/_pydecimal.py
index 75df3db262..ff80180a79 100644
--- a/contrib/tools/python3/Lib/_pydecimal.py
+++ b/contrib/tools/python3/Lib/_pydecimal.py
@@ -97,7 +97,7 @@ class DecimalException(ArithmeticError):
Used exceptions derive from this.
If an exception derives from another exception besides this (such as
- Underflow (Inexact, Rounded, Subnormal) that indicates that it is only
+ Underflow (Inexact, Rounded, Subnormal)) that indicates that it is only
called if the others are present. This isn't actually used for
anything, though.
@@ -145,7 +145,7 @@ class InvalidOperation(DecimalException):
x ** (+-)INF
An operand is invalid
- The result of the operation after these is a quiet positive NaN,
+ The result of the operation after this is a quiet positive NaN,
except when the cause is a signaling NaN, in which case the result is
also a quiet NaN, but with the original sign, and an optional
diagnostic information.
diff --git a/contrib/tools/python3/Lib/_strptime.py b/contrib/tools/python3/Lib/_strptime.py
index dfd2bc5d8b..5d9df2b12f 100644
--- a/contrib/tools/python3/Lib/_strptime.py
+++ b/contrib/tools/python3/Lib/_strptime.py
@@ -300,8 +300,6 @@ class TimeRE(dict):
'V': r"(?P<V>5[0-3]|0[1-9]|[1-4]\d|\d)",
# W is set below by using 'U'
'y': r"(?P<y>\d\d)",
- #XXX: Does 'Y' need to worry about having less or more than
- # 4 digits?
'Y': r"(?P<Y>\d\d\d\d)",
'z': r"(?P<z>[+-]\d\d:?[0-5]\d(:?[0-5]\d(\.\d{1,6})?)?|(?-i:Z))",
'A': self.__seqToRE(self.locale_time.f_weekday, 'A'),
diff --git a/contrib/tools/python3/Lib/ast.py b/contrib/tools/python3/Lib/ast.py
index b0995fa7f1..6d9785cc48 100644
--- a/contrib/tools/python3/Lib/ast.py
+++ b/contrib/tools/python3/Lib/ast.py
@@ -1246,9 +1246,14 @@ class _Unparser(NodeVisitor):
fallback_to_repr = True
break
quote_types = new_quote_types
- elif "\n" in value:
- quote_types = [q for q in quote_types if q in _MULTI_QUOTES]
- assert quote_types
+ else:
+ if "\n" in value:
+ quote_types = [q for q in quote_types if q in _MULTI_QUOTES]
+ assert quote_types
+
+ new_quote_types = [q for q in quote_types if q not in value]
+ if new_quote_types:
+ quote_types = new_quote_types
new_fstring_parts.append(value)
if fallback_to_repr:
diff --git a/contrib/tools/python3/Lib/asyncio/base_events.py b/contrib/tools/python3/Lib/asyncio/base_events.py
index 3146f7f3f6..136c163182 100644
--- a/contrib/tools/python3/Lib/asyncio/base_events.py
+++ b/contrib/tools/python3/Lib/asyncio/base_events.py
@@ -466,7 +466,12 @@ class BaseEventLoop(events.AbstractEventLoop):
tasks._set_task_name(task, name)
- return task
+ try:
+ return task
+ finally:
+ # gh-128552: prevent a refcycle of
+ # task.exception().__traceback__->BaseEventLoop.create_task->task
+ del task
def set_task_factory(self, factory):
"""Set a task factory that will be used by loop.create_task().
@@ -1550,7 +1555,9 @@ class BaseEventLoop(events.AbstractEventLoop):
if reuse_address:
sock.setsockopt(
socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
- if reuse_port:
+ # Since Linux 6.12.9, SO_REUSEPORT is not allowed
+ # on other address families than AF_INET/AF_INET6.
+ if reuse_port and af in (socket.AF_INET, socket.AF_INET6):
_set_reuseport(sock)
# Disable IPv4/IPv6 dual stack support (enabled by
# default on Linux) which makes a single socket
diff --git a/contrib/tools/python3/Lib/asyncio/locks.py b/contrib/tools/python3/Lib/asyncio/locks.py
index ce5d8d5bfb..588dca6c0e 100644
--- a/contrib/tools/python3/Lib/asyncio/locks.py
+++ b/contrib/tools/python3/Lib/asyncio/locks.py
@@ -454,7 +454,7 @@ class Barrier(mixins._LoopBoundMixin):
def __init__(self, parties):
"""Create a barrier, initialised to 'parties' tasks."""
if parties < 1:
- raise ValueError('parties must be > 0')
+ raise ValueError('parties must be >= 1')
self._cond = Condition() # notify all tasks when state changes
diff --git a/contrib/tools/python3/Lib/asyncio/runners.py b/contrib/tools/python3/Lib/asyncio/runners.py
index 1b89236599..102ae78021 100644
--- a/contrib/tools/python3/Lib/asyncio/runners.py
+++ b/contrib/tools/python3/Lib/asyncio/runners.py
@@ -168,6 +168,7 @@ def run(main, *, debug=None, loop_factory=None):
running in the same thread.
If debug is True, the event loop will be run in debug mode.
+ If loop_factory is passed, it is used for new event loop creation.
This function always creates a new event loop and closes it at the end.
It should be used as a main entry point for asyncio programs, and should
diff --git a/contrib/tools/python3/Lib/asyncio/selector_events.py b/contrib/tools/python3/Lib/asyncio/selector_events.py
index 790711f834..160ed6ca13 100644
--- a/contrib/tools/python3/Lib/asyncio/selector_events.py
+++ b/contrib/tools/python3/Lib/asyncio/selector_events.py
@@ -1183,15 +1183,19 @@ class _SelectorSocketTransport(_SelectorTransport):
# If the entire buffer couldn't be written, register a write handler
if self._buffer:
self._loop._add_writer(self._sock_fd, self._write_ready)
+ self._maybe_pause_protocol()
def can_write_eof(self):
return True
def _call_connection_lost(self, exc):
- super()._call_connection_lost(exc)
- if self._empty_waiter is not None:
- self._empty_waiter.set_exception(
- ConnectionError("Connection is closed by peer"))
+ try:
+ super()._call_connection_lost(exc)
+ finally:
+ self._write_ready = None
+ if self._empty_waiter is not None:
+ self._empty_waiter.set_exception(
+ ConnectionError("Connection is closed by peer"))
def _make_empty_waiter(self):
if self._empty_waiter is not None:
@@ -1206,7 +1210,6 @@ class _SelectorSocketTransport(_SelectorTransport):
def close(self):
self._read_ready_cb = None
- self._write_ready = None
super().close()
diff --git a/contrib/tools/python3/Lib/asyncio/staggered.py b/contrib/tools/python3/Lib/asyncio/staggered.py
index 0f4df8855a..0afed64fdf 100644
--- a/contrib/tools/python3/Lib/asyncio/staggered.py
+++ b/contrib/tools/python3/Lib/asyncio/staggered.py
@@ -66,8 +66,27 @@ async def staggered_race(coro_fns, delay, *, loop=None):
enum_coro_fns = enumerate(coro_fns)
winner_result = None
winner_index = None
+ unhandled_exceptions = []
exceptions = []
- running_tasks = []
+ running_tasks = set()
+ on_completed_fut = None
+
+ def task_done(task):
+ running_tasks.discard(task)
+ if (
+ on_completed_fut is not None
+ and not on_completed_fut.done()
+ and not running_tasks
+ ):
+ on_completed_fut.set_result(None)
+
+ if task.cancelled():
+ return
+
+ exc = task.exception()
+ if exc is None:
+ return
+ unhandled_exceptions.append(exc)
async def run_one_coro(ok_to_start, previous_failed) -> None:
# in eager tasks this waits for the calling task to append this task
@@ -91,11 +110,11 @@ async def staggered_race(coro_fns, delay, *, loop=None):
this_failed = locks.Event()
next_ok_to_start = locks.Event()
next_task = loop.create_task(run_one_coro(next_ok_to_start, this_failed))
- running_tasks.append(next_task)
+ running_tasks.add(next_task)
+ next_task.add_done_callback(task_done)
# next_task has been appended to running_tasks so next_task is ok to
# start.
next_ok_to_start.set()
- assert len(running_tasks) == this_index + 2
# Prepare place to put this coroutine's exceptions if not won
exceptions.append(None)
assert len(exceptions) == this_index + 1
@@ -120,31 +139,36 @@ async def staggered_race(coro_fns, delay, *, loop=None):
# up as done() == True, cancelled() == False, exception() ==
# asyncio.CancelledError. This behavior is specified in
# https://bugs.python.org/issue30048
- for i, t in enumerate(running_tasks):
- if i != this_index:
+ current_task = tasks.current_task(loop)
+ for t in running_tasks:
+ if t is not current_task:
t.cancel()
- ok_to_start = locks.Event()
- first_task = loop.create_task(run_one_coro(ok_to_start, None))
- running_tasks.append(first_task)
- # first_task has been appended to running_tasks so first_task is ok to start.
- ok_to_start.set()
+ propagate_cancellation_error = None
try:
- # Wait for a growing list of tasks to all finish: poor man's version of
- # curio's TaskGroup or trio's nursery
- done_count = 0
- while done_count != len(running_tasks):
- done, _ = await tasks.wait(running_tasks)
- done_count = len(done)
+ ok_to_start = locks.Event()
+ first_task = loop.create_task(run_one_coro(ok_to_start, None))
+ running_tasks.add(first_task)
+ first_task.add_done_callback(task_done)
+ # first_task has been appended to running_tasks so first_task is ok to start.
+ ok_to_start.set()
+ propagate_cancellation_error = None
+ # Make sure no tasks are left running if we leave this function
+ while running_tasks:
+ on_completed_fut = loop.create_future()
+ try:
+ await on_completed_fut
+ except exceptions_mod.CancelledError as ex:
+ propagate_cancellation_error = ex
+ for task in running_tasks:
+ task.cancel(*ex.args)
+ on_completed_fut = None
+ if __debug__ and unhandled_exceptions:
# If run_one_coro raises an unhandled exception, it's probably a
# programming error, and I want to see it.
- if __debug__:
- for d in done:
- if d.done() and not d.cancelled() and d.exception():
- raise d.exception()
+ raise ExceptionGroup("staggered race failed", unhandled_exceptions)
+ if propagate_cancellation_error is not None:
+ raise propagate_cancellation_error
return winner_result, winner_index, exceptions
finally:
- del exceptions
- # Make sure no tasks are left running if we leave this function
- for t in running_tasks:
- t.cancel()
+ del exceptions, propagate_cancellation_error, unhandled_exceptions
diff --git a/contrib/tools/python3/Lib/asyncio/taskgroups.py b/contrib/tools/python3/Lib/asyncio/taskgroups.py
index aada3ffa8e..b2b953b093 100644
--- a/contrib/tools/python3/Lib/asyncio/taskgroups.py
+++ b/contrib/tools/python3/Lib/asyncio/taskgroups.py
@@ -185,15 +185,20 @@ class TaskGroup:
else:
task = self._loop.create_task(coro, context=context)
tasks._set_task_name(task, name)
- # optimization: Immediately call the done callback if the task is
+
+ # Always schedule the done callback even if the task is
# already done (e.g. if the coro was able to complete eagerly),
- # and skip scheduling a done callback
- if task.done():
- self._on_task_done(task)
- else:
- self._tasks.add(task)
- task.add_done_callback(self._on_task_done)
- return task
+ # otherwise if the task completes with an exception then it will cancel
+ # the current task too early. gh-128550, gh-128588
+
+ self._tasks.add(task)
+ task.add_done_callback(self._on_task_done)
+ try:
+ return task
+ finally:
+ # gh-128552: prevent a refcycle of
+ # task.exception().__traceback__->TaskGroup.create_task->task
+ del task
# Since Python 3.8 Tasks propagate all exceptions correctly,
# except for KeyboardInterrupt and SystemExit which are
diff --git a/contrib/tools/python3/Lib/bdb.py b/contrib/tools/python3/Lib/bdb.py
index 196e6b178c..085c17ce05 100644
--- a/contrib/tools/python3/Lib/bdb.py
+++ b/contrib/tools/python3/Lib/bdb.py
@@ -3,6 +3,7 @@
import fnmatch
import sys
import os
+from contextlib import contextmanager
from inspect import CO_GENERATOR, CO_COROUTINE, CO_ASYNC_GENERATOR
__all__ = ["BdbQuit", "Bdb", "Breakpoint"]
@@ -33,6 +34,7 @@ class Bdb:
self.breaks = {}
self.fncache = {}
self.frame_returning = None
+ self.enterframe = None
self._load_breaks()
@@ -60,6 +62,12 @@ class Bdb:
self.botframe = None
self._set_stopinfo(None, None)
+ @contextmanager
+ def set_enterframe(self, frame):
+ self.enterframe = frame
+ yield
+ self.enterframe = None
+
def trace_dispatch(self, frame, event, arg):
"""Dispatch a trace function for debugged frames based on the event.
@@ -84,24 +92,26 @@ class Bdb:
The arg parameter depends on the previous event.
"""
- if self.quitting:
- return # None
- if event == 'line':
- return self.dispatch_line(frame)
- if event == 'call':
- return self.dispatch_call(frame, arg)
- if event == 'return':
- return self.dispatch_return(frame, arg)
- if event == 'exception':
- return self.dispatch_exception(frame, arg)
- if event == 'c_call':
- return self.trace_dispatch
- if event == 'c_exception':
- return self.trace_dispatch
- if event == 'c_return':
+
+ with self.set_enterframe(frame):
+ if self.quitting:
+ return # None
+ if event == 'line':
+ return self.dispatch_line(frame)
+ if event == 'call':
+ return self.dispatch_call(frame, arg)
+ if event == 'return':
+ return self.dispatch_return(frame, arg)
+ if event == 'exception':
+ return self.dispatch_exception(frame, arg)
+ if event == 'c_call':
+ return self.trace_dispatch
+ if event == 'c_exception':
+ return self.trace_dispatch
+ if event == 'c_return':
+ return self.trace_dispatch
+ print('bdb.Bdb.dispatch: unknown debugging event:', repr(event))
return self.trace_dispatch
- print('bdb.Bdb.dispatch: unknown debugging event:', repr(event))
- return self.trace_dispatch
def dispatch_line(self, frame):
"""Invoke user function and return trace function for line event.
@@ -332,14 +342,16 @@ class Bdb:
If frame is not specified, debugging starts from caller's frame.
"""
+ sys.settrace(None)
if frame is None:
frame = sys._getframe().f_back
self.reset()
- while frame:
- frame.f_trace = self.trace_dispatch
- self.botframe = frame
- frame = frame.f_back
- self.set_step()
+ with self.set_enterframe(frame):
+ while frame:
+ frame.f_trace = self.trace_dispatch
+ self.botframe = frame
+ frame = frame.f_back
+ self.set_step()
sys.settrace(self.trace_dispatch)
def set_continue(self):
@@ -394,6 +406,14 @@ class Bdb:
return 'Line %s:%d does not exist' % (filename, lineno)
self._add_to_breaks(filename, lineno)
bp = Breakpoint(filename, lineno, temporary, cond, funcname)
+ # After we set a new breakpoint, we need to search through all frames
+ # and set f_trace to trace_dispatch if there could be a breakpoint in
+ # that frame.
+ frame = self.enterframe
+ while frame:
+ if self.break_anywhere(frame):
+ frame.f_trace = self.trace_dispatch
+ frame = frame.f_back
return None
def _load_breaks(self):
diff --git a/contrib/tools/python3/Lib/dis.py b/contrib/tools/python3/Lib/dis.py
index 320dec03d2..b1069c8252 100644
--- a/contrib/tools/python3/Lib/dis.py
+++ b/contrib/tools/python3/Lib/dis.py
@@ -790,12 +790,12 @@ class Bytecode:
return output.getvalue()
-def main():
+def main(args=None):
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('infile', type=argparse.FileType('rb'), nargs='?', default='-')
- args = parser.parse_args()
+ args = parser.parse_args(args=args)
with args.infile as infile:
source = infile.read()
code = compile(source, args.infile.name, "exec")
diff --git a/contrib/tools/python3/Lib/email/_header_value_parser.py b/contrib/tools/python3/Lib/email/_header_value_parser.py
index ec2215a5e5..3d845c09d4 100644
--- a/contrib/tools/python3/Lib/email/_header_value_parser.py
+++ b/contrib/tools/python3/Lib/email/_header_value_parser.py
@@ -95,8 +95,16 @@ EXTENDED_ATTRIBUTE_ENDS = ATTRIBUTE_ENDS - set('%')
NLSET = {'\n', '\r'}
SPECIALSNL = SPECIALS | NLSET
+
+def make_quoted_pairs(value):
+ """Escape dquote and backslash for use within a quoted-string."""
+ return str(value).replace('\\', '\\\\').replace('"', '\\"')
+
+
def quote_string(value):
- return '"'+str(value).replace('\\', '\\\\').replace('"', r'\"')+'"'
+ escaped = make_quoted_pairs(value)
+ return f'"{escaped}"'
+
# Match a RFC 2047 word, looks like =?utf-8?q?someword?=
rfc2047_matcher = re.compile(r'''
@@ -2905,6 +2913,15 @@ def _refold_parse_tree(parse_tree, *, policy):
if not hasattr(part, 'encode'):
# It's not a terminal, try folding the subparts.
newparts = list(part)
+ if part.token_type == 'bare-quoted-string':
+ # To fold a quoted string we need to create a list of terminal
+ # tokens that will render the leading and trailing quotes
+ # and use quoted pairs in the value as appropriate.
+ newparts = (
+ [ValueTerminal('"', 'ptext')] +
+ [ValueTerminal(make_quoted_pairs(p), 'ptext')
+ for p in newparts] +
+ [ValueTerminal('"', 'ptext')])
if not part.as_ew_allowed:
wrap_as_ew_blocked += 1
newparts.append(end_ew_not_allowed)
diff --git a/contrib/tools/python3/Lib/email/message.py b/contrib/tools/python3/Lib/email/message.py
index 46bb8c2194..6b7c3a2377 100644
--- a/contrib/tools/python3/Lib/email/message.py
+++ b/contrib/tools/python3/Lib/email/message.py
@@ -286,8 +286,12 @@ class Message:
if i is not None and not isinstance(self._payload, list):
raise TypeError('Expected list, got %s' % type(self._payload))
payload = self._payload
- # cte might be a Header, so for now stringify it.
- cte = str(self.get('content-transfer-encoding', '')).lower()
+ cte = self.get('content-transfer-encoding', '')
+ if hasattr(cte, 'cte'):
+ cte = cte.cte
+ else:
+ # cte might be a Header, so for now stringify it.
+ cte = str(cte).strip().lower()
# payload may be bytes here.
if not decode:
if isinstance(payload, str) and utils._has_surrogates(payload):
diff --git a/contrib/tools/python3/Lib/functools.py b/contrib/tools/python3/Lib/functools.py
index f6849899e7..6025032bfa 100644
--- a/contrib/tools/python3/Lib/functools.py
+++ b/contrib/tools/python3/Lib/functools.py
@@ -340,6 +340,9 @@ class partial:
self.args = args
self.keywords = kwds
+ __class_getitem__ = classmethod(GenericAlias)
+
+
try:
from _functools import partial
except ImportError:
diff --git a/contrib/tools/python3/Lib/http/client.py b/contrib/tools/python3/Lib/http/client.py
index a353716a85..fb29923d94 100644
--- a/contrib/tools/python3/Lib/http/client.py
+++ b/contrib/tools/python3/Lib/http/client.py
@@ -472,7 +472,7 @@ class HTTPResponse(io.BufferedIOBase):
if self.chunked:
return self._read_chunked(amt)
- if amt is not None:
+ if amt is not None and amt >= 0:
if self.length is not None and amt > self.length:
# clip the read to the "end of response"
amt = self.length
@@ -590,6 +590,8 @@ class HTTPResponse(io.BufferedIOBase):
def _read_chunked(self, amt=None):
assert self.chunked != _UNKNOWN
+ if amt is not None and amt < 0:
+ amt = None
value = []
try:
while (chunk_left := self._get_chunk_left()) is not None:
diff --git a/contrib/tools/python3/Lib/http/cookies.py b/contrib/tools/python3/Lib/http/cookies.py
index 6b9ed24ad8..57791c6ab0 100644
--- a/contrib/tools/python3/Lib/http/cookies.py
+++ b/contrib/tools/python3/Lib/http/cookies.py
@@ -424,9 +424,11 @@ _CookiePattern = re.compile(r"""
( # Optional group: there may not be a value.
\s*=\s* # Equal Sign
(?P<val> # Start of group 'val'
- "(?:[^\\"]|\\.)*" # Any doublequoted string
+ "(?:[^\\"]|\\.)*" # Any double-quoted string
| # or
- \w{3},\s[\w\d\s-]{9,11}\s[\d:]{8}\sGMT # Special case for "expires" attr
+ # Special case for "expires" attr
+ (\w{3,6}day|\w{3}),\s # Day of the week or abbreviated day
+ [\w\d\s-]{9,11}\s[\d:]{8}\sGMT # Date and time in specific format
| # or
[""" + _LegalValueChars + r"""]* # Any word or empty string
) # End of group 'val'
diff --git a/contrib/tools/python3/Lib/imaplib.py b/contrib/tools/python3/Lib/imaplib.py
index 577b4b9b03..e337fe6471 100644
--- a/contrib/tools/python3/Lib/imaplib.py
+++ b/contrib/tools/python3/Lib/imaplib.py
@@ -52,6 +52,9 @@ AllowedVersions = ('IMAP4REV1', 'IMAP4') # Most recent first
# search command can be quite large, so we now use 1M.
_MAXLINE = 1000000
+# Data larger than this will be read in chunks, to prevent extreme
+# overallocation.
+_SAFE_BUF_SIZE = 1 << 20
# Commands
@@ -315,7 +318,13 @@ class IMAP4:
def read(self, size):
"""Read 'size' bytes from remote."""
- return self.file.read(size)
+ cursize = min(size, _SAFE_BUF_SIZE)
+ data = self.file.read(cursize)
+ while cursize < size and len(data) == cursize:
+ delta = min(cursize, size - cursize)
+ data += self.file.read(delta)
+ cursize += delta
+ return data
def readline(self):
diff --git a/contrib/tools/python3/Lib/importlib/resources/_common.py b/contrib/tools/python3/Lib/importlib/resources/_common.py
index a390253534..a85df4b399 100644
--- a/contrib/tools/python3/Lib/importlib/resources/_common.py
+++ b/contrib/tools/python3/Lib/importlib/resources/_common.py
@@ -93,12 +93,13 @@ def _infer_caller():
"""
def is_this_file(frame_info):
- return frame_info.filename == __file__
+ return frame_info.filename == stack[0].filename
def is_wrapper(frame_info):
return frame_info.function == 'wrapper'
- not_this_file = itertools.filterfalse(is_this_file, inspect.stack())
+ stack = inspect.stack()
+ not_this_file = itertools.filterfalse(is_this_file, stack)
# also exclude 'wrapper' due to singledispatch in the call stack
callers = itertools.filterfalse(is_wrapper, not_this_file)
return next(callers).frame
diff --git a/contrib/tools/python3/Lib/linecache.py b/contrib/tools/python3/Lib/linecache.py
index 05eb49d3b0..06eea3c94f 100644
--- a/contrib/tools/python3/Lib/linecache.py
+++ b/contrib/tools/python3/Lib/linecache.py
@@ -54,14 +54,17 @@ def checkcache(filename=None):
(This is not checked upon each call!)"""
if filename is None:
- filenames = list(cache.keys())
- elif filename in cache:
- filenames = [filename]
+ # get keys atomically
+ filenames = cache.copy().keys()
else:
- return
+ filenames = [filename]
for filename in filenames:
- entry = cache[filename]
+ try:
+ entry = cache[filename]
+ except KeyError:
+ continue
+
if len(entry) == 1:
# lazy cache entry, leave it lazy.
continue
diff --git a/contrib/tools/python3/Lib/multiprocessing/connection.py b/contrib/tools/python3/Lib/multiprocessing/connection.py
index fdbc3bda7d..81ed2ae51d 100644
--- a/contrib/tools/python3/Lib/multiprocessing/connection.py
+++ b/contrib/tools/python3/Lib/multiprocessing/connection.py
@@ -846,7 +846,7 @@ _MD5_DIGEST_LEN = 16
_LEGACY_LENGTHS = (_MD5ONLY_MESSAGE_LENGTH, _MD5_DIGEST_LEN)
-def _get_digest_name_and_payload(message: bytes) -> (str, bytes):
+def _get_digest_name_and_payload(message): # type: (bytes) -> tuple[str, bytes]
"""Returns a digest name and the payload for a response hash.
If a legacy protocol is detected based on the message length
diff --git a/contrib/tools/python3/Lib/multiprocessing/resource_tracker.py b/contrib/tools/python3/Lib/multiprocessing/resource_tracker.py
index 79e96ecf32..23fea295c3 100644
--- a/contrib/tools/python3/Lib/multiprocessing/resource_tracker.py
+++ b/contrib/tools/python3/Lib/multiprocessing/resource_tracker.py
@@ -142,13 +142,14 @@ class ResourceTracker(object):
# that can make the child die before it registers signal handlers
# for SIGINT and SIGTERM. The mask is unregistered after spawning
# the child.
+ prev_sigmask = None
try:
if _HAVE_SIGMASK:
- signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS)
+ prev_sigmask = signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS)
pid = util.spawnv_passfds(exe, args, fds_to_pass)
finally:
- if _HAVE_SIGMASK:
- signal.pthread_sigmask(signal.SIG_UNBLOCK, _IGNORED_SIGNALS)
+ if prev_sigmask is not None:
+ signal.pthread_sigmask(signal.SIG_SETMASK, prev_sigmask)
except:
os.close(w)
raise
diff --git a/contrib/tools/python3/Lib/multiprocessing/synchronize.py b/contrib/tools/python3/Lib/multiprocessing/synchronize.py
index 0f682b9a09..870c91349b 100644
--- a/contrib/tools/python3/Lib/multiprocessing/synchronize.py
+++ b/contrib/tools/python3/Lib/multiprocessing/synchronize.py
@@ -360,7 +360,7 @@ class Event(object):
return True
return False
- def __repr__(self) -> str:
+ def __repr__(self):
set_status = 'set' if self.is_set() else 'unset'
return f"<{type(self).__qualname__} at {id(self):#x} {set_status}>"
#
diff --git a/contrib/tools/python3/Lib/pdb.py b/contrib/tools/python3/Lib/pdb.py
index 1e1b5ea4f0..2a6e994dac 100755
--- a/contrib/tools/python3/Lib/pdb.py
+++ b/contrib/tools/python3/Lib/pdb.py
@@ -281,6 +281,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
if hasattr(self, 'curframe') and self.curframe:
self.curframe.f_globals.pop('__pdb_convenience_variables', None)
self.curframe = None
+ self.curframe_locals = {}
self.tb_lineno.clear()
def setup(self, f, tb):
diff --git a/contrib/tools/python3/Lib/platform.py b/contrib/tools/python3/Lib/platform.py
index c5b6048036..b86e683491 100755
--- a/contrib/tools/python3/Lib/platform.py
+++ b/contrib/tools/python3/Lib/platform.py
@@ -348,7 +348,8 @@ _WIN32_CLIENT_RELEASES = [
]
_WIN32_SERVER_RELEASES = [
- ((10, 1, 0), "post2022Server"),
+ ((10, 1, 0), "post2025Server"),
+ ((10, 0, 26100), "2025Server"),
((10, 0, 20348), "2022Server"),
((10, 0, 17763), "2019Server"),
((6, 4, 0), "2016Server"),
diff --git a/contrib/tools/python3/Lib/poplib.py b/contrib/tools/python3/Lib/poplib.py
index 9a5ef03c98..81b0138598 100644
--- a/contrib/tools/python3/Lib/poplib.py
+++ b/contrib/tools/python3/Lib/poplib.py
@@ -309,7 +309,7 @@ class POP3:
# optional commands:
def rpop(self, user):
- """Not sure what this does."""
+ """Send RPOP command to access the mailbox with an alternate user."""
return self._shortcmd('RPOP %s' % user)
diff --git a/contrib/tools/python3/Lib/pydoc.py b/contrib/tools/python3/Lib/pydoc.py
index e3745e5453..9dfa87b2a8 100755
--- a/contrib/tools/python3/Lib/pydoc.py
+++ b/contrib/tools/python3/Lib/pydoc.py
@@ -54,6 +54,7 @@ Richard Chamberlain, for the first implementation of textdoc.
# the current directory is changed with os.chdir(), an incorrect
# path will be displayed.
+import ast
import __future__
import builtins
import importlib._bootstrap
@@ -346,21 +347,29 @@ def ispackage(path):
return False
def source_synopsis(file):
- line = file.readline()
- while line[:1] == '#' or not line.strip():
- line = file.readline()
- if not line: break
- line = line.strip()
- if line[:4] == 'r"""': line = line[1:]
- if line[:3] == '"""':
- line = line[3:]
- if line[-1:] == '\\': line = line[:-1]
- while not line.strip():
- line = file.readline()
- if not line: break
- result = line.split('"""')[0].strip()
- else: result = None
- return result
+ """Return the one-line summary of a file object, if present"""
+
+ string = ''
+ try:
+ tokens = tokenize.generate_tokens(file.readline)
+ for tok_type, tok_string, _, _, _ in tokens:
+ if tok_type == tokenize.STRING:
+ string += tok_string
+ elif tok_type == tokenize.NEWLINE:
+ with warnings.catch_warnings():
+ # Ignore the "invalid escape sequence" warning.
+ warnings.simplefilter("ignore", SyntaxWarning)
+ docstring = ast.literal_eval(string)
+ if not isinstance(docstring, str):
+ return None
+ return docstring.strip().split('\n')[0].strip()
+ elif tok_type == tokenize.OP and tok_string in ('(', ')'):
+ string += tok_string
+ elif tok_type not in (tokenize.COMMENT, tokenize.NL, tokenize.ENCODING):
+ return None
+ except (tokenize.TokenError, UnicodeDecodeError, SyntaxError):
+ return None
+ return None
def synopsis(filename, cache={}):
"""Get the one-line summary out of a module file."""
diff --git a/contrib/tools/python3/Lib/pydoc_data/topics.py b/contrib/tools/python3/Lib/pydoc_data/topics.py
index 12523999ca..66204433c0 100644
--- a/contrib/tools/python3/Lib/pydoc_data/topics.py
+++ b/contrib/tools/python3/Lib/pydoc_data/topics.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Autogenerated by Sphinx on Tue Dec 3 19:41:14 2024
+# Autogenerated by Sphinx on Tue Feb 4 15:37:58 2025
# as part of the release process.
topics = {'assert': 'The "assert" statement\n'
'**********************\n'
@@ -2769,15 +2769,18 @@ topics = {'assert': 'The "assert" statement\n'
' enter = type(manager).__enter__\n'
' exit = type(manager).__exit__\n'
' value = enter(manager)\n'
+ ' hit_except = False\n'
'\n'
' try:\n'
' TARGET = value\n'
' SUITE\n'
' except:\n'
+ ' hit_except = True\n'
' if not exit(manager, *sys.exc_info()):\n'
' raise\n'
- ' else:\n'
- ' exit(manager, None, None, None)\n'
+ ' finally:\n'
+ ' if not hit_except:\n'
+ ' exit(manager, None, None, None)\n'
'\n'
'With more than one item, the context managers are processed as '
'if\n'
@@ -3578,8 +3581,11 @@ topics = {'assert': 'The "assert" statement\n'
'defparameter)* ["," [parameter_list_starargs]]\n'
' | parameter_list_starargs\n'
' parameter_list_starargs ::= "*" [star_parameter] ("," '
- 'defparameter)* ["," ["**" parameter [","]]]\n'
- ' | "**" parameter [","]\n'
+ 'defparameter)* ["," [parameter_star_kwargs]]\n'
+ ' "*" ("," defparameter)+ ["," '
+ '[parameter_star_kwargs]]\n'
+ ' | parameter_star_kwargs\n'
+ ' parameter_star_kwargs ::= "**" parameter [","]\n'
' parameter ::= identifier [":" expression]\n'
' star_parameter ::= identifier [":" ["*"] '
'expression]\n'
@@ -6908,8 +6914,12 @@ topics = {'assert': 'The "assert" statement\n'
'trailing zeros are not removed from the result.\n'
'\n'
'The "\',\'" option signals the use of a comma for a '
- 'thousands separator.\n'
- 'For a locale aware separator, use the "\'n\'" integer '
+ 'thousands separator\n'
+ 'for floating-point presentation types and for integer '
+ 'presentation\n'
+ 'type "\'d\'". For other presentation types, this option is '
+ 'an error. For\n'
+ 'a locale aware separator, use the "\'n\'" integer '
'presentation type\n'
'instead.\n'
'\n'
@@ -7417,8 +7427,11 @@ topics = {'assert': 'The "assert" statement\n'
'defparameter)* ["," [parameter_list_starargs]]\n'
' | parameter_list_starargs\n'
' parameter_list_starargs ::= "*" [star_parameter] ("," '
- 'defparameter)* ["," ["**" parameter [","]]]\n'
- ' | "**" parameter [","]\n'
+ 'defparameter)* ["," [parameter_star_kwargs]]\n'
+ ' "*" ("," defparameter)+ ["," '
+ '[parameter_star_kwargs]]\n'
+ ' | parameter_star_kwargs\n'
+ ' parameter_star_kwargs ::= "**" parameter [","]\n'
' parameter ::= identifier [":" expression]\n'
' star_parameter ::= identifier [":" ["*"] '
'expression]\n'
@@ -16976,15 +16989,18 @@ topics = {'assert': 'The "assert" statement\n'
' enter = type(manager).__enter__\n'
' exit = type(manager).__exit__\n'
' value = enter(manager)\n'
+ ' hit_except = False\n'
'\n'
' try:\n'
' TARGET = value\n'
' SUITE\n'
' except:\n'
+ ' hit_except = True\n'
' if not exit(manager, *sys.exc_info()):\n'
' raise\n'
- ' else:\n'
- ' exit(manager, None, None, None)\n'
+ ' finally:\n'
+ ' if not hit_except:\n'
+ ' exit(manager, None, None, None)\n'
'\n'
'With more than one item, the context managers are processed as if\n'
'multiple "with" statements were nested:\n'
diff --git a/contrib/tools/python3/Lib/socket.py b/contrib/tools/python3/Lib/socket.py
index c1880c4ea5..91782b30ae 100644
--- a/contrib/tools/python3/Lib/socket.py
+++ b/contrib/tools/python3/Lib/socket.py
@@ -932,7 +932,9 @@ def create_server(address, *, family=AF_INET, backlog=None, reuse_port=False,
# Fail later on bind(), for platforms which may not
# support this option.
pass
- if reuse_port:
+ # Since Linux 6.12.9, SO_REUSEPORT is not allowed
+ # on other address families than AF_INET/AF_INET6.
+ if reuse_port and family in (AF_INET, AF_INET6):
sock.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1)
if has_ipv6 and family == AF_INET6:
if dualstack_ipv6:
diff --git a/contrib/tools/python3/Lib/socketserver.py b/contrib/tools/python3/Lib/socketserver.py
index cd028ef1c6..35b2723de3 100644
--- a/contrib/tools/python3/Lib/socketserver.py
+++ b/contrib/tools/python3/Lib/socketserver.py
@@ -468,7 +468,12 @@ class TCPServer(BaseServer):
"""
if self.allow_reuse_address and hasattr(socket, "SO_REUSEADDR"):
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- if self.allow_reuse_port and hasattr(socket, "SO_REUSEPORT"):
+ # Since Linux 6.12.9, SO_REUSEPORT is not allowed
+ # on other address families than AF_INET/AF_INET6.
+ if (
+ self.allow_reuse_port and hasattr(socket, "SO_REUSEPORT")
+ and self.address_family in (socket.AF_INET, socket.AF_INET6)
+ ):
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
self.socket.bind(self.server_address)
self.server_address = self.socket.getsockname()
diff --git a/contrib/tools/python3/Lib/subprocess.py b/contrib/tools/python3/Lib/subprocess.py
index 1d17ae3608..3ec39ca3e6 100644
--- a/contrib/tools/python3/Lib/subprocess.py
+++ b/contrib/tools/python3/Lib/subprocess.py
@@ -43,10 +43,8 @@ getstatusoutput(...): Runs a command in the shell, waits for it to complete,
import builtins
import errno
import io
-import locale
import os
import time
-import signal
import sys
import threading
import warnings
@@ -138,6 +136,8 @@ class CalledProcessError(SubprocessError):
def __str__(self):
if self.returncode and self.returncode < 0:
+ # Lazy import to improve module import time
+ import signal
try:
return "Command '%s' died with %r." % (
self.cmd, signal.Signals(-self.returncode))
@@ -375,12 +375,14 @@ def _text_encoding():
if sys.flags.utf8_mode:
return "utf-8"
else:
+ # Lazy import to improve module import time
+ import locale
return locale.getencoding()
def call(*popenargs, timeout=None, **kwargs):
"""Run command with arguments. Wait for command to complete or
- timeout, then return the returncode attribute.
+ for timeout seconds, then return the returncode attribute.
The arguments are the same as for the Popen constructor. Example:
@@ -517,8 +519,8 @@ def run(*popenargs,
in the returncode attribute, and output & stderr attributes if those streams
were captured.
- If timeout is given, and the process takes too long, a TimeoutExpired
- exception will be raised.
+ If timeout (seconds) is given and the process takes too long,
+ a TimeoutExpired exception will be raised.
There is an optional argument "input", allowing you to
pass bytes or a string to the subprocess's stdin. If you use this argument
@@ -1655,6 +1657,9 @@ class Popen:
# Don't signal a process that we know has already died.
if self.returncode is not None:
return
+
+ # Lazy import to improve module import time
+ import signal
if sig == signal.SIGTERM:
self.terminate()
elif sig == signal.CTRL_C_EVENT:
@@ -1759,6 +1764,9 @@ class Popen:
kwargs = {}
if restore_signals:
+ # Lazy import to improve module import time
+ import signal
+
# See _Py_RestoreSignals() in Python/pylifecycle.c
sigset = []
for signame in ('SIGPIPE', 'SIGXFZ', 'SIGXFSZ'):
@@ -2208,9 +2216,13 @@ class Popen:
def terminate(self):
"""Terminate the process with SIGTERM
"""
+ # Lazy import to improve module import time
+ import signal
self.send_signal(signal.SIGTERM)
def kill(self):
"""Kill the process with SIGKILL
"""
+ # Lazy import to improve module import time
+ import signal
self.send_signal(signal.SIGKILL)
diff --git a/contrib/tools/python3/Lib/sysconfig.py b/contrib/tools/python3/Lib/sysconfig.py
index 9bb81e7842..168f42871b 100644
--- a/contrib/tools/python3/Lib/sysconfig.py
+++ b/contrib/tools/python3/Lib/sysconfig.py
@@ -765,7 +765,8 @@ def get_platform():
solaris-2.6-sun4u
Windows will return one of:
- win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
+ win-amd64 (64-bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
+ win-arm64 (64-bit Windows on ARM64 (aka AArch64)
win32 (all others - specifically, sys.platform is returned)
For other non-POSIX platforms, currently just returns 'sys.platform'.
diff --git a/contrib/tools/python3/Lib/threading.py b/contrib/tools/python3/Lib/threading.py
index 0bba85d08a..064c74d40f 100644
--- a/contrib/tools/python3/Lib/threading.py
+++ b/contrib/tools/python3/Lib/threading.py
@@ -686,7 +686,7 @@ class Barrier:
"""
if parties < 1:
- raise ValueError("parties must be > 0")
+ raise ValueError("parties must be >= 1")
self._cond = Condition(Lock())
self._action = action
self._timeout = timeout
diff --git a/contrib/tools/python3/Lib/tokenize.py b/contrib/tools/python3/Lib/tokenize.py
index b2dff8e696..553c1ca438 100644
--- a/contrib/tools/python3/Lib/tokenize.py
+++ b/contrib/tools/python3/Lib/tokenize.py
@@ -320,16 +320,10 @@ def untokenize(iterable):
with at least two elements, a token number and token value. If
only two tokens are passed, the resulting output is poor.
- Round-trip invariant for full input:
- Untokenized source will match input source exactly
-
- Round-trip invariant for limited input:
- # Output bytes will tokenize back to the input
- t1 = [tok[:2] for tok in tokenize(f.readline)]
- newcode = untokenize(t1)
- readline = BytesIO(newcode).readline
- t2 = [tok[:2] for tok in tokenize(readline)]
- assert t1 == t2
+ The result is guaranteed to tokenize back to match the input so
+ that the conversion is lossless and round-trips are assured.
+ The guarantee applies only to the token type and token string as
+ the spacing between tokens (column positions) may change.
"""
ut = Untokenizer()
out = ut.untokenize(iterable)
diff --git a/contrib/tools/python3/Lib/typing.py b/contrib/tools/python3/Lib/typing.py
index a271416d46..d7f96b60f0 100644
--- a/contrib/tools/python3/Lib/typing.py
+++ b/contrib/tools/python3/Lib/typing.py
@@ -1610,12 +1610,16 @@ class _UnionGenericAlias(_NotIterable, _GenericAlias, _root=True):
return super().__repr__()
def __instancecheck__(self, obj):
- return self.__subclasscheck__(type(obj))
+ for arg in self.__args__:
+ if isinstance(obj, arg):
+ return True
+ return False
def __subclasscheck__(self, cls):
for arg in self.__args__:
if issubclass(cls, arg):
return True
+ return False
def __reduce__(self):
func, (origin, args) = super().__reduce__()
diff --git a/contrib/tools/python3/Lib/urllib/parse.py b/contrib/tools/python3/Lib/urllib/parse.py
index 2481595203..c72138a33c 100644
--- a/contrib/tools/python3/Lib/urllib/parse.py
+++ b/contrib/tools/python3/Lib/urllib/parse.py
@@ -436,6 +436,23 @@ def _checknetloc(netloc):
raise ValueError("netloc '" + netloc + "' contains invalid " +
"characters under NFKC normalization")
+def _check_bracketed_netloc(netloc):
+ # Note that this function must mirror the splitting
+ # done in NetlocResultMixins._hostinfo().
+ hostname_and_port = netloc.rpartition('@')[2]
+ before_bracket, have_open_br, bracketed = hostname_and_port.partition('[')
+ if have_open_br:
+ # No data is allowed before a bracket.
+ if before_bracket:
+ raise ValueError("Invalid IPv6 URL")
+ hostname, _, port = bracketed.partition(']')
+ # No data is allowed after the bracket but before the port delimiter.
+ if port and not port.startswith(":"):
+ raise ValueError("Invalid IPv6 URL")
+ else:
+ hostname, _, port = hostname_and_port.partition(':')
+ _check_bracketed_host(hostname)
+
# Valid bracketed hosts are defined in
# https://www.rfc-editor.org/rfc/rfc3986#page-49 and https://url.spec.whatwg.org/
def _check_bracketed_host(hostname):
@@ -496,8 +513,7 @@ def urlsplit(url, scheme='', allow_fragments=True):
(']' in netloc and '[' not in netloc)):
raise ValueError("Invalid IPv6 URL")
if '[' in netloc and ']' in netloc:
- bracketed_host = netloc.partition('[')[2].partition(']')[0]
- _check_bracketed_host(bracketed_host)
+ _check_bracketed_netloc(netloc)
if allow_fragments and '#' in url:
url, fragment = url.split('#', 1)
if '?' in url:
diff --git a/contrib/tools/python3/Lib/urllib/request.py b/contrib/tools/python3/Lib/urllib/request.py
index 9a559f4415..c7ded0f67f 100644
--- a/contrib/tools/python3/Lib/urllib/request.py
+++ b/contrib/tools/python3/Lib/urllib/request.py
@@ -903,9 +903,9 @@ class HTTPPasswordMgrWithDefaultRealm(HTTPPasswordMgr):
class HTTPPasswordMgrWithPriorAuth(HTTPPasswordMgrWithDefaultRealm):
- def __init__(self, *args, **kwargs):
+ def __init__(self):
self.authenticated = {}
- super().__init__(*args, **kwargs)
+ super().__init__()
def add_password(self, realm, uri, user, passwd, is_authenticated=False):
self.update_authenticated(uri, is_authenticated)
diff --git a/contrib/tools/python3/Lib/urllib/robotparser.py b/contrib/tools/python3/Lib/urllib/robotparser.py
index c58565e394..409f2b2e48 100644
--- a/contrib/tools/python3/Lib/urllib/robotparser.py
+++ b/contrib/tools/python3/Lib/urllib/robotparser.py
@@ -11,6 +11,7 @@
"""
import collections
+import urllib.error
import urllib.parse
import urllib.request
@@ -65,6 +66,7 @@ class RobotFileParser:
self.disallow_all = True
elif err.code >= 400 and err.code < 500:
self.allow_all = True
+ err.close()
else:
raw = f.read()
self.parse(raw.decode("utf-8").splitlines())
diff --git a/contrib/tools/python3/Lib/xml/dom/xmlbuilder.py b/contrib/tools/python3/Lib/xml/dom/xmlbuilder.py
index 8a20026349..a8852625a2 100644
--- a/contrib/tools/python3/Lib/xml/dom/xmlbuilder.py
+++ b/contrib/tools/python3/Lib/xml/dom/xmlbuilder.py
@@ -189,7 +189,7 @@ class DOMBuilder:
options.filter = self.filter
options.errorHandler = self.errorHandler
fp = input.byteStream
- if fp is None and options.systemId:
+ if fp is None and input.systemId:
import urllib.request
fp = urllib.request.urlopen(input.systemId)
return self._parse_bytestream(fp, options)
@@ -247,10 +247,12 @@ class DOMEntityResolver(object):
def _guess_media_encoding(self, source):
info = source.byteStream.info()
- if "Content-Type" in info:
- for param in info.getplist():
- if param.startswith("charset="):
- return param.split("=", 1)[1].lower()
+ # import email.message
+ # assert isinstance(info, email.message.Message)
+ charset = info.get_param('charset')
+ if charset is not None:
+ return charset.lower()
+ return None
class DOMInputSource(object):
diff --git a/contrib/tools/python3/Lib/ya.make b/contrib/tools/python3/Lib/ya.make
index ba53fefab3..2310e5fa99 100644
--- a/contrib/tools/python3/Lib/ya.make
+++ b/contrib/tools/python3/Lib/ya.make
@@ -4,9 +4,9 @@ ENABLE(PYBUILD_NO_PY)
PY3_LIBRARY()
-VERSION(3.12.8)
+VERSION(3.12.9)
-ORIGINAL_SOURCE(https://github.com/python/cpython/archive/v3.12.8.tar.gz)
+ORIGINAL_SOURCE(https://github.com/python/cpython/archive/v3.12.9.tar.gz)
LICENSE(Python-2.0)
diff --git a/contrib/tools/python3/Lib/zipfile/__init__.py b/contrib/tools/python3/Lib/zipfile/__init__.py
index cf71c6dba2..91b2e032e5 100644
--- a/contrib/tools/python3/Lib/zipfile/__init__.py
+++ b/contrib/tools/python3/Lib/zipfile/__init__.py
@@ -794,7 +794,10 @@ class _SharedFile:
raise ValueError("Can't reposition in the ZIP file while "
"there is an open writing handle on it. "
"Close the writing handle before trying to read.")
- self._file.seek(offset, whence)
+ if whence == os.SEEK_CUR:
+ self._file.seek(self._pos + offset)
+ else:
+ self._file.seek(offset, whence)
self._pos = self._file.tell()
return self._pos
@@ -1137,13 +1140,15 @@ class ZipExtFile(io.BufferedIOBase):
self._offset = buff_offset
read_offset = 0
# Fast seek uncompressed unencrypted file
- elif self._compress_type == ZIP_STORED and self._decrypter is None and read_offset > 0:
+ elif self._compress_type == ZIP_STORED and self._decrypter is None and read_offset != 0:
# disable CRC checking after first seeking - it would be invalid
self._expected_crc = None
# seek actual file taking already buffered data into account
read_offset -= len(self._readbuffer) - self._offset
self._fileobj.seek(read_offset, os.SEEK_CUR)
self._left -= read_offset
+ self._compress_left -= read_offset
+ self._eof = self._left <= 0
read_offset = 0
# flush read buffer
self._readbuffer = b''
diff --git a/contrib/tools/python3/Modules/_csv.c b/contrib/tools/python3/Modules/_csv.c
index 9a7b7d27c2..df7207d089 100644
--- a/contrib/tools/python3/Modules/_csv.c
+++ b/contrib/tools/python3/Modules/_csv.c
@@ -1074,7 +1074,7 @@ join_append_data(WriterObj *self, int field_kind, const void *field_data,
int copy_phase)
{
DialectObj *dialect = self->dialect;
- int i;
+ Py_ssize_t i;
Py_ssize_t rec_len;
#define INCLEN \
diff --git a/contrib/tools/python3/Modules/_ctypes/_ctypes.c b/contrib/tools/python3/Modules/_ctypes/_ctypes.c
index b6d45e9264..96a0575549 100644
--- a/contrib/tools/python3/Modules/_ctypes/_ctypes.c
+++ b/contrib/tools/python3/Modules/_ctypes/_ctypes.c
@@ -125,6 +125,7 @@ bytes(cdata)
#include "ctypes.h"
#include "pycore_long.h" // _PyLong_GetZero()
+#include "pycore_pyerrors.h" // _PyErr_SetLocaleString()
ctypes_state global_state;
@@ -778,31 +779,38 @@ CDataType_in_dll(PyObject *type, PyObject *args)
return NULL;
}
+#undef USE_DLERROR
#ifdef MS_WIN32
Py_BEGIN_ALLOW_THREADS
address = (void *)GetProcAddress(handle, name);
Py_END_ALLOW_THREADS
- if (!address) {
- PyErr_Format(PyExc_ValueError,
- "symbol '%s' not found",
- name);
- return NULL;
- }
#else
+ #ifdef __CYGWIN__
+ // dlerror() isn't very helpful on cygwin
+ #else
+ #define USE_DLERROR
+ /* dlerror() always returns the latest error.
+ *
+ * Clear the previous value before calling dlsym(),
+ * to ensure we can tell if our call resulted in an error.
+ */
+ (void)dlerror();
+ #endif
address = (void *)dlsym(handle, name);
- if (!address) {
-#ifdef __CYGWIN__
-/* dlerror() isn't very helpful on cygwin */
- PyErr_Format(PyExc_ValueError,
- "symbol '%s' not found",
- name);
-#else
- PyErr_SetString(PyExc_ValueError, dlerror());
#endif
+ if (address) {
+ return PyCData_AtAddress(type, address);
+ }
+ #ifdef USE_DLERROR
+ const char *dlerr = dlerror();
+ if (dlerr) {
+ _PyErr_SetLocaleString(PyExc_ValueError, dlerr);
return NULL;
}
-#endif
- return PyCData_AtAddress(type, address);
+ #endif
+#undef USE_DLERROR
+ PyErr_Format(PyExc_ValueError, "symbol '%s' not found", name);
+ return NULL;
}
PyDoc_STRVAR(from_param_doc,
@@ -847,8 +855,13 @@ CDataType_from_param(PyObject *type, PyObject *value)
return NULL;
}
if (as_parameter) {
+ if (_Py_EnterRecursiveCall(" while processing _as_parameter_")) {
+ Py_DECREF(as_parameter);
+ return NULL;
+ }
value = CDataType_from_param(type, as_parameter);
Py_DECREF(as_parameter);
+ _Py_LeaveRecursiveCall();
return value;
}
PyErr_Format(PyExc_TypeError,
@@ -1716,8 +1729,13 @@ c_wchar_p_from_param(PyObject *type, PyObject *value)
return NULL;
}
if (as_parameter) {
+ if (_Py_EnterRecursiveCall(" while processing _as_parameter_")) {
+ Py_DECREF(as_parameter);
+ return NULL;
+ }
value = c_wchar_p_from_param(type, as_parameter);
Py_DECREF(as_parameter);
+ _Py_LeaveRecursiveCall();
return value;
}
/* XXX better message */
@@ -1780,8 +1798,13 @@ c_char_p_from_param(PyObject *type, PyObject *value)
return NULL;
}
if (as_parameter) {
+ if (_Py_EnterRecursiveCall(" while processing _as_parameter_")) {
+ Py_DECREF(as_parameter);
+ return NULL;
+ }
value = c_char_p_from_param(type, as_parameter);
Py_DECREF(as_parameter);
+ _Py_LeaveRecursiveCall();
return value;
}
/* XXX better message */
@@ -1915,8 +1938,13 @@ c_void_p_from_param(PyObject *type, PyObject *value)
return NULL;
}
if (as_parameter) {
+ if (_Py_EnterRecursiveCall(" while processing _as_parameter_")) {
+ Py_DECREF(as_parameter);
+ return NULL;
+ }
value = c_void_p_from_param(type, as_parameter);
Py_DECREF(as_parameter);
+ _Py_LeaveRecursiveCall();
return value;
}
/* XXX better message */
@@ -2275,9 +2303,9 @@ PyCSimpleType_from_param(PyObject *type, PyObject *value)
return NULL;
}
value = PyCSimpleType_from_param(type, as_parameter);
- _Py_LeaveRecursiveCall();
Py_DECREF(as_parameter);
Py_XDECREF(exc);
+ _Py_LeaveRecursiveCall();
return value;
}
if (exc) {
@@ -3583,6 +3611,7 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
return NULL;
}
+#undef USE_DLERROR
#ifdef MS_WIN32
address = FindAddress(handle, name, (PyObject *)type);
if (!address) {
@@ -3598,20 +3627,33 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
return NULL;
}
#else
+ #ifdef __CYGWIN__
+ //dlerror() isn't very helpful on cygwin */
+ #else
+ #define USE_DLERROR
+ /* dlerror() always returns the latest error.
+ *
+ * Clear the previous value before calling dlsym(),
+ * to ensure we can tell if our call resulted in an error.
+ */
+ (void)dlerror();
+ #endif
address = (PPROC)dlsym(handle, name);
if (!address) {
-#ifdef __CYGWIN__
-/* dlerror() isn't very helpful on cygwin */
- PyErr_Format(PyExc_AttributeError,
- "function '%s' not found",
- name);
-#else
- PyErr_SetString(PyExc_AttributeError, dlerror());
-#endif
+ #ifdef USE_DLERROR
+ const char *dlerr = dlerror();
+ if (dlerr) {
+ _PyErr_SetLocaleString(PyExc_AttributeError, dlerr);
+ Py_DECREF(ftuple);
+ return NULL;
+ }
+ #endif
+ PyErr_Format(PyExc_AttributeError, "function '%s' not found", name);
Py_DECREF(ftuple);
return NULL;
}
#endif
+#undef USE_DLERROR
if (!_validate_paramflags(type, paramflags)) {
Py_DECREF(ftuple);
return NULL;
diff --git a/contrib/tools/python3/Modules/_ctypes/callbacks.c b/contrib/tools/python3/Modules/_ctypes/callbacks.c
index d71297f9c5..65dfb980a3 100644
--- a/contrib/tools/python3/Modules/_ctypes/callbacks.c
+++ b/contrib/tools/python3/Modules/_ctypes/callbacks.c
@@ -82,22 +82,6 @@ PyType_Spec cthunk_spec = {
/**************************************************************/
-static void
-PrintError(const char *msg, ...)
-{
- char buf[512];
- PyObject *f = PySys_GetObject("stderr");
- va_list marker;
-
- va_start(marker, msg);
- PyOS_vsnprintf(buf, sizeof(buf), msg, marker);
- va_end(marker);
- if (f != NULL && f != Py_None)
- PyFile_WriteString(buf, f);
- PyErr_Print();
-}
-
-
#ifdef MS_WIN32
/*
* We must call AddRef() on non-NULL COM pointers we receive as arguments
@@ -109,22 +93,19 @@ PrintError(const char *msg, ...)
* after checking for PyObject_IsTrue(), but this would probably be somewhat
* slower.
*/
-static void
+static int
TryAddRef(StgDictObject *dict, CDataObject *obj)
{
IUnknown *punk;
int r = PyDict_Contains((PyObject *)dict, &_Py_ID(_needs_com_addref_));
if (r <= 0) {
- if (r < 0) {
- PrintError("getting _needs_com_addref_");
- }
- return;
+ return r;
}
punk = *(IUnknown **)obj->b_ptr;
if (punk)
punk->lpVtbl->AddRef(punk);
- return;
+ return 0;
}
#endif
@@ -160,8 +141,7 @@ static void _CallPythonObject(void *mem,
if (dict && dict->getfunc && !_ctypes_simple_instance(cnv)) {
PyObject *v = dict->getfunc(*pArgs, dict->size);
if (!v) {
- PrintError("create argument %zd:\n", i);
- goto Done;
+ goto Error;
}
args[i] = v;
/* XXX XXX XX
@@ -173,24 +153,25 @@ static void _CallPythonObject(void *mem,
/* Hm, shouldn't we use PyCData_AtAddress() or something like that instead? */
CDataObject *obj = (CDataObject *)_PyObject_CallNoArgs(cnv);
if (!obj) {
- PrintError("create argument %zd:\n", i);
- goto Done;
+ goto Error;
}
if (!CDataObject_Check(obj)) {
+ PyErr_Format(PyExc_TypeError,
+ "%R returned unexpected result of type %T", cnv, obj);
Py_DECREF(obj);
- PrintError("unexpected result of create argument %zd:\n", i);
- goto Done;
+ goto Error;
}
memcpy(obj->b_ptr, *pArgs, dict->size);
args[i] = (PyObject *)obj;
#ifdef MS_WIN32
- TryAddRef(dict, obj);
+ if (TryAddRef(dict, obj) < 0) {
+ goto Error;
+ }
#endif
} else {
- PyErr_SetString(PyExc_TypeError,
- "cannot build parameter");
- PrintError("Parsing argument %zd\n", i);
- goto Done;
+ PyErr_Format(PyExc_TypeError,
+ "cannot build parameter of type %R", cnv);
+ goto Error;
}
/* XXX error handling! */
pArgs++;
@@ -198,8 +179,12 @@ static void _CallPythonObject(void *mem,
if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
error_object = _ctypes_get_errobj(&space);
- if (error_object == NULL)
+ if (error_object == NULL) {
+ _PyErr_WriteUnraisableMsg("while setting error for "
+ "ctypes callback function",
+ callable);
goto Done;
+ }
if (flags & FUNCFLAG_USE_ERRNO) {
int temp = space[0];
space[0] = errno;
@@ -284,6 +269,13 @@ static void _CallPythonObject(void *mem,
Py_DECREF(args[j]);
}
PyGILState_Release(state);
+ return;
+
+ Error:
+ _PyErr_WriteUnraisableMsg("while creating argument for "
+ "ctypes callback function",
+ callable);
+ goto Done;
}
static void closure_fcn(ffi_cif *cif,
diff --git a/contrib/tools/python3/Modules/_ctypes/callproc.c b/contrib/tools/python3/Modules/_ctypes/callproc.c
index d2fe525dd4..f98479ee85 100644
--- a/contrib/tools/python3/Modules/_ctypes/callproc.c
+++ b/contrib/tools/python3/Modules/_ctypes/callproc.c
@@ -96,6 +96,7 @@
#define DONT_USE_SEH
#endif
+#include "pycore_pyerrors.h" // _PyErr_SetLocaleString()
#include "pycore_runtime.h" // _PyRuntime
#include "pycore_global_objects.h" // _Py_ID()
@@ -1550,10 +1551,11 @@ static PyObject *py_dl_open(PyObject *self, PyObject *args)
Py_XDECREF(name2);
if (!handle) {
const char *errmsg = dlerror();
- if (!errmsg)
- errmsg = "dlopen() error";
- PyErr_SetString(PyExc_OSError,
- errmsg);
+ if (errmsg) {
+ _PyErr_SetLocaleString(PyExc_OSError, errmsg);
+ return NULL;
+ }
+ PyErr_SetString(PyExc_OSError, "dlopen() error");
return NULL;
}
return PyLong_FromVoidPtr(handle);
@@ -1566,8 +1568,12 @@ static PyObject *py_dl_close(PyObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "O&:dlclose", &_parse_voidp, &handle))
return NULL;
if (dlclose(handle)) {
- PyErr_SetString(PyExc_OSError,
- dlerror());
+ const char *errmsg = dlerror();
+ if (errmsg) {
+ _PyErr_SetLocaleString(PyExc_OSError, errmsg);
+ return NULL;
+ }
+ PyErr_SetString(PyExc_OSError, "dlclose() error");
return NULL;
}
Py_RETURN_NONE;
@@ -1585,13 +1591,32 @@ static PyObject *py_dl_sym(PyObject *self, PyObject *args)
if (PySys_Audit("ctypes.dlsym/handle", "O", args) < 0) {
return NULL;
}
+#undef USE_DLERROR
+ #ifdef __CYGWIN__
+ // dlerror() isn't very helpful on cygwin
+ #else
+ #define USE_DLERROR
+ /* dlerror() always returns the latest error.
+ *
+ * Clear the previous value before calling dlsym(),
+ * to ensure we can tell if our call resulted in an error.
+ */
+ (void)dlerror();
+ #endif
ptr = dlsym((void*)handle, name);
- if (!ptr) {
- PyErr_SetString(PyExc_OSError,
- dlerror());
+ if (ptr) {
+ return PyLong_FromVoidPtr(ptr);
+ }
+ #ifdef USE_DLERROR
+ const char *errmsg = dlerror();
+ if (errmsg) {
+ _PyErr_SetLocaleString(PyExc_OSError, errmsg);
return NULL;
}
- return PyLong_FromVoidPtr(ptr);
+ #endif
+ #undef USE_DLERROR
+ PyErr_Format(PyExc_OSError, "symbol '%s' not found", name);
+ return NULL;
}
#endif
diff --git a/contrib/tools/python3/Modules/_hashopenssl.c b/contrib/tools/python3/Modules/_hashopenssl.c
index 2998820953..3cc7d6f50e 100644
--- a/contrib/tools/python3/Modules/_hashopenssl.c
+++ b/contrib/tools/python3/Modules/_hashopenssl.c
@@ -320,6 +320,7 @@ _setException(PyObject *exc, const char* altmsg, ...)
va_end(vargs);
ERR_clear_error();
+ /* ERR_ERROR_STRING(3) ensures that the messages below are ASCII */
lib = ERR_lib_error_string(errcode);
func = ERR_func_error_string(errcode);
reason = ERR_reason_error_string(errcode);
diff --git a/contrib/tools/python3/Modules/_multiprocessing/semaphore.c b/contrib/tools/python3/Modules/_multiprocessing/semaphore.c
index c7df82dfe2..bc718e273a 100644
--- a/contrib/tools/python3/Modules/_multiprocessing/semaphore.c
+++ b/contrib/tools/python3/Modules/_multiprocessing/semaphore.c
@@ -23,6 +23,8 @@ typedef struct {
char *name;
} SemLockObject;
+#define _SemLockObject_CAST(op) ((SemLockObject *)(op))
+
/*[python input]
class SEM_HANDLE_converter(CConverter):
type = "SEM_HANDLE"
@@ -567,8 +569,9 @@ _multiprocessing_SemLock__rebuild_impl(PyTypeObject *type, SEM_HANDLE handle,
}
static void
-semlock_dealloc(SemLockObject* self)
+semlock_dealloc(PyObject *op)
{
+ SemLockObject *self = _SemLockObject_CAST(op);
PyTypeObject *tp = Py_TYPE(self);
PyObject_GC_UnTrack(self);
if (self->handle != SEM_FAILED)
@@ -706,7 +709,7 @@ _multiprocessing_SemLock___exit___impl(SemLockObject *self,
}
static int
-semlock_traverse(SemLockObject *s, visitproc visit, void *arg)
+semlock_traverse(PyObject *s, visitproc visit, void *arg)
{
Py_VISIT(Py_TYPE(s));
return 0;
diff --git a/contrib/tools/python3/Modules/_sqlite/connection.c b/contrib/tools/python3/Modules/_sqlite/connection.c
index 12e5c135aa..1450037ca9 100644
--- a/contrib/tools/python3/Modules/_sqlite/connection.c
+++ b/contrib/tools/python3/Modules/_sqlite/connection.c
@@ -927,6 +927,11 @@ step_callback(sqlite3_context *context, int argc, sqlite3_value **params)
assert(ctx != NULL);
aggregate_instance = (PyObject**)sqlite3_aggregate_context(context, sizeof(PyObject*));
+ if (aggregate_instance == NULL) {
+ (void)PyErr_NoMemory();
+ set_sqlite_error(context, "unable to allocate SQLite aggregate context");
+ goto error;
+ }
if (*aggregate_instance == NULL) {
*aggregate_instance = PyObject_CallNoArgs(ctx->callable);
if (!*aggregate_instance) {
diff --git a/contrib/tools/python3/Modules/_sqlite/util.c b/contrib/tools/python3/Modules/_sqlite/util.c
index 2b3bbfefa3..c521fc5ad6 100644
--- a/contrib/tools/python3/Modules/_sqlite/util.c
+++ b/contrib/tools/python3/Modules/_sqlite/util.c
@@ -129,6 +129,7 @@ _pysqlite_seterror(pysqlite_state *state, sqlite3 *db)
/* Create and set the exception. */
int extended_errcode = sqlite3_extended_errcode(db);
+ // sqlite3_errmsg() always returns an UTF-8 encoded message
const char *errmsg = sqlite3_errmsg(db);
raise_exception(exc_class, extended_errcode, errmsg);
return extended_errcode;
diff --git a/contrib/tools/python3/Modules/_sqlite/ya.make b/contrib/tools/python3/Modules/_sqlite/ya.make
index bbafbaff85..7abcc1886a 100644
--- a/contrib/tools/python3/Modules/_sqlite/ya.make
+++ b/contrib/tools/python3/Modules/_sqlite/ya.make
@@ -2,9 +2,9 @@
PY3_LIBRARY()
-VERSION(3.12.8)
+VERSION(3.12.9)
-ORIGINAL_SOURCE(https://github.com/python/cpython/archive/v3.12.8.tar.gz)
+ORIGINAL_SOURCE(https://github.com/python/cpython/archive/v3.12.9.tar.gz)
LICENSE(Python-2.0)
diff --git a/contrib/tools/python3/Modules/_ssl.c b/contrib/tools/python3/Modules/_ssl.c
index dbb4e8da78..498c538c6b 100644
--- a/contrib/tools/python3/Modules/_ssl.c
+++ b/contrib/tools/python3/Modules/_ssl.c
@@ -651,6 +651,11 @@ PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno)
ERR_GET_REASON(e) == SSL_R_CERTIFICATE_VERIFY_FAILED) {
type = state->PySSLCertVerificationErrorObject;
}
+ if (ERR_GET_LIB(e) == ERR_LIB_SYS) {
+ // A system error is being reported; reason is set to errno
+ errno = ERR_GET_REASON(e);
+ return PyErr_SetFromErrno(PyExc_OSError);
+ }
p = PY_SSL_ERROR_SYSCALL;
}
break;
@@ -676,6 +681,11 @@ PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno)
errstr = "EOF occurred in violation of protocol";
}
#endif
+ if (ERR_GET_LIB(e) == ERR_LIB_SYS) {
+ // A system error is being reported; reason is set to errno
+ errno = ERR_GET_REASON(e);
+ return PyErr_SetFromErrno(PyExc_OSError);
+ }
break;
}
default:
diff --git a/contrib/tools/python3/Modules/_tracemalloc.c b/contrib/tools/python3/Modules/_tracemalloc.c
index f3f4af9aba..3a4092c9d0 100644
--- a/contrib/tools/python3/Modules/_tracemalloc.c
+++ b/contrib/tools/python3/Modules/_tracemalloc.c
@@ -219,10 +219,5 @@ PyInit__tracemalloc(void)
if (m == NULL)
return NULL;
- if (_PyTraceMalloc_Init() < 0) {
- Py_DECREF(m);
- return NULL;
- }
-
return m;
}
diff --git a/contrib/tools/python3/Modules/_winapi.c b/contrib/tools/python3/Modules/_winapi.c
index 76f18c71a0..ac3ee113ff 100644
--- a/contrib/tools/python3/Modules/_winapi.c
+++ b/contrib/tools/python3/Modules/_winapi.c
@@ -946,7 +946,7 @@ getenvironment(PyObject* environment)
}
normalized_environment = normalize_environment(environment);
- if (normalize_environment == NULL) {
+ if (normalized_environment == NULL) {
return NULL;
}
diff --git a/contrib/tools/python3/Modules/arraymodule.c b/contrib/tools/python3/Modules/arraymodule.c
index d24c5989af..090a7b841c 100644
--- a/contrib/tools/python3/Modules/arraymodule.c
+++ b/contrib/tools/python3/Modules/arraymodule.c
@@ -2966,11 +2966,16 @@ array_arrayiterator___setstate__(arrayiterobject *self, PyObject *state)
Py_ssize_t index = PyLong_AsSsize_t(state);
if (index == -1 && PyErr_Occurred())
return NULL;
- if (index < 0)
- index = 0;
- else if (index > Py_SIZE(self->ao))
- index = Py_SIZE(self->ao); /* iterator exhausted */
- self->index = index;
+ arrayobject *ao = self->ao;
+ if (ao != NULL) {
+ if (index < 0) {
+ index = 0;
+ }
+ else if (index > Py_SIZE(ao)) {
+ index = Py_SIZE(ao); /* iterator exhausted */
+ }
+ self->index = index;
+ }
Py_RETURN_NONE;
}
diff --git a/contrib/tools/python3/Modules/clinic/posixmodule.c.h b/contrib/tools/python3/Modules/clinic/posixmodule.c.h
index a33461dc56..57121e1e89 100644
--- a/contrib/tools/python3/Modules/clinic/posixmodule.c.h
+++ b/contrib/tools/python3/Modules/clinic/posixmodule.c.h
@@ -304,7 +304,7 @@ exit:
return return_value;
}
-#if defined(HAVE_TTYNAME)
+#if defined(HAVE_TTYNAME_R)
PyDoc_STRVAR(os_ttyname__doc__,
"ttyname($module, fd, /)\n"
@@ -337,7 +337,7 @@ exit:
return return_value;
}
-#endif /* defined(HAVE_TTYNAME) */
+#endif /* defined(HAVE_TTYNAME_R) */
#if defined(HAVE_CTERMID)
@@ -12032,4 +12032,4 @@ exit:
#ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF
#define OS_WAITSTATUS_TO_EXITCODE_METHODDEF
#endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */
-/*[clinic end generated code: output=6d34c4564aca7725 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=efe45f93561db8d3 input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Modules/main.c b/contrib/tools/python3/Modules/main.c
index 5c60878626..7ed7c440e7 100644
--- a/contrib/tools/python3/Modules/main.c
+++ b/contrib/tools/python3/Modules/main.c
@@ -326,6 +326,7 @@ pymain_run_file_obj(PyObject *program_name, PyObject *filename,
if (fp == NULL) {
// Ignore the OSError
PyErr_Clear();
+ // TODO(picnixz): strerror() is locale dependent but not PySys_FormatStderr().
PySys_FormatStderr("%S: can't open file %R: [Errno %d] %s\n",
program_name, filename, errno, strerror(errno));
return 2;
diff --git a/contrib/tools/python3/Modules/posixmodule.c b/contrib/tools/python3/Modules/posixmodule.c
index ce2c80e6da..3707516550 100644
--- a/contrib/tools/python3/Modules/posixmodule.c
+++ b/contrib/tools/python3/Modules/posixmodule.c
@@ -49,10 +49,6 @@
# include "winreparse.h"
#endif
-#if !defined(EX_OK) && defined(EXIT_SUCCESS)
-# define EX_OK EXIT_SUCCESS
-#endif
-
/* On android API level 21, 'AT_EACCESS' is not declared although
* HAVE_FACCESSAT is defined. */
#ifdef __ANDROID__
@@ -61,6 +57,9 @@
#include <stdio.h> // ctermid()
#include <stdlib.h> // system()
+#ifdef HAVE_SYS_PIDFD_H
+# error #include <sys/pidfd.h> // PIDFD_NONBLOCK
+#endif
/*
* A number of APIs are available on macOS from a certain macOS version.
@@ -267,6 +266,10 @@ corresponding Unix manual entries for more information on calls.");
# include <sysexits.h>
#endif
+#if !defined(EX_OK) && defined(EXIT_SUCCESS)
+# define EX_OK EXIT_SUCCESS
+#endif
+
#ifdef HAVE_SYS_LOADAVG_H
# include <sys/loadavg.h>
#endif
@@ -3235,7 +3238,7 @@ os_access_impl(PyObject *module, path_t *path, int mode, int dir_fd,
#endif
-#ifdef HAVE_TTYNAME
+#ifdef HAVE_TTYNAME_R
/*[clinic input]
os.ttyname
@@ -8982,42 +8985,33 @@ os_kill_impl(PyObject *module, pid_t pid, Py_ssize_t signal)
Py_RETURN_NONE;
#else /* !MS_WINDOWS */
- PyObject *result;
DWORD sig = (DWORD)signal;
- DWORD err;
- HANDLE handle;
#ifdef HAVE_WINDOWS_CONSOLE_IO
/* Console processes which share a common console can be sent CTRL+C or
CTRL+BREAK events, provided they handle said events. */
if (sig == CTRL_C_EVENT || sig == CTRL_BREAK_EVENT) {
if (GenerateConsoleCtrlEvent(sig, (DWORD)pid) == 0) {
- err = GetLastError();
- PyErr_SetFromWindowsErr(err);
- }
- else {
- Py_RETURN_NONE;
+ return PyErr_SetFromWindowsErr(0);
}
+ Py_RETURN_NONE;
}
#endif /* HAVE_WINDOWS_CONSOLE_IO */
/* If the signal is outside of what GenerateConsoleCtrlEvent can use,
attempt to open and terminate the process. */
- handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid);
+ HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid);
if (handle == NULL) {
- err = GetLastError();
- return PyErr_SetFromWindowsErr(err);
+ return PyErr_SetFromWindowsErr(0);
}
- if (TerminateProcess(handle, sig) == 0) {
- err = GetLastError();
- result = PyErr_SetFromWindowsErr(err);
- } else {
- result = Py_NewRef(Py_None);
+ BOOL res = TerminateProcess(handle, sig);
+ CloseHandle(handle);
+ if (res == 0) {
+ return PyErr_SetFromWindowsErr(0);
}
- CloseHandle(handle);
- return result;
+ Py_RETURN_NONE;
#endif /* !MS_WINDOWS */
}
#endif /* HAVE_KILL */
diff --git a/contrib/tools/python3/Modules/pyexpat.c b/contrib/tools/python3/Modules/pyexpat.c
index be31c637fc..b354a86e7f 100644
--- a/contrib/tools/python3/Modules/pyexpat.c
+++ b/contrib/tools/python3/Modules/pyexpat.c
@@ -1784,7 +1784,12 @@ add_error(PyObject *errors_module, PyObject *codes_dict,
* with the other uses of the XML_ErrorString function
* elsewhere within this file. pyexpat's copy of the messages
* only acts as a fallback in case of outdated runtime libexpat,
- * where it returns NULL. */
+ * where it returns NULL.
+ *
+ * In addition, XML_ErrorString is assumed to return UTF-8 encoded
+ * strings (in conv_string_to_unicode, we decode them using 'strict'
+ * error handling).
+ */
const char *error_string = XML_ErrorString(error_code);
if (error_string == NULL) {
error_string = error_info_of[error_index].description;
diff --git a/contrib/tools/python3/Modules/syslogmodule.c b/contrib/tools/python3/Modules/syslogmodule.c
index 6db8de9c49..c925a42dc0 100644
--- a/contrib/tools/python3/Modules/syslogmodule.c
+++ b/contrib/tools/python3/Modules/syslogmodule.c
@@ -170,7 +170,7 @@ syslog_openlog_impl(PyObject *module, PyObject *ident, long logopt,
}
}
if (PySys_Audit("syslog.openlog", "Oll", ident ? ident : Py_None, logopt, facility) < 0) {
- Py_DECREF(ident);
+ Py_XDECREF(ident);
return NULL;
}
@@ -250,7 +250,7 @@ syslog_closelog_impl(PyObject *module)
// Since the sys.closelog changes the process level state of syslog library,
// this operation is only allowed for the main interpreter.
if (!is_main_interpreter()) {
- PyErr_SetString(PyExc_RuntimeError, "sunbinterpreter can't use syslog.closelog()");
+ PyErr_SetString(PyExc_RuntimeError, "subinterpreter can't use syslog.closelog()");
return NULL;
}
diff --git a/contrib/tools/python3/Objects/dictobject.c b/contrib/tools/python3/Objects/dictobject.c
index 4e96531494..7337e290e8 100644
--- a/contrib/tools/python3/Objects/dictobject.c
+++ b/contrib/tools/python3/Objects/dictobject.c
@@ -458,11 +458,14 @@ estimate_log2_keysize(Py_ssize_t n)
/* This immutable, empty PyDictKeysObject is used for PyDict_Clear()
* (which cannot fail and thus can do no allocation).
+ *
+ * See https://github.com/python/cpython/pull/127568#discussion_r1868070614
+ * for the rationale of using dk_log2_index_bytes=3 instead of 0.
*/
static PyDictKeysObject empty_keys_struct = {
_Py_IMMORTAL_REFCNT, /* dk_refcnt */
0, /* dk_log2_size */
- 0, /* dk_log2_index_bytes */
+ 3, /* dk_log2_index_bytes */
DICT_KEYS_UNICODE, /* dk_kind */
1, /* dk_version */
0, /* dk_usable (immutable) */
diff --git a/contrib/tools/python3/Objects/genobject.c b/contrib/tools/python3/Objects/genobject.c
index 474abe1094..640a7d906c 100644
--- a/contrib/tools/python3/Objects/genobject.c
+++ b/contrib/tools/python3/Objects/genobject.c
@@ -618,30 +618,19 @@ gen_iternext(PyGenObject *gen)
int
_PyGen_SetStopIterationValue(PyObject *value)
{
- PyObject *e;
-
- if (value == NULL ||
- (!PyTuple_Check(value) && !PyExceptionInstance_Check(value)))
- {
- /* Delay exception instantiation if we can */
- PyErr_SetObject(PyExc_StopIteration, value);
- return 0;
- }
- /* Construct an exception instance manually with
- * PyObject_CallOneArg and pass it to PyErr_SetObject.
- *
- * We do this to handle a situation when "value" is a tuple, in which
- * case PyErr_SetObject would set the value of StopIteration to
- * the first element of the tuple.
- *
- * (See PyErr_SetObject/_PyErr_CreateException code for details.)
- */
- e = PyObject_CallOneArg(PyExc_StopIteration, value);
- if (e == NULL) {
+ assert(!PyErr_Occurred());
+ // Construct an exception instance manually with PyObject_CallOneArg()
+ // but use PyErr_SetRaisedException() instead of PyErr_SetObject() as
+ // PyErr_SetObject(exc_type, value) has a fast path when 'value'
+ // is a tuple, where the value of the StopIteration exception would be
+ // set to 'value[0]' instead of 'value'.
+ PyObject *exc = value == NULL
+ ? PyObject_CallNoArgs(PyExc_StopIteration)
+ : PyObject_CallOneArg(PyExc_StopIteration, value);
+ if (exc == NULL) {
return -1;
}
- PyErr_SetObject(PyExc_StopIteration, e);
- Py_DECREF(e);
+ PyErr_SetRaisedException(exc /* stolen */);
return 0;
}
diff --git a/contrib/tools/python3/Objects/iterobject.c b/contrib/tools/python3/Objects/iterobject.c
index 7cb17a6ca4..66e4490766 100644
--- a/contrib/tools/python3/Objects/iterobject.c
+++ b/contrib/tools/python3/Objects/iterobject.c
@@ -382,6 +382,7 @@ anextawaitable_iternext(anextawaitableobject *obj)
return result;
}
if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration)) {
+ PyErr_Clear();
_PyGen_SetStopIterationValue(obj->default_value);
}
return NULL;
@@ -405,6 +406,7 @@ anextawaitable_proxy(anextawaitableobject *obj, char *meth, PyObject *arg) {
* exception we replace it with a `StopIteration(default)`, as if
* it was the return value of `__anext__()` coroutine.
*/
+ PyErr_Clear();
_PyGen_SetStopIterationValue(obj->default_value);
}
return NULL;
diff --git a/contrib/tools/python3/Objects/namespaceobject.c b/contrib/tools/python3/Objects/namespaceobject.c
index 2cc4ddd3c9..7082c6fd46 100644
--- a/contrib/tools/python3/Objects/namespaceobject.c
+++ b/contrib/tools/python3/Objects/namespaceobject.c
@@ -121,6 +121,10 @@ namespace_repr(PyObject *ns)
goto error;
}
+ if (PyErr_Occurred()) {
+ goto error;
+ }
+
separator = PyUnicode_FromString(", ");
if (separator == NULL)
goto error;
diff --git a/contrib/tools/python3/Objects/typeobject.c b/contrib/tools/python3/Objects/typeobject.c
index 46d7a4b973..012920fcf8 100644
--- a/contrib/tools/python3/Objects/typeobject.c
+++ b/contrib/tools/python3/Objects/typeobject.c
@@ -2319,7 +2319,7 @@ vectorcall_maybe(PyThreadState *tstate, PyObject *name,
*/
static int
-tail_contains(PyObject *tuple, int whence, PyObject *o)
+tail_contains(PyObject *tuple, Py_ssize_t whence, PyObject *o)
{
Py_ssize_t j, size;
size = PyTuple_GET_SIZE(tuple);
@@ -2382,7 +2382,7 @@ check_duplicates(PyObject *tuple)
*/
static void
-set_mro_error(PyObject **to_merge, Py_ssize_t to_merge_size, int *remain)
+set_mro_error(PyObject **to_merge, Py_ssize_t to_merge_size, Py_ssize_t *remain)
{
Py_ssize_t i, n, off;
char buf[1000];
@@ -2437,13 +2437,13 @@ pmerge(PyObject *acc, PyObject **to_merge, Py_ssize_t to_merge_size)
{
int res = 0;
Py_ssize_t i, j, empty_cnt;
- int *remain;
+ Py_ssize_t *remain;
/* remain stores an index into each sublist of to_merge.
remain[i] is the index of the next base in to_merge[i]
that is not included in acc.
*/
- remain = PyMem_New(int, to_merge_size);
+ remain = PyMem_New(Py_ssize_t, to_merge_size);
if (remain == NULL) {
PyErr_NoMemory();
return -1;
@@ -6473,8 +6473,11 @@ object___sizeof___impl(PyObject *self)
res = 0;
isize = Py_TYPE(self)->tp_itemsize;
- if (isize > 0)
- res = Py_SIZE(self) * isize;
+ if (isize > 0) {
+ /* This assumes that ob_size is valid if tp_itemsize is not 0,
+ which isn't true for PyLongObject. */
+ res = _PyVarObject_CAST(self)->ob_size * isize;
+ }
res += Py_TYPE(self)->tp_basicsize;
return PyLong_FromSsize_t(res);
diff --git a/contrib/tools/python3/Objects/unicodeobject.c b/contrib/tools/python3/Objects/unicodeobject.c
index 8fe275d4c8..8c25866640 100644
--- a/contrib/tools/python3/Objects/unicodeobject.c
+++ b/contrib/tools/python3/Objects/unicodeobject.c
@@ -1472,10 +1472,13 @@ _copy_characters(PyObject *to, Py_ssize_t to_start,
assert(PyUnicode_Check(from));
assert(from_start + how_many <= PyUnicode_GET_LENGTH(from));
- if (how_many == 0)
+ assert(to == NULL || PyUnicode_Check(to));
+
+ if (how_many == 0) {
return 0;
+ }
- assert(PyUnicode_Check(to));
+ assert(to != NULL);
assert(to_start + how_many <= PyUnicode_GET_LENGTH(to));
from_kind = PyUnicode_KIND(from);
diff --git a/contrib/tools/python3/Parser/action_helpers.c b/contrib/tools/python3/Parser/action_helpers.c
index be52d89495..e029ae692f 100644
--- a/contrib/tools/python3/Parser/action_helpers.c
+++ b/contrib/tools/python3/Parser/action_helpers.c
@@ -909,8 +909,6 @@ _PyPegen_check_fstring_conversion(Parser *p, Token *conv_token, expr_ty conv) {
return result_token_with_metadata(p, conv, conv_token->metadata);
}
-static asdl_expr_seq *
-unpack_top_level_joined_strs(Parser *p, asdl_expr_seq *raw_expressions);
ResultTokenWithMetadata *
_PyPegen_setup_full_format_spec(Parser *p, Token *colon, asdl_expr_seq *spec,
int lineno, int col_offset, int end_lineno,
@@ -1192,8 +1190,9 @@ static expr_ty _PyPegen_decode_fstring_part(Parser *p, int is_raw,
p->arena);
}
-static asdl_expr_seq *
-unpack_top_level_joined_strs(Parser *p, asdl_expr_seq *raw_expressions) {
+expr_ty
+_PyPegen_joined_str(Parser *p, Token* a, asdl_expr_seq* expr, Token*b) {
+
/* The parser might put multiple f-string values into an individual
* JoinedStr node at the top level due to stuff like f-string debugging
* expressions. This function flattens those and promotes them to the
@@ -1201,44 +1200,14 @@ unpack_top_level_joined_strs(Parser *p, asdl_expr_seq *raw_expressions) {
* of the regular output, so this is not necessary if you are not going
* to expose the output AST to Python level. */
- Py_ssize_t i, req_size, raw_size;
-
- req_size = raw_size = asdl_seq_LEN(raw_expressions);
- expr_ty expr;
- for (i = 0; i < raw_size; i++) {
- expr = asdl_seq_GET(raw_expressions, i);
- if (expr->kind == JoinedStr_kind) {
- req_size += asdl_seq_LEN(expr->v.JoinedStr.values) - 1;
- }
- }
-
- asdl_expr_seq *expressions = _Py_asdl_expr_seq_new(req_size, p->arena);
- if (expressions == NULL) {
- return NULL;
- }
-
- Py_ssize_t raw_index, req_index = 0;
- for (raw_index = 0; raw_index < raw_size; raw_index++) {
- expr = asdl_seq_GET(raw_expressions, raw_index);
- if (expr->kind == JoinedStr_kind) {
- asdl_expr_seq *values = expr->v.JoinedStr.values;
- for (Py_ssize_t n = 0; n < asdl_seq_LEN(values); n++) {
- asdl_seq_SET(expressions, req_index, asdl_seq_GET(values, n));
- req_index++;
+ Py_ssize_t n_items = asdl_seq_LEN(expr);
+ Py_ssize_t total_items = n_items;
+ for (Py_ssize_t i = 0; i < n_items; i++) {
+ expr_ty item = asdl_seq_GET(expr, i);
+ if (item->kind == JoinedStr_kind) {
+ total_items += asdl_seq_LEN(item->v.JoinedStr.values) - 1;
}
- } else {
- asdl_seq_SET(expressions, req_index, expr);
- req_index++;
- }
}
- return expressions;
-}
-
-expr_ty _PyPegen_joined_str(Parser *p, Token *a, asdl_expr_seq *raw_expressions,
- Token *b) {
-
- asdl_expr_seq *expr = unpack_top_level_joined_strs(p, raw_expressions);
- Py_ssize_t n_items = asdl_seq_LEN(expr);
const char *quote_str = PyBytes_AsString(a->bytes);
if (quote_str == NULL) {
@@ -1246,7 +1215,7 @@ expr_ty _PyPegen_joined_str(Parser *p, Token *a, asdl_expr_seq *raw_expressions,
}
int is_raw = strpbrk(quote_str, "rR") != NULL;
- asdl_expr_seq *seq = _Py_asdl_expr_seq_new(n_items, p->arena);
+ asdl_expr_seq *seq = _Py_asdl_expr_seq_new(total_items, p->arena);
if (seq == NULL) {
return NULL;
}
@@ -1254,6 +1223,29 @@ expr_ty _PyPegen_joined_str(Parser *p, Token *a, asdl_expr_seq *raw_expressions,
Py_ssize_t index = 0;
for (Py_ssize_t i = 0; i < n_items; i++) {
expr_ty item = asdl_seq_GET(expr, i);
+ // This should correspond to a JoinedStr node of two elements
+ // created _PyPegen_formatted_value. This situation can only be the result of
+ // a f-string debug expression where the first element is a constant with the text and the second
+ // a formatted value with the expression.
+ if (item->kind == JoinedStr_kind) {
+ asdl_expr_seq *values = item->v.JoinedStr.values;
+ if (asdl_seq_LEN(values) != 2) {
+ PyErr_Format(PyExc_SystemError,
+ "unexpected JoinedStr node without debug data in f-string at line %d",
+ item->lineno);
+ return NULL;
+ }
+
+ expr_ty first = asdl_seq_GET(values, 0);
+ assert(first->kind == Constant_kind);
+ asdl_seq_SET(seq, index++, first);
+
+ expr_ty second = asdl_seq_GET(values, 1);
+ assert(second->kind == FormattedValue_kind);
+ asdl_seq_SET(seq, index++, second);
+
+ continue;
+ }
if (item->kind == Constant_kind) {
item = _PyPegen_decode_fstring_part(p, is_raw, item, b);
if (item == NULL) {
@@ -1272,7 +1264,7 @@ expr_ty _PyPegen_joined_str(Parser *p, Token *a, asdl_expr_seq *raw_expressions,
}
asdl_expr_seq *resized_exprs;
- if (index != n_items) {
+ if (index != total_items) {
resized_exprs = _Py_asdl_expr_seq_new(index, p->arena);
if (resized_exprs == NULL) {
return NULL;
diff --git a/contrib/tools/python3/Parser/tokenizer.c b/contrib/tools/python3/Parser/tokenizer.c
index 106307e775..f35cd94d6d 100644
--- a/contrib/tools/python3/Parser/tokenizer.c
+++ b/contrib/tools/python3/Parser/tokenizer.c
@@ -480,9 +480,7 @@ static int update_fstring_expr(struct tok_state *tok, char cur) {
case '}':
case '!':
case ':':
- if (tok_mode->last_expr_end == -1) {
- tok_mode->last_expr_end = strlen(tok->start);
- }
+ tok_mode->last_expr_end = strlen(tok->start);
break;
default:
Py_UNREACHABLE();
diff --git a/contrib/tools/python3/Python/ceval.c b/contrib/tools/python3/Python/ceval.c
index 6110883ca0..3985b52649 100644
--- a/contrib/tools/python3/Python/ceval.c
+++ b/contrib/tools/python3/Python/ceval.c
@@ -20,6 +20,7 @@
#include "pycore_range.h" // _PyRangeIterObject
#include "pycore_sliceobject.h" // _PyBuildSlice_ConsumeRefs
#include "pycore_sysmodule.h" // _PySys_Audit()
+#include "pycore_traceback.h" // _PyTraceBack_FromFrame
#include "pycore_tuple.h" // _PyTuple_ITEMS()
#include "pycore_typeobject.h" // _PySuper_Lookup()
#include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS
@@ -544,6 +545,7 @@ fail:
static int do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause);
static int exception_group_match(
+ _PyInterpreterFrame *frame,
PyObject* exc_value, PyObject *match_type,
PyObject **match, PyObject **rest);
@@ -1856,7 +1858,7 @@ raise_error:
*/
static int
-exception_group_match(PyObject* exc_value, PyObject *match_type,
+exception_group_match(_PyInterpreterFrame *frame, PyObject* exc_value, PyObject *match_type,
PyObject **match, PyObject **rest)
{
if (Py_IsNone(exc_value)) {
@@ -1883,6 +1885,15 @@ exception_group_match(PyObject* exc_value, PyObject *match_type,
if (wrapped == NULL) {
return -1;
}
+ PyFrameObject *f = _PyFrame_GetFrameObject(frame);
+ if (f != NULL) {
+ PyObject *tb = _PyTraceBack_FromFrame(NULL, f);
+ if (tb == NULL) {
+ return -1;
+ }
+ PyException_SetTraceback(wrapped, tb);
+ Py_DECREF(tb);
+ }
*match = wrapped;
}
*rest = Py_NewRef(Py_None);
@@ -1898,8 +1909,25 @@ exception_group_match(PyObject* exc_value, PyObject *match_type,
if (pair == NULL) {
return -1;
}
- assert(PyTuple_CheckExact(pair));
- assert(PyTuple_GET_SIZE(pair) == 2);
+
+ if (!PyTuple_CheckExact(pair)) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s.split must return a tuple, not %.200s",
+ Py_TYPE(exc_value)->tp_name, Py_TYPE(pair)->tp_name);
+ Py_DECREF(pair);
+ return -1;
+ }
+
+ // allow tuples of length > 2 for backwards compatibility
+ if (PyTuple_GET_SIZE(pair) < 2) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s.split must return a 2-tuple, "
+ "got tuple of size %zd",
+ Py_TYPE(exc_value)->tp_name, PyTuple_GET_SIZE(pair));
+ Py_DECREF(pair);
+ return -1;
+ }
+
*match = Py_NewRef(PyTuple_GET_ITEM(pair, 0));
*rest = Py_NewRef(PyTuple_GET_ITEM(pair, 1));
Py_DECREF(pair);
diff --git a/contrib/tools/python3/Python/errors.c b/contrib/tools/python3/Python/errors.c
index 7a16841eba..e4b7fd6b24 100644
--- a/contrib/tools/python3/Python/errors.c
+++ b/contrib/tools/python3/Python/errors.c
@@ -305,6 +305,15 @@ PyErr_SetString(PyObject *exception, const char *string)
_PyErr_SetString(tstate, exception, string);
}
+void
+_PyErr_SetLocaleString(PyObject *exception, const char *string)
+{
+ PyObject *value = PyUnicode_DecodeLocale(string, "surrogateescape");
+ if (value != NULL) {
+ PyErr_SetObject(exception, value);
+ Py_DECREF(value);
+ }
+}
PyObject* _Py_HOT_FUNCTION
PyErr_Occurred(void)
diff --git a/contrib/tools/python3/Python/generated_cases.c.h b/contrib/tools/python3/Python/generated_cases.c.h
index bbaf589e2e..246d37e771 100644
--- a/contrib/tools/python3/Python/generated_cases.c.h
+++ b/contrib/tools/python3/Python/generated_cases.c.h
@@ -2952,7 +2952,7 @@
match = NULL;
rest = NULL;
- int res = exception_group_match(exc_value, match_type,
+ int res = exception_group_match(frame, exc_value, match_type,
&match, &rest);
#line 2958 "Python/generated_cases.c.h"
Py_DECREF(exc_value);
diff --git a/contrib/tools/python3/Python/pylifecycle.c b/contrib/tools/python3/Python/pylifecycle.c
index e9c1a0d72d..ef4e607657 100644
--- a/contrib/tools/python3/Python/pylifecycle.c
+++ b/contrib/tools/python3/Python/pylifecycle.c
@@ -654,6 +654,11 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
// didn't depend on interp->feature_flags being set already.
_PyObject_InitState(interp);
+ status = _PyTraceMalloc_Init();
+ if (_PyStatus_EXCEPTION(status)) {
+ return status;
+ }
+
PyThreadState *tstate = _PyThreadState_New(interp);
if (tstate == NULL) {
return _PyStatus_ERR("can't make first thread");
@@ -1928,7 +1933,7 @@ Py_FinalizeEx(void)
/* Disable tracemalloc after all Python objects have been destroyed,
so it is possible to use tracemalloc in objects destructor. */
- _PyTraceMalloc_Fini();
+ _PyTraceMalloc_Stop();
/* Finalize any remaining import state */
// XXX Move these up to where finalize_modules() is currently.
@@ -1981,6 +1986,8 @@ Py_FinalizeEx(void)
finalize_interp_clear(tstate);
+ _PyTraceMalloc_Fini();
+
#ifdef WITH_PYMALLOC
if (malloc_stats) {
_PyObject_DebugMallocStats(stderr);
diff --git a/contrib/tools/python3/Python/pythonrun.c b/contrib/tools/python3/Python/pythonrun.c
index 5f3d249df4..cf84573a8e 100644
--- a/contrib/tools/python3/Python/pythonrun.c
+++ b/contrib/tools/python3/Python/pythonrun.c
@@ -538,43 +538,37 @@ parse_syntax_error(PyObject *err, PyObject **message, PyObject **filename,
*offset = hold;
}
- if (Py_TYPE(err) == (PyTypeObject*)PyExc_SyntaxError) {
- v = PyObject_GetAttr(err, &_Py_ID(end_lineno));
- if (!v) {
- PyErr_Clear();
- *end_lineno = *lineno;
- }
- else if (v == Py_None) {
- *end_lineno = *lineno;
- Py_DECREF(v);
- } else {
- hold = PyLong_AsSsize_t(v);
- Py_DECREF(v);
- if (hold < 0 && PyErr_Occurred())
- goto finally;
- *end_lineno = hold;
- }
-
- v = PyObject_GetAttr(err, &_Py_ID(end_offset));
- if (!v) {
- PyErr_Clear();
- *end_offset = -1;
- }
- else if (v == Py_None) {
- *end_offset = -1;
- Py_DECREF(v);
- } else {
- hold = PyLong_AsSsize_t(v);
- Py_DECREF(v);
- if (hold < 0 && PyErr_Occurred())
- goto finally;
- *end_offset = hold;
- }
- } else {
- // SyntaxError subclasses
+ v = PyObject_GetAttr(err, &_Py_ID(end_lineno));
+ if (!v) {
+ PyErr_Clear();
*end_lineno = *lineno;
+ }
+ else if (v == Py_None) {
+ *end_lineno = *lineno;
+ Py_DECREF(v);
+ } else {
+ hold = PyLong_AsSsize_t(v);
+ Py_DECREF(v);
+ if (hold < 0 && PyErr_Occurred())
+ goto finally;
+ *end_lineno = hold;
+ }
+
+ v = PyObject_GetAttr(err, &_Py_ID(end_offset));
+ if (!v) {
+ PyErr_Clear();
*end_offset = -1;
}
+ else if (v == Py_None) {
+ *end_offset = -1;
+ Py_DECREF(v);
+ } else {
+ hold = PyLong_AsSsize_t(v);
+ Py_DECREF(v);
+ if (hold < 0 && PyErr_Occurred())
+ goto finally;
+ *end_offset = hold;
+ }
v = PyObject_GetAttr(err, &_Py_ID(text));
if (!v)
diff --git a/contrib/tools/python3/Python/sysmodule.c b/contrib/tools/python3/Python/sysmodule.c
index 7240f8e4e1..7813048be4 100644
--- a/contrib/tools/python3/Python/sysmodule.c
+++ b/contrib/tools/python3/Python/sysmodule.c
@@ -2653,6 +2653,7 @@ PySys_ResetWarnOptions(void)
static int
_PySys_AddWarnOptionWithError(PyThreadState *tstate, PyObject *option)
{
+ assert(tstate != NULL);
PyObject *warnoptions = get_warnoptions(tstate);
if (warnoptions == NULL) {
return -1;
@@ -2667,11 +2668,11 @@ void
PySys_AddWarnOptionUnicode(PyObject *option)
{
PyThreadState *tstate = _PyThreadState_GET();
+ _Py_EnsureTstateNotNULL(tstate);
+ assert(!_PyErr_Occurred(tstate));
if (_PySys_AddWarnOptionWithError(tstate, option) < 0) {
/* No return value, therefore clear error state if possible */
- if (tstate) {
- _PyErr_Clear(tstate);
- }
+ _PyErr_Clear(tstate);
}
}
diff --git a/contrib/tools/python3/Python/tracemalloc.c b/contrib/tools/python3/Python/tracemalloc.c
index e13064bd14..852e5b0391 100644
--- a/contrib/tools/python3/Python/tracemalloc.c
+++ b/contrib/tools/python3/Python/tracemalloc.c
@@ -2,6 +2,7 @@
#include "pycore_fileutils.h" // _Py_write_noraise()
#include "pycore_gc.h" // PyGC_Head
#include "pycore_hashtable.h" // _Py_hashtable_t
+#include "pycore_initconfig.h" // _PyStatus_NO_MEMORY()
#include "pycore_object.h" // _PyType_PreHeaderSize
#include "pycore_pymem.h" // _Py_tracemalloc_config
#include "pycore_runtime.h" // _Py_ID()
@@ -536,12 +537,16 @@ tracemalloc_alloc(int use_calloc, void *ctx, size_t nelem, size_t elsize)
return NULL;
TABLES_LOCK();
- if (ADD_TRACE(ptr, nelem * elsize) < 0) {
- /* Failed to allocate a trace for the new memory block */
- TABLES_UNLOCK();
- alloc->free(alloc->ctx, ptr);
- return NULL;
+
+ if (tracemalloc_config.tracing) {
+ if (ADD_TRACE(ptr, nelem * elsize) < 0) {
+ /* Failed to allocate a trace for the new memory block */
+ alloc->free(alloc->ctx, ptr);
+ ptr = NULL;
+ }
}
+ // else: gh-128679: tracemalloc.stop() was called by another thread
+
TABLES_UNLOCK();
return ptr;
}
@@ -557,11 +562,15 @@ tracemalloc_realloc(void *ctx, void *ptr, size_t new_size)
if (ptr2 == NULL)
return NULL;
+ TABLES_LOCK();
+ if (!tracemalloc_config.tracing) {
+ // gh-128679: tracemalloc.stop() was called by another thread
+ goto done;
+ }
+
if (ptr != NULL) {
/* an existing memory block has been resized */
- TABLES_LOCK();
-
/* tracemalloc_add_trace() updates the trace if there is already
a trace at address ptr2 */
if (ptr2 != ptr) {
@@ -580,20 +589,19 @@ tracemalloc_realloc(void *ctx, void *ptr, size_t new_size)
allocating memory. */
Py_FatalError("tracemalloc_realloc() failed to allocate a trace");
}
- TABLES_UNLOCK();
}
else {
/* new allocation */
- TABLES_LOCK();
if (ADD_TRACE(ptr2, new_size) < 0) {
/* Failed to allocate a trace for the new memory block */
- TABLES_UNLOCK();
alloc->free(alloc->ctx, ptr2);
- return NULL;
+ ptr2 = NULL;
}
- TABLES_UNLOCK();
}
+
+done:
+ TABLES_UNLOCK();
return ptr2;
}
@@ -612,7 +620,12 @@ tracemalloc_free(void *ctx, void *ptr)
alloc->free(alloc->ctx, ptr);
TABLES_LOCK();
- REMOVE_TRACE(ptr);
+
+ if (tracemalloc_config.tracing) {
+ REMOVE_TRACE(ptr);
+ }
+ // else: gh-128679: tracemalloc.stop() was called by another thread
+
TABLES_UNLOCK();
}
@@ -671,7 +684,9 @@ tracemalloc_realloc_gil(void *ctx, void *ptr, size_t new_size)
ptr2 = alloc->realloc(alloc->ctx, ptr, new_size);
if (ptr2 != NULL && ptr != NULL) {
TABLES_LOCK();
- REMOVE_TRACE(ptr);
+ if (tracemalloc_config.tracing) {
+ REMOVE_TRACE(ptr);
+ }
TABLES_UNLOCK();
}
return ptr2;
@@ -746,7 +761,9 @@ tracemalloc_raw_realloc(void *ctx, void *ptr, size_t new_size)
if (ptr2 != NULL && ptr != NULL) {
TABLES_LOCK();
- REMOVE_TRACE(ptr);
+ if (tracemalloc_config.tracing) {
+ REMOVE_TRACE(ptr);
+ }
TABLES_UNLOCK();
}
return ptr2;
@@ -777,46 +794,36 @@ tracemalloc_clear_filename(void *value)
/* reentrant flag must be set to call this function and GIL must be held */
static void
-tracemalloc_clear_traces(void)
+tracemalloc_clear_traces_unlocked(void)
{
+ set_reentrant(1);
+
/* The GIL protects variables against concurrent access */
assert(PyGILState_Check());
- TABLES_LOCK();
_Py_hashtable_clear(tracemalloc_traces);
_Py_hashtable_clear(tracemalloc_domains);
tracemalloc_traced_memory = 0;
tracemalloc_peak_traced_memory = 0;
- TABLES_UNLOCK();
_Py_hashtable_clear(tracemalloc_tracebacks);
_Py_hashtable_clear(tracemalloc_filenames);
+
+ set_reentrant(0);
}
-int
+PyStatus
_PyTraceMalloc_Init(void)
{
- if (tracemalloc_config.initialized == TRACEMALLOC_FINALIZED) {
- PyErr_SetString(PyExc_RuntimeError,
- "the tracemalloc module has been unloaded");
- return -1;
- }
-
- if (tracemalloc_config.initialized == TRACEMALLOC_INITIALIZED)
- return 0;
+ assert(tracemalloc_config.initialized == TRACEMALLOC_NOT_INITIALIZED);
PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw);
#ifdef REENTRANT_THREADLOCAL
if (PyThread_tss_create(&tracemalloc_reentrant_key) != 0) {
-#ifdef MS_WINDOWS
- PyErr_SetFromWindowsErr(0);
-#else
- PyErr_SetFromErrno(PyExc_OSError);
-#endif
- return -1;
+ return _PyStatus_NO_MEMORY();
}
#endif
@@ -824,8 +831,7 @@ _PyTraceMalloc_Init(void)
if (tables_lock == NULL) {
tables_lock = PyThread_allocate_lock();
if (tables_lock == NULL) {
- PyErr_SetString(PyExc_RuntimeError, "cannot allocate lock");
- return -1;
+ return _PyStatus_NO_MEMORY();
}
}
#endif
@@ -842,9 +848,9 @@ _PyTraceMalloc_Init(void)
tracemalloc_domains = tracemalloc_create_domains_table();
if (tracemalloc_filenames == NULL || tracemalloc_tracebacks == NULL
- || tracemalloc_traces == NULL || tracemalloc_domains == NULL) {
- PyErr_NoMemory();
- return -1;
+ || tracemalloc_traces == NULL || tracemalloc_domains == NULL)
+ {
+ return _PyStatus_NO_MEMORY();
}
tracemalloc_empty_traceback.nframe = 1;
@@ -855,7 +861,7 @@ _PyTraceMalloc_Init(void)
tracemalloc_empty_traceback.hash = traceback_hash(&tracemalloc_empty_traceback);
tracemalloc_config.initialized = TRACEMALLOC_INITIALIZED;
- return 0;
+ return _PyStatus_OK();
}
@@ -900,10 +906,6 @@ _PyTraceMalloc_Start(int max_nframe)
return -1;
}
- if (_PyTraceMalloc_Init() < 0) {
- return -1;
- }
-
if (tracemalloc_config.tracing) {
/* hook already installed: do nothing */
return 0;
@@ -954,8 +956,13 @@ _PyTraceMalloc_Start(int max_nframe)
void
_PyTraceMalloc_Stop(void)
{
- if (!tracemalloc_config.tracing)
- return;
+ // Lock to synchronize with tracemalloc_free() which checks
+ // 'tracing' while holding the lock.
+ TABLES_LOCK();
+
+ if (!tracemalloc_config.tracing) {
+ goto done;
+ }
/* stop tracing Python memory allocations */
tracemalloc_config.tracing = 0;
@@ -967,11 +974,14 @@ _PyTraceMalloc_Stop(void)
PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &allocators.mem);
PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &allocators.obj);
- tracemalloc_clear_traces();
+ tracemalloc_clear_traces_unlocked();
/* release memory */
raw_free(tracemalloc_traceback);
tracemalloc_traceback = NULL;
+
+done:
+ TABLES_UNLOCK();
}
@@ -1221,23 +1231,17 @@ tracemalloc_pyobject_decref(void *value)
static traceback_t*
-tracemalloc_get_traceback(unsigned int domain, uintptr_t ptr)
+tracemalloc_get_traceback_unlocked(unsigned int domain, uintptr_t ptr)
{
-
- if (!tracemalloc_config.tracing)
+ if (!tracemalloc_config.tracing) {
return NULL;
+ }
- trace_t *trace;
- TABLES_LOCK();
_Py_hashtable_t *traces = tracemalloc_get_traces_table(domain);
+ trace_t *trace = NULL;
if (traces) {
trace = _Py_hashtable_get(traces, TO_PTR(ptr));
}
- else {
- trace = NULL;
- }
- TABLES_UNLOCK();
-
if (!trace) {
return NULL;
}
@@ -1266,13 +1270,20 @@ _PyMem_DumpTraceback(int fd, const void *ptr)
traceback_t *traceback;
int i;
- if (!tracemalloc_config.tracing) {
+ TABLES_LOCK();
+
+ if (tracemalloc_config.tracing) {
+ traceback = tracemalloc_get_traceback_unlocked(DEFAULT_DOMAIN,
+ (uintptr_t)ptr);
+ }
+ else {
+ traceback = NULL;
PUTS(fd, "Enable tracemalloc to get the memory block "
"allocation traceback\n\n");
- return;
}
- traceback = tracemalloc_get_traceback(DEFAULT_DOMAIN, (uintptr_t)ptr);
+ TABLES_UNLOCK();
+
if (traceback == NULL)
return;
@@ -1301,38 +1312,62 @@ int
PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr,
size_t size)
{
- int res;
- PyGILState_STATE gil_state;
+ PyGILState_STATE gil_state = PyGILState_Ensure();
+ int result;
+ // gh-129185: Check before TABLES_LOCK() to support calls after
+ // _PyTraceMalloc_Fini().
if (!tracemalloc_config.tracing) {
- /* tracemalloc is not tracing: do nothing */
- return -2;
+ result = -2;
+ goto done;
}
- gil_state = PyGILState_Ensure();
-
TABLES_LOCK();
- res = tracemalloc_add_trace(domain, ptr, size);
- TABLES_UNLOCK();
+ if (tracemalloc_config.tracing) {
+ result = tracemalloc_add_trace(domain, ptr, size);
+ }
+ else {
+ // gh-128679: tracemalloc.stop() was called by another thread
+ result = -2;
+ }
+
+ TABLES_UNLOCK();
+done:
PyGILState_Release(gil_state);
- return res;
+ return result;
}
int
PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr)
{
+ // Need the GIL to prevent races on the first 'tracing' test
+ PyGILState_STATE gil_state = PyGILState_Ensure();
+ int result;
+
+ // gh-129185: Check before TABLES_LOCK() to support calls after
+ // _PyTraceMalloc_Fini()
if (!tracemalloc_config.tracing) {
- /* tracemalloc is not tracing: do nothing */
- return -2;
+ result = -2;
+ goto done;
}
TABLES_LOCK();
- tracemalloc_remove_trace(domain, ptr);
- TABLES_UNLOCK();
- return 0;
+ if (tracemalloc_config.tracing) {
+ tracemalloc_remove_trace(domain, ptr);
+ result = 0;
+ }
+ else {
+ /* tracemalloc is not tracing: do nothing */
+ result = -2;
+ }
+
+ TABLES_UNLOCK();
+done:
+ PyGILState_Release(gil_state);
+ return result;
}
@@ -1366,6 +1401,12 @@ _PyTraceMalloc_NewReference(PyObject *op)
int res = -1;
TABLES_LOCK();
+
+ if (!tracemalloc_config.tracing) {
+ // gh-128679: tracemalloc.stop() was called by another thread
+ goto done;
+ }
+
trace_t *trace = _Py_hashtable_get(tracemalloc_traces, TO_PTR(ptr));
if (trace != NULL) {
/* update the traceback of the memory block */
@@ -1376,6 +1417,8 @@ _PyTraceMalloc_NewReference(PyObject *op)
}
}
/* else: cannot track the object, its memory block size is unknown */
+
+done:
TABLES_UNLOCK();
return res;
@@ -1387,7 +1430,9 @@ _PyTraceMalloc_GetTraceback(unsigned int domain, uintptr_t ptr)
{
traceback_t *traceback;
- traceback = tracemalloc_get_traceback(domain, ptr);
+ TABLES_LOCK();
+ traceback = tracemalloc_get_traceback_unlocked(domain, ptr);
+ TABLES_UNLOCK();
if (traceback == NULL)
Py_RETURN_NONE;
@@ -1397,19 +1442,20 @@ _PyTraceMalloc_GetTraceback(unsigned int domain, uintptr_t ptr)
int
_PyTraceMalloc_IsTracing(void)
{
- return tracemalloc_config.tracing;
+ TABLES_LOCK();
+ int tracing = tracemalloc_config.tracing;
+ TABLES_UNLOCK();
+ return tracing;
}
void
_PyTraceMalloc_ClearTraces(void)
{
-
- if (!tracemalloc_config.tracing) {
- return;
+ TABLES_LOCK();
+ if (tracemalloc_config.tracing) {
+ tracemalloc_clear_traces_unlocked();
}
- set_reentrant(1);
- tracemalloc_clear_traces();
- set_reentrant(0);
+ TABLES_UNLOCK();
}
PyObject *
@@ -1496,19 +1542,10 @@ PyObject *
_PyTraceMalloc_GetObjectTraceback(PyObject *obj)
/*[clinic end generated code: output=41ee0553a658b0aa input=29495f1b21c53212]*/
{
- PyTypeObject *type;
- traceback_t *traceback;
-
- type = Py_TYPE(obj);
+ PyTypeObject *type = Py_TYPE(obj);
const size_t presize = _PyType_PreHeaderSize(type);
uintptr_t ptr = (uintptr_t)((char *)obj - presize);
-
- traceback = tracemalloc_get_traceback(DEFAULT_DOMAIN, ptr);
- if (traceback == NULL) {
- Py_RETURN_NONE;
- }
-
- return traceback_to_pyobject(traceback, NULL);
+ return _PyTraceMalloc_GetTraceback(DEFAULT_DOMAIN, ptr);
}
int _PyTraceMalloc_GetTracebackLimit(void) {
@@ -1520,14 +1557,19 @@ _PyTraceMalloc_GetMemory(void) {
size_t size;
- size = _Py_hashtable_size(tracemalloc_tracebacks);
- size += _Py_hashtable_size(tracemalloc_filenames);
-
TABLES_LOCK();
- size += _Py_hashtable_size(tracemalloc_traces);
- _Py_hashtable_foreach(tracemalloc_domains,
- tracemalloc_get_tracemalloc_memory_cb, &size);
+ if (tracemalloc_config.tracing) {
+ size = _Py_hashtable_size(tracemalloc_tracebacks);
+ size += _Py_hashtable_size(tracemalloc_filenames);
+ size += _Py_hashtable_size(tracemalloc_traces);
+ _Py_hashtable_foreach(tracemalloc_domains,
+ tracemalloc_get_tracemalloc_memory_cb, &size);
+ }
+ else {
+ size = 0;
+ }
TABLES_UNLOCK();
+
return size;
}
@@ -1537,12 +1579,15 @@ _PyTraceMalloc_GetTracedMemory(void)
{
Py_ssize_t size, peak_size;
- if (!tracemalloc_config.tracing)
- return Py_BuildValue("ii", 0, 0);
-
TABLES_LOCK();
- size = tracemalloc_traced_memory;
- peak_size = tracemalloc_peak_traced_memory;
+ if (tracemalloc_config.tracing) {
+ size = tracemalloc_traced_memory;
+ peak_size = tracemalloc_peak_traced_memory;
+ }
+ else {
+ size = 0;
+ peak_size = 0;
+ }
TABLES_UNLOCK();
return Py_BuildValue("nn", size, peak_size);
@@ -1551,10 +1596,9 @@ _PyTraceMalloc_GetTracedMemory(void)
void
_PyTraceMalloc_ResetPeak(void)
{
- if (!tracemalloc_config.tracing) {
- return;
- }
TABLES_LOCK();
- tracemalloc_peak_traced_memory = tracemalloc_traced_memory;
+ if (tracemalloc_config.tracing) {
+ tracemalloc_peak_traced_memory = tracemalloc_traced_memory;
+ }
TABLES_UNLOCK();
}
diff --git a/contrib/tools/python3/README.rst b/contrib/tools/python3/README.rst
index c6dc40b17f..3cb9879f34 100644
--- a/contrib/tools/python3/README.rst
+++ b/contrib/tools/python3/README.rst
@@ -1,4 +1,4 @@
-This is Python version 3.12.8
+This is Python version 3.12.9
=============================
.. image:: https://github.com/python/cpython/workflows/Tests/badge.svg
diff --git a/contrib/tools/python3/bin/ya.make b/contrib/tools/python3/bin/ya.make
index 082185c090..71a2859826 100644
--- a/contrib/tools/python3/bin/ya.make
+++ b/contrib/tools/python3/bin/ya.make
@@ -2,9 +2,9 @@
PY3_PROGRAM(python3)
-VERSION(3.12.8)
+VERSION(3.12.9)
-ORIGINAL_SOURCE(https://github.com/python/cpython/archive/v3.12.8.tar.gz)
+ORIGINAL_SOURCE(https://github.com/python/cpython/archive/v3.12.9.tar.gz)
LICENSE(Python-2.0)
diff --git a/contrib/tools/python3/patches/pr127876-fix-segfault.patch b/contrib/tools/python3/patches/pr127876-fix-segfault.patch
deleted file mode 100644
index 1cadd3e8ad..0000000000
--- a/contrib/tools/python3/patches/pr127876-fix-segfault.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From c56b5256ff30f59955362ab536c6f089dd86f9fd Mon Sep 17 00:00:00 2001
-From: Alexander Shadchin <shadchin@yandex-team.com>
-Date: Thu, 12 Dec 2024 19:39:27 +0300
-Subject: [PATCH 1/2] Fix segmentation fault
-
----
- Objects/unicodeobject.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
-index 33c4747bbef488..e043015b49d510 100644
---- a/Objects/unicodeobject.c
-+++ b/Objects/unicodeobject.c
-@@ -1429,12 +1429,12 @@ _copy_characters(PyObject *to, Py_ssize_t to_start,
- assert(PyUnicode_Check(from));
- assert(from_start + how_many <= PyUnicode_GET_LENGTH(from));
-
-- assert(PyUnicode_Check(to));
-- assert(to_start + how_many <= PyUnicode_GET_LENGTH(to));
--
- if (how_many == 0)
- return 0;
-
-+ assert(PyUnicode_Check(to));
-+ assert(to_start + how_many <= PyUnicode_GET_LENGTH(to));
-+
- from_kind = PyUnicode_KIND(from);
- from_data = PyUnicode_DATA(from);
- to_kind = PyUnicode_KIND(to);
diff --git a/contrib/tools/python3/ya.make b/contrib/tools/python3/ya.make
index c7cc5c443b..188fd28c12 100644
--- a/contrib/tools/python3/ya.make
+++ b/contrib/tools/python3/ya.make
@@ -2,9 +2,9 @@
LIBRARY()
-VERSION(3.12.8)
+VERSION(3.12.9)
-ORIGINAL_SOURCE(https://github.com/python/cpython/archive/v3.12.8.tar.gz)
+ORIGINAL_SOURCE(https://github.com/python/cpython/archive/v3.12.9.tar.gz)
LICENSE(Python-2.0)