diff --git a/contrib/tools/python3/Include/cpython/pytime.h b/contrib/tools/python3/Include/cpython/pytime.h index 16d88d191..46cc97bd7 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 546f98d96..de82d9e76 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 4620a2696..dab41405b 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 6c597837d..3a80c5f87 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 2eec3b5a2..dd45eee1a 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 header file. */ #define HAVE_SYS_PARAM_H 1 +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_PIDFD_H */ + /* Define to 1 if you have the 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 81e4e3c15..7439801d0 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 header file. */ #define HAVE_SYS_PARAM_H 1 +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_PIDFD_H */ + /* Define to 1 if you have the 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 d5700dc38..94e6248d2 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 580027a8e..2c88dbab7 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 ad6292e1e..fc43cf0bb 100644 --- a/contrib/tools/python3/Lib/_pydatetime.py +++ b/contrib/tools/python3/Lib/_pydatetime.py @@ -2313,7 +2313,6 @@ def __reduce__(self): 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 75df3db26..ff80180a7 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 dfd2bc5d8..5d9df2b12 100644 --- a/contrib/tools/python3/Lib/_strptime.py +++ b/contrib/tools/python3/Lib/_strptime.py @@ -300,8 +300,6 @@ def __init__(self, locale_time=None): 'V': r"(?P5[0-3]|0[1-9]|[1-4]\d|\d)", # W is set below by using 'U' 'y': r"(?P\d\d)", - #XXX: Does 'Y' need to worry about having less or more than - # 4 digits? 'Y': r"(?P\d\d\d\d)", 'z': r"(?P[+-]\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 b0995fa7f..6d9785cc4 100644 --- a/contrib/tools/python3/Lib/ast.py +++ b/contrib/tools/python3/Lib/ast.py @@ -1246,9 +1246,14 @@ def visit_JoinedStr(self, node): 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 3146f7f3f..136c16318 100644 --- a/contrib/tools/python3/Lib/asyncio/base_events.py +++ b/contrib/tools/python3/Lib/asyncio/base_events.py @@ -466,7 +466,12 @@ def create_task(self, coro, *, name=None, context=None): 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 @@ async def create_server( 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 ce5d8d5bf..588dca6c0 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 1b8923659..102ae7802 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 790711f83..160ed6ca1 100644 --- a/contrib/tools/python3/Lib/asyncio/selector_events.py +++ b/contrib/tools/python3/Lib/asyncio/selector_events.py @@ -1183,15 +1183,19 @@ def writelines(self, list_of_data): # 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 @@ def _reset_empty_waiter(self): 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 0f4df8855..0afed64fd 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 run_one_coro(ok_to_start, previous_failed) -> 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 run_one_coro(ok_to_start, previous_failed) -> 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 aada3ffa8..b2b953b09 100644 --- a/contrib/tools/python3/Lib/asyncio/taskgroups.py +++ b/contrib/tools/python3/Lib/asyncio/taskgroups.py @@ -185,15 +185,20 @@ def create_task(self, coro, *, name=None, context=None): 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 196e6b178..085c17ce0 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 @@ def __init__(self, skip=None): self.breaks = {} self.fncache = {} self.frame_returning = None + self.enterframe = None self._load_breaks() @@ -60,6 +62,12 @@ def reset(self): 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 @@ def trace_dispatch(self, frame, event, arg): 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 @@ def set_trace(self, frame=None): 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 @@ def set_break(self, filename, lineno, temporary=False, cond=None, 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 320dec03d..b1069c825 100644 --- a/contrib/tools/python3/Lib/dis.py +++ b/contrib/tools/python3/Lib/dis.py @@ -790,12 +790,12 @@ def dis(self): 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 ec2215a5e..3d845c09d 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 @@ 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 46bb8c219..6b7c3a237 100644 --- a/contrib/tools/python3/Lib/email/message.py +++ b/contrib/tools/python3/Lib/email/message.py @@ -286,8 +286,12 @@ def get_payload(self, i=None, decode=False): 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 f6849899e..6025032bf 100644 --- a/contrib/tools/python3/Lib/functools.py +++ b/contrib/tools/python3/Lib/functools.py @@ -340,6 +340,9 @@ def __setstate__(self, state): 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 a353716a8..fb29923d9 100644 --- a/contrib/tools/python3/Lib/http/client.py +++ b/contrib/tools/python3/Lib/http/client.py @@ -472,7 +472,7 @@ def read(self, amt=None): 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 @@ def _get_chunk_left(self): 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 6b9ed24ad..57791c6ab 100644 --- a/contrib/tools/python3/Lib/http/cookies.py +++ b/contrib/tools/python3/Lib/http/cookies.py @@ -424,9 +424,11 @@ def OutputString(self, attrs=None): ( # Optional group: there may not be a value. \s*=\s* # Equal Sign (?P # 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 577b4b9b0..e337fe647 100644 --- a/contrib/tools/python3/Lib/imaplib.py +++ b/contrib/tools/python3/Lib/imaplib.py @@ -52,6 +52,9 @@ # 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 @@ def open(self, host='', port=IMAP4_PORT, timeout=None): 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 a39025353..a85df4b39 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 05eb49d3b..06eea3c94 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 fdbc3bda7..81ed2ae51 100644 --- a/contrib/tools/python3/Lib/multiprocessing/connection.py +++ b/contrib/tools/python3/Lib/multiprocessing/connection.py @@ -846,7 +846,7 @@ def PipeClient(address): _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 79e96ecf3..23fea295c 100644 --- a/contrib/tools/python3/Lib/multiprocessing/resource_tracker.py +++ b/contrib/tools/python3/Lib/multiprocessing/resource_tracker.py @@ -142,13 +142,14 @@ def ensure_running(self): # 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 0f682b9a0..870c91349 100644 --- a/contrib/tools/python3/Lib/multiprocessing/synchronize.py +++ b/contrib/tools/python3/Lib/multiprocessing/synchronize.py @@ -360,7 +360,7 @@ def wait(self, timeout=None): 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 1e1b5ea4f..2a6e994da 100755 --- a/contrib/tools/python3/Lib/pdb.py +++ b/contrib/tools/python3/Lib/pdb.py @@ -281,6 +281,7 @@ def forget(self): 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 c5b604803..b86e68349 100755 --- a/contrib/tools/python3/Lib/platform.py +++ b/contrib/tools/python3/Lib/platform.py @@ -348,7 +348,8 @@ def _wmi_query(table, *keys): ] _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 9a5ef03c9..81b013859 100644 --- a/contrib/tools/python3/Lib/poplib.py +++ b/contrib/tools/python3/Lib/poplib.py @@ -309,7 +309,7 @@ def close(self): # 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 e3745e545..9dfa87b2a 100755 --- a/contrib/tools/python3/Lib/pydoc.py +++ b/contrib/tools/python3/Lib/pydoc.py @@ -54,6 +54,7 @@ class or function within a module or module in a package. If the # 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 12523999c..66204433c 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 @@ ' 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 @@ '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 @@ '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 @@ '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 @@ ' 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 c1880c4ea..91782b30a 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 cd028ef1c..35b2723de 100644 --- a/contrib/tools/python3/Lib/socketserver.py +++ b/contrib/tools/python3/Lib/socketserver.py @@ -468,7 +468,12 @@ def server_bind(self): """ 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 1d17ae360..3ec39ca3e 100644 --- a/contrib/tools/python3/Lib/subprocess.py +++ b/contrib/tools/python3/Lib/subprocess.py @@ -43,10 +43,8 @@ import builtins import errno import io -import locale import os import time -import signal import sys import threading import warnings @@ -138,6 +136,8 @@ def __init__(self, returncode, cmd, output=None, stderr=None): 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 @@ def send_signal(self, sig): # 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 @@ def _posix_spawn(self, args, executable, env, restore_signals, 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 @@ def send_signal(self, sig): 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 9bb81e784..168f42871 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 0bba85d08..064c74d40 100644 --- a/contrib/tools/python3/Lib/threading.py +++ b/contrib/tools/python3/Lib/threading.py @@ -686,7 +686,7 @@ def __init__(self, parties, action=None, timeout=None): """ 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 b2dff8e69..553c1ca43 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 a271416d4..d7f96b60f 100644 --- a/contrib/tools/python3/Lib/typing.py +++ b/contrib/tools/python3/Lib/typing.py @@ -1610,12 +1610,16 @@ def __repr__(self): 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 248159520..c72138a33 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 9a559f441..c7ded0f67 100644 --- a/contrib/tools/python3/Lib/urllib/request.py +++ b/contrib/tools/python3/Lib/urllib/request.py @@ -903,9 +903,9 @@ def find_user_password(self, realm, authuri): 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 c58565e39..409f2b2e4 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 @@ def read(self): 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 8a2002634..a8852625a 100644 --- a/contrib/tools/python3/Lib/xml/dom/xmlbuilder.py +++ b/contrib/tools/python3/Lib/xml/dom/xmlbuilder.py @@ -189,7 +189,7 @@ def parse(self, input): 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 @@ def _create_opener(self): 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 ba53fefab..2310e5fa9 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 cf71c6dba..91b2e032e 100644 --- a/contrib/tools/python3/Lib/zipfile/__init__.py +++ b/contrib/tools/python3/Lib/zipfile/__init__.py @@ -794,7 +794,10 @@ def seek(self, offset, whence=0): 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 @@ def seek(self, offset, whence=os.SEEK_SET): 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 9a7b7d27c..df7207d08 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 b6d45e926..96a057554 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 d71297f9c..65dfb980a 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 d2fe525dd..f98479ee8 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 299882095..3cc7d6f50 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 c7df82dfe..bc718e273 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 12e5c135a..1450037ca 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 2b3bbfefa..c521fc5ad 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 bbafbaff8..7abcc1886 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 dbb4e8da7..498c538c6 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 f3f4af9ab..3a4092c9d 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/arraymodule.c b/contrib/tools/python3/Modules/arraymodule.c index d24c5989a..090a7b841 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 a33461dc5..57121e1e8 100644 --- a/contrib/tools/python3/Modules/clinic/posixmodule.c.h +++ b/contrib/tools/python3/Modules/clinic/posixmodule.c.h @@ -304,7 +304,7 @@ os_access(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k 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 @@ os_ttyname(PyObject *module, PyObject *arg) return return_value; } -#endif /* defined(HAVE_TTYNAME) */ +#endif /* defined(HAVE_TTYNAME_R) */ #if defined(HAVE_CTERMID) @@ -12032,4 +12032,4 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #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 5c6087862..7ed7c440e 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 ce2c80e6d..370751655 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 // ctermid() #include // system() +#ifdef HAVE_SYS_PIDFD_H +# error #include // 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 #endif +#if !defined(EX_OK) && defined(EXIT_SUCCESS) +# define EX_OK EXIT_SUCCESS +#endif + #ifdef HAVE_SYS_LOADAVG_H # include #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 be31c637f..b354a86e7 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 6db8de9c4..c925a42dc 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 4e9653149..7337e290e 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 474abe109..640a7d906 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 7cb17a6ca..66e449076 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 2cc4ddd3c..7082c6fd4 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 46d7a4b97..012920fcf 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 8fe275d4c..8c2586664 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 be52d8949..e029ae692 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 106307e77..f35cd94d6 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 6110883ca..3985b5264 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 @@ match_class(PyThreadState *tstate, PyObject *subject, PyObject *type, 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 @@ do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause) */ 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 7a16841eb..e4b7fd6b2 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 bbaf589e2..246d37e77 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 e9c1a0d72..ef4e60765 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 5f3d249df..cf84573a8 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 7240f8e4e..7813048be 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 e13064bd1..852e5b039 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 c6dc40b17..3cb9879f3 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 082185c09..71a285982 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/ya.make b/contrib/tools/python3/ya.make index c7cc5c443..188fd28c1 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)