From 4e7e2dd043c1da85b0c157d3ed24866b77e83a4f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 2 Oct 2025 22:51:57 +0200 Subject: [PATCH 001/373] gh-139322: Reenable test_os.test_getlogin() (#139498) Fix also getlogin() errno. --- Lib/test/test_os/test_os.py | 11 +++++++---- .../2025-10-02-15-45-08.gh-issue-139322.rouPGj.rst | 2 ++ Modules/posixmodule.c | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-02-15-45-08.gh-issue-139322.rouPGj.rst diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py index 43f1a79a7c7..371771087aa 100644 --- a/Lib/test/test_os/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -3197,13 +3197,16 @@ def test_spawnvpe_invalid_env(self): self._test_invalid_env(os.spawnvpe) -# The introduction of this TestCase caused at least two different errors on -# *nix buildbots. Temporarily skip this to let the buildbots move along. -@unittest.skip("Skip due to platform/environment differences on *NIX buildbots") @unittest.skipUnless(hasattr(os, 'getlogin'), "test needs os.getlogin") class LoginTests(unittest.TestCase): def test_getlogin(self): - user_name = os.getlogin() + try: + user_name = os.getlogin() + except OSError as exc: + if exc.errno in (errno.ENOTTY, errno.ENXIO): + self.skipTest(str(exc)) + else: + raise self.assertNotEqual(len(user_name), 0) diff --git a/Misc/NEWS.d/next/Library/2025-10-02-15-45-08.gh-issue-139322.rouPGj.rst b/Misc/NEWS.d/next/Library/2025-10-02-15-45-08.gh-issue-139322.rouPGj.rst new file mode 100644 index 00000000000..39cae22474c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-02-15-45-08.gh-issue-139322.rouPGj.rst @@ -0,0 +1,2 @@ +Fix :func:`os.getlogin` error handling: fix the error number. Patch by +Victor Stinner. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index b7a01102265..f50167c223e 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9605,7 +9605,7 @@ os_getlogin_impl(PyObject *module) int err = getlogin_r(name, sizeof(name)); if (err) { int old_errno = errno; - errno = -err; + errno = err; posix_error(); errno = old_errno; } From fd7dac0430c3773b90a1fb73f7a29ecc6199fdc0 Mon Sep 17 00:00:00 2001 From: Angela Liss <59097311+angela-tarantula@users.noreply.github.com> Date: Fri, 3 Oct 2025 01:50:19 -0400 Subject: [PATCH 002/373] gh-137840: Implement PEP 728 (closed and extra_items in typing.TypedDict) (#137933) Co-authored-by: Jelle Zijlstra --- Lib/test/test_typing.py | 99 ++++++++++++++++++- Lib/typing.py | 67 ++++++++++++- ...-08-18-07-10-55.gh-issue-137840.9b7AnG.rst | 3 + 3 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-08-18-07-10-55.gh-issue-137840.9b7AnG.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 1c8b2978aa3..428089d88cc 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -35,7 +35,7 @@ from typing import dataclass_transform from typing import no_type_check, no_type_check_decorator from typing import Type -from typing import NamedTuple, NotRequired, Required, ReadOnly, TypedDict +from typing import NamedTuple, NotRequired, Required, ReadOnly, TypedDict, NoExtraItems from typing import IO, TextIO, BinaryIO from typing import Pattern, Match from typing import Annotated, ForwardRef @@ -8820,6 +8820,32 @@ class ChildWithInlineAndOptional(Untotal, Inline): class Wrong(*bases): pass + def test_closed_values(self): + class Implicit(TypedDict): ... + class ExplicitTrue(TypedDict, closed=True): ... + class ExplicitFalse(TypedDict, closed=False): ... + + self.assertIsNone(Implicit.__closed__) + self.assertIs(ExplicitTrue.__closed__, True) + self.assertIs(ExplicitFalse.__closed__, False) + + def test_extra_items_class_arg(self): + class TD(TypedDict, extra_items=int): + a: str + + self.assertIs(TD.__extra_items__, int) + self.assertEqual(TD.__annotations__, {'a': str}) + self.assertEqual(TD.__required_keys__, frozenset({'a'})) + self.assertEqual(TD.__optional_keys__, frozenset()) + + class NoExtra(TypedDict): + a: str + + self.assertIs(NoExtra.__extra_items__, NoExtraItems) + self.assertEqual(NoExtra.__annotations__, {'a': str}) + self.assertEqual(NoExtra.__required_keys__, frozenset({'a'})) + self.assertEqual(NoExtra.__optional_keys__, frozenset()) + def test_is_typeddict(self): self.assertIs(is_typeddict(Point2D), True) self.assertIs(is_typeddict(Union[str, int]), False) @@ -9147,6 +9173,71 @@ class AllTheThings(TypedDict): }, ) + def test_closed_inheritance(self): + class Base(TypedDict, extra_items=ReadOnly[Union[str, None]]): + a: int + + self.assertEqual(Base.__required_keys__, frozenset({"a"})) + self.assertEqual(Base.__optional_keys__, frozenset({})) + self.assertEqual(Base.__readonly_keys__, frozenset({})) + self.assertEqual(Base.__mutable_keys__, frozenset({"a"})) + self.assertEqual(Base.__annotations__, {"a": int}) + self.assertEqual(Base.__extra_items__, ReadOnly[Union[str, None]]) + self.assertIsNone(Base.__closed__) + + class Child(Base, extra_items=int): + a: str + + self.assertEqual(Child.__required_keys__, frozenset({'a'})) + self.assertEqual(Child.__optional_keys__, frozenset({})) + self.assertEqual(Child.__readonly_keys__, frozenset({})) + self.assertEqual(Child.__mutable_keys__, frozenset({'a'})) + self.assertEqual(Child.__annotations__, {"a": str}) + self.assertIs(Child.__extra_items__, int) + self.assertIsNone(Child.__closed__) + + class GrandChild(Child, closed=True): + a: float + + self.assertEqual(GrandChild.__required_keys__, frozenset({'a'})) + self.assertEqual(GrandChild.__optional_keys__, frozenset({})) + self.assertEqual(GrandChild.__readonly_keys__, frozenset({})) + self.assertEqual(GrandChild.__mutable_keys__, frozenset({'a'})) + self.assertEqual(GrandChild.__annotations__, {"a": float}) + self.assertIs(GrandChild.__extra_items__, NoExtraItems) + self.assertIs(GrandChild.__closed__, True) + + class GrandGrandChild(GrandChild): + ... + self.assertEqual(GrandGrandChild.__required_keys__, frozenset({'a'})) + self.assertEqual(GrandGrandChild.__optional_keys__, frozenset({})) + self.assertEqual(GrandGrandChild.__readonly_keys__, frozenset({})) + self.assertEqual(GrandGrandChild.__mutable_keys__, frozenset({'a'})) + self.assertEqual(GrandGrandChild.__annotations__, {"a": float}) + self.assertIs(GrandGrandChild.__extra_items__, NoExtraItems) + self.assertIsNone(GrandGrandChild.__closed__) + + def test_implicit_extra_items(self): + class Base(TypedDict): + a: int + + self.assertIs(Base.__extra_items__, NoExtraItems) + self.assertIsNone(Base.__closed__) + + class ChildA(Base, closed=True): + ... + + self.assertEqual(ChildA.__extra_items__, NoExtraItems) + self.assertIs(ChildA.__closed__, True) + + def test_cannot_combine_closed_and_extra_items(self): + with self.assertRaisesRegex( + TypeError, + "Cannot combine closed=True and extra_items" + ): + class TD(TypedDict, closed=True, extra_items=range): + x: str + def test_annotations(self): # _type_check is applied with self.assertRaisesRegex(TypeError, "Plain typing.Final is not valid as type argument"): @@ -9376,6 +9467,12 @@ class A(typing.Match): class B(typing.Pattern): pass + def test_typed_dict_signature(self): + self.assertListEqual( + list(inspect.signature(TypedDict).parameters), + ['typename', 'fields', 'total', 'closed', 'extra_items'] + ) + class AnnotatedTests(BaseTestCase): diff --git a/Lib/typing.py b/Lib/typing.py index df84e2c8764..4311a77b8db 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -141,6 +141,7 @@ 'no_type_check', 'no_type_check_decorator', 'NoDefault', + 'NoExtraItems', 'NoReturn', 'NotRequired', 'overload', @@ -3063,6 +3064,33 @@ def _namedtuple_mro_entries(bases): NamedTuple.__mro_entries__ = _namedtuple_mro_entries +class _SingletonMeta(type): + def __setattr__(cls, attr, value): + # TypeError is consistent with the behavior of NoneType + raise TypeError( + f"cannot set {attr!r} attribute of immutable type {cls.__name__!r}" + ) + + +class _NoExtraItemsType(metaclass=_SingletonMeta): + """The type of the NoExtraItems singleton.""" + + __slots__ = () + + def __new__(cls): + return globals().get("NoExtraItems") or object.__new__(cls) + + def __repr__(self): + return 'typing.NoExtraItems' + + def __reduce__(self): + return 'NoExtraItems' + +NoExtraItems = _NoExtraItemsType() +del _NoExtraItemsType +del _SingletonMeta + + def _get_typeddict_qualifiers(annotation_type): while True: annotation_origin = get_origin(annotation_type) @@ -3086,7 +3114,8 @@ def _get_typeddict_qualifiers(annotation_type): class _TypedDictMeta(type): - def __new__(cls, name, bases, ns, total=True): + def __new__(cls, name, bases, ns, total=True, closed=None, + extra_items=NoExtraItems): """Create a new typed dict class object. This method is called when TypedDict is subclassed, @@ -3098,6 +3127,8 @@ def __new__(cls, name, bases, ns, total=True): if type(base) is not _TypedDictMeta and base is not Generic: raise TypeError('cannot inherit from both a TypedDict type ' 'and a non-TypedDict base class') + if closed is not None and extra_items is not NoExtraItems: + raise TypeError(f"Cannot combine closed={closed!r} and extra_items") if any(issubclass(b, Generic) for b in bases): generic_base = (Generic,) @@ -3209,6 +3240,8 @@ def __annotate__(format): tp_dict.__readonly_keys__ = frozenset(readonly_keys) tp_dict.__mutable_keys__ = frozenset(mutable_keys) tp_dict.__total__ = total + tp_dict.__closed__ = closed + tp_dict.__extra_items__ = extra_items return tp_dict __call__ = dict # static method @@ -3220,7 +3253,8 @@ def __subclasscheck__(cls, other): __instancecheck__ = __subclasscheck__ -def TypedDict(typename, fields, /, *, total=True): +def TypedDict(typename, fields, /, *, total=True, closed=None, + extra_items=NoExtraItems): """A simple typed namespace. At runtime it is equivalent to a plain dict. TypedDict creates a dictionary type such that a type checker will expect all @@ -3274,6 +3308,32 @@ class DatabaseUser(TypedDict): id: ReadOnly[int] # the "id" key must not be modified username: str # the "username" key can be changed + The closed argument controls whether the TypedDict allows additional + non-required items during inheritance and assignability checks. + If closed=True, the TypedDict does not allow additional items:: + + Point2D = TypedDict('Point2D', {'x': int, 'y': int}, closed=True) + class Point3D(Point2D): + z: int # Type checker error + + Passing closed=False explicitly requests TypedDict's default open behavior. + If closed is not provided, the behavior is inherited from the superclass. + A type checker is only expected to support a literal False or True as the + value of the closed argument. + + The extra_items argument can instead be used to specify the assignable type + of unknown non-required keys:: + + Point2D = TypedDict('Point2D', {'x': int, 'y': int}, extra_items=int) + class Point3D(Point2D): + z: int # OK + label: str # Type checker error + + The extra_items argument is also inherited through subclassing. It is unset + by default, and it may not be used with the closed argument at the same + time. + + See PEP 728 for more information about closed and extra_items. """ ns = {'__annotations__': dict(fields)} module = _caller() @@ -3281,7 +3341,8 @@ class DatabaseUser(TypedDict): # Setting correct module is necessary to make typed dict classes pickleable. ns['__module__'] = module - td = _TypedDictMeta(typename, (), ns, total=total) + td = _TypedDictMeta(typename, (), ns, total=total, closed=closed, + extra_items=extra_items) td.__orig_bases__ = (TypedDict,) return td diff --git a/Misc/NEWS.d/next/Library/2025-08-18-07-10-55.gh-issue-137840.9b7AnG.rst b/Misc/NEWS.d/next/Library/2025-08-18-07-10-55.gh-issue-137840.9b7AnG.rst new file mode 100644 index 00000000000..5ea25a86204 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-08-18-07-10-55.gh-issue-137840.9b7AnG.rst @@ -0,0 +1,3 @@ +:class:`typing.TypedDict` now supports the ``closed`` and ``extra_items`` +keyword arguments (as described in :pep:`728`) to control whether additional +non-required keys are allowed and to specify their value type. From aa99a7c70c1d07717fff37133d5cc08e569d9c62 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 3 Oct 2025 12:48:57 +0530 Subject: [PATCH 003/373] gh-105987: unskip `test_issue105987` from `test_asyncio.test_eager_task_factory` (#139538) --- Lib/test/test_asyncio/test_eager_task_factory.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_asyncio/test_eager_task_factory.py b/Lib/test/test_asyncio/test_eager_task_factory.py index da79ee9260a..0561b54a3f1 100644 --- a/Lib/test/test_asyncio/test_eager_task_factory.py +++ b/Lib/test/test_asyncio/test_eager_task_factory.py @@ -316,11 +316,9 @@ def tearDown(self): asyncio.all_tasks = asyncio.tasks.all_tasks = self._all_tasks return super().tearDown() - - @unittest.skip("skip") def test_issue105987(self): code = """if 1: - from _asyncio import _swap_current_task + from _asyncio import _swap_current_task, _set_running_loop class DummyTask: pass @@ -329,6 +327,7 @@ class DummyLoop: pass l = DummyLoop() + _set_running_loop(l) _swap_current_task(l, DummyTask()) t = _swap_current_task(l, None) """ From e6e376a760b7a3b267e89244a2c89e7ddfba9c38 Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Fri, 3 Oct 2025 14:58:00 +0500 Subject: [PATCH 004/373] gh-132042: Remove resolve_slotdups() to speedup class creation (#132156) Co-authored-by: Victor Stinner Co-authored-by: sobolevn Co-authored-by: Kumar Aditya --- Include/internal/pycore_interp_structs.h | 5 - Include/internal/pycore_typeobject.h | 3 + ...-04-10-01-52-42.gh-issue-132042.fePwlj.rst | 2 + Objects/typeobject.c | 140 +++++++++++------- Python/pylifecycle.c | 4 + Tools/c-analyzer/cpython/ignored.tsv | 2 + 6 files changed, 95 insertions(+), 61 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-04-10-01-52-42.gh-issue-132042.fePwlj.rst diff --git a/Include/internal/pycore_interp_structs.h b/Include/internal/pycore_interp_structs.h index 0e039de8ae0..2124e76514f 100644 --- a/Include/internal/pycore_interp_structs.h +++ b/Include/internal/pycore_interp_structs.h @@ -672,11 +672,6 @@ struct _Py_interp_cached_objects { /* object.__reduce__ */ PyObject *objreduce; -#ifndef Py_GIL_DISABLED - /* resolve_slotdups() */ - PyObject *type_slots_pname; - pytype_slotdef *type_slots_ptrs[MAX_EQUIV]; -#endif /* TypeVar and related types */ PyTypeObject *generic_type; diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 402f168547e..3661f171e2b 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -152,6 +152,9 @@ typedef int (*_py_validate_type)(PyTypeObject *); extern int _PyType_Validate(PyTypeObject *ty, _py_validate_type validate, unsigned int *tp_version); extern int _PyType_CacheGetItemForSpecialization(PyHeapTypeObject *ht, PyObject *descriptor, uint32_t tp_version); +// Precalculates count of non-unique slots and fills wrapperbase.name_count. +extern int _PyType_InitSlotDefs(PyInterpreterState *interp); + #ifdef __cplusplus } #endif diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-10-01-52-42.gh-issue-132042.fePwlj.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-10-01-52-42.gh-issue-132042.fePwlj.rst new file mode 100644 index 00000000000..cd37a93b3a5 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-10-01-52-42.gh-issue-132042.fePwlj.rst @@ -0,0 +1,2 @@ +Improve class creation times by up to 12% by pre-computing type slots +just once. Patch by Sergey Miryanov. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 7d03655e77a..9398bcb29c8 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -11422,6 +11422,11 @@ static pytype_slotdef slotdefs[] = { {NULL} }; +/* Stores the number of times where slotdefs has elements with same name. + This counter precalculated by _PyType_InitSlotDefs() when the main + interpreter starts. */ +static uint8_t slotdefs_name_counts[Py_ARRAY_LENGTH(slotdefs)]; + /* Given a type pointer and an offset gotten from a slotdef entry, return a pointer to the actual slot. This is not quite the same as simply adding the offset to the type pointer, since it takes care to indirect through the @@ -11464,61 +11469,6 @@ slotptr(PyTypeObject *type, int ioffset) return (void **)ptr; } -/* Return a slot pointer for a given name, but ONLY if the attribute has - exactly one slot function. The name must be an interned string. */ -static void ** -resolve_slotdups(PyTypeObject *type, PyObject *name) -{ - /* XXX Maybe this could be optimized more -- but is it worth it? */ - -#ifdef Py_GIL_DISABLED - pytype_slotdef *ptrs[MAX_EQUIV]; - pytype_slotdef **pp = ptrs; - /* Collect all slotdefs that match name into ptrs. */ - for (pytype_slotdef *p = slotdefs; p->name_strobj; p++) { - if (p->name_strobj == name) - *pp++ = p; - } - *pp = NULL; -#else - /* pname and ptrs act as a little cache */ - PyInterpreterState *interp = _PyInterpreterState_GET(); -#define pname _Py_INTERP_CACHED_OBJECT(interp, type_slots_pname) -#define ptrs _Py_INTERP_CACHED_OBJECT(interp, type_slots_ptrs) - pytype_slotdef *p, **pp; - - if (pname != name) { - /* Collect all slotdefs that match name into ptrs. */ - pname = name; - pp = ptrs; - for (p = slotdefs; p->name_strobj; p++) { - if (p->name_strobj == name) - *pp++ = p; - } - *pp = NULL; - } -#endif - - /* Look in all slots of the type matching the name. If exactly one of these - has a filled-in slot, return a pointer to that slot. - Otherwise, return NULL. */ - void **res, **ptr; - res = NULL; - for (pp = ptrs; *pp; pp++) { - ptr = slotptr(type, (*pp)->offset); - if (ptr == NULL || *ptr == NULL) - continue; - if (res != NULL) - return NULL; - res = ptr; - } -#ifndef Py_GIL_DISABLED -#undef pname -#undef ptrs -#endif - return res; -} - // Return true if "name" corresponds to at least one slot definition. This is // a more accurate but more expensive test compared to is_dunder_name(). static bool @@ -11645,7 +11595,15 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p, pytype_slotdef **next_p, } if (Py_IS_TYPE(descr, &PyWrapperDescr_Type) && ((PyWrapperDescrObject *)descr)->d_base->name_strobj == p->name_strobj) { - void **tptr = resolve_slotdups(type, p->name_strobj); + void **tptr; + size_t index = (p - slotdefs) / sizeof(slotdefs[0]); + if (slotdefs_name_counts[index] == 1) { + tptr = slotptr(type, p->offset); + } + else { + tptr = NULL; + } + if (tptr == NULL || tptr == ptr) generic = p->function; d = (PyWrapperDescrObject *)descr; @@ -11858,6 +11816,76 @@ update_all_slots(PyTypeObject* type) #endif +int +_PyType_InitSlotDefs(PyInterpreterState *interp) +{ + if (!_Py_IsMainInterpreter(interp)) { + return 0; + } + PyObject *bytearray = NULL; + PyObject *cache = PyDict_New(); + if (!cache) { + return -1; + } + + pytype_slotdef *p; + Py_ssize_t idx = 0; + for (p = slotdefs; p->name_strobj; p++, idx++) { + assert(idx < 255); + + if (PyDict_GetItemRef(cache, p->name_strobj, &bytearray) < 0) { + goto error; + } + + if (!bytearray) { + Py_ssize_t size = sizeof(uint8_t) * (1 + MAX_EQUIV); + bytearray = PyByteArray_FromStringAndSize(NULL, size); + if (!bytearray) { + goto error; + } + + uint8_t *data = (uint8_t *)PyByteArray_AS_STRING(bytearray); + data[0] = 0; + + if (PyDict_SetItem(cache, p->name_strobj, bytearray) < 0) { + goto error; + } + } + + assert(PyByteArray_CheckExact(bytearray)); + uint8_t *data = (uint8_t *)PyByteArray_AS_STRING(bytearray); + + data[0] += 1; + assert(data[0] < MAX_EQUIV); + + data[data[0]] = (uint8_t)idx; + + Py_CLEAR(bytearray); + } + + memset(slotdefs_name_counts, 0, sizeof(slotdefs_name_counts)); + + Py_ssize_t pos = 0; + PyObject *key = NULL; + PyObject *value = NULL; + while (PyDict_Next(cache, &pos, &key, &value)) { + uint8_t *data = (uint8_t *)PyByteArray_AS_STRING(value); + uint8_t n = data[0]; + for (uint8_t i = 0; i < n; i++) { + uint8_t idx = data[i + 1]; + slotdefs_name_counts[idx] = n; + } + } + + Py_DECREF(cache); + return 0; + +error: + Py_XDECREF(bytearray); + Py_DECREF(cache); + return -1; +} + PyObject * _PyType_GetSlotWrapperNames(void) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 185c9ae7528..b813166f167 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -836,6 +836,10 @@ pycore_init_builtins(PyThreadState *tstate) } interp->callable_cache.object__getattribute__ = object__getattribute__; + if (_PyType_InitSlotDefs(interp) < 0) { + return _PyStatus_ERR("failed to init slotdefs"); + } + if (_PyBuiltins_AddExceptions(bimod) < 0) { return _PyStatus_ERR("failed to add exceptions to builtins"); } diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index 4fdb7b3cd1a..c3b13d69f0d 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -344,6 +344,8 @@ Objects/obmalloc.c - obmalloc_state_main - Objects/obmalloc.c - obmalloc_state_initialized - Objects/typeobject.c - name_op - Objects/typeobject.c - slotdefs - +# It initialized only once when main interpeter starts +Objects/typeobject.c - slotdefs_name_counts - Objects/unicodeobject.c - stripfuncnames - Objects/unicodeobject.c - utf7_category - Objects/unicodeobject.c unicode_decode_call_errorhandler_wchar argparse - From 12805ef9dac1d564fef222d632dcb4063117bc32 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Fri, 3 Oct 2025 12:33:49 +0100 Subject: [PATCH 005/373] `Python/codecs.c`: Remove unused forward declaration (#139511) --- Python/codecs.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Python/codecs.c b/Python/codecs.c index 364e07990ba..0bde56c0ac6 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -83,8 +83,6 @@ PyCodec_Unregister(PyObject *search_function) return 0; } -extern int _Py_normalize_encoding(const char *, char *, size_t); - /* Convert a string to a normalized Python string: all ASCII letters are converted to lower case, spaces are replaced with hyphens. */ From ff0cf0af10a775ea03dc2546b6a5506a708c2ab7 Mon Sep 17 00:00:00 2001 From: Dino Viehland Date: Fri, 3 Oct 2025 09:58:32 -0700 Subject: [PATCH 006/373] gh-139525: Don't specialize functions which have a modified vectorcall (#139524) Don't specialize functions which have a modified vectorcall --- Lib/test/test_opcache.py | 8 ++++++++ Modules/_testinternalcapi.c | 20 ++++++++++++++++++++ Python/specialize.c | 9 +++++++++ 3 files changed, 37 insertions(+) diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index f1271fc540e..f23f8c053e8 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -567,6 +567,14 @@ def test(default=None): with self.assertRaises(RecursionError): test() + def test_dont_specialize_custom_vectorcall(self): + def f(): + raise Exception("no way") + + _testinternalcapi.set_vectorcall_nop(f) + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + f() + def make_deferred_ref_count_obj(): """Create an object that uses deferred reference counting. diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index a4348e7e149..c2647d405e2 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -2399,6 +2399,25 @@ simple_pending_call(PyObject *self, PyObject *callable) Py_RETURN_NONE; } +static PyObject * +vectorcall_nop(PyObject *callable, PyObject *const *args, + size_t nargsf, PyObject *kwnames) +{ + Py_RETURN_NONE; +} + +static PyObject * +set_vectorcall_nop(PyObject *self, PyObject *func) +{ + if (!PyFunction_Check(func)) { + PyErr_SetString(PyExc_TypeError, "expected function"); + return NULL; + } + + ((PyFunctionObject*)func)->vectorcall = vectorcall_nop; + Py_RETURN_NONE; +} + static PyMethodDef module_functions[] = { {"get_configs", get_configs, METH_NOARGS}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, @@ -2507,6 +2526,7 @@ static PyMethodDef module_functions[] = { {"emscripten_set_up_async_input_device", emscripten_set_up_async_input_device, METH_NOARGS}, #endif {"simple_pending_call", simple_pending_call, METH_O}, + {"set_vectorcall_nop", set_vectorcall_nop, METH_O}, {NULL, NULL} /* sentinel */ }; diff --git a/Python/specialize.c b/Python/specialize.c index 47f7b27b490..a1c5dedd615 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -630,6 +630,7 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters #define SPEC_FAIL_CALL_INIT_NOT_PYTHON 21 #define SPEC_FAIL_CALL_PEP_523 22 #define SPEC_FAIL_CALL_BOUND_METHOD 23 +#define SPEC_FAIL_CALL_VECTORCALL 24 #define SPEC_FAIL_CALL_CLASS_MUTABLE 26 #define SPEC_FAIL_CALL_METHOD_WRAPPER 28 #define SPEC_FAIL_CALL_OPERATOR_WRAPPER 29 @@ -2071,6 +2072,10 @@ specialize_py_call(PyFunctionObject *func, _Py_CODEUNIT *instr, int nargs, SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_PEP_523); return -1; } + if (func->vectorcall != _PyFunction_Vectorcall) { + SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_VECTORCALL); + return -1; + } int argcount = -1; if (kind == SPEC_FAIL_CODE_NOT_OPTIMIZED) { SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CODE_NOT_OPTIMIZED); @@ -2110,6 +2115,10 @@ specialize_py_call_kw(PyFunctionObject *func, _Py_CODEUNIT *instr, int nargs, SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_PEP_523); return -1; } + if (func->vectorcall != _PyFunction_Vectorcall) { + SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_VECTORCALL); + return -1; + } if (kind == SPEC_FAIL_CODE_NOT_OPTIMIZED) { SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CODE_NOT_OPTIMIZED); return -1; From 1ae92503647328544866f9586f57eec285e1949a Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Fri, 3 Oct 2025 15:49:03 -0400 Subject: [PATCH 007/373] gh-137638: Use macos-15-intel in GitHub Actions (#139154) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- .github/actionlint.yaml | 3 ++- .github/workflows/build.yml | 6 +++--- .github/workflows/jit.yml | 7 +------ .github/workflows/reusable-macos.yml | 6 +++--- .github/workflows/tail-call.yml | 9 +-------- 5 files changed, 10 insertions(+), 21 deletions(-) diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml index 68aae196357..267ff6b42a8 100644 --- a/.github/actionlint.yaml +++ b/.github/actionlint.yaml @@ -1,6 +1,7 @@ self-hosted-runner: # Pending https://github.com/rhysd/actionlint/issues/533 - labels: ["windows-11-arm"] + # and https://github.com/rhysd/actionlint/issues/571 + labels: ["windows-11-arm", "macos-15-intel"] config-variables: null diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 83a668fc720..ebfaf32e193 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -202,13 +202,13 @@ jobs: strategy: fail-fast: false matrix: - # Cirrus and macos-14 are M1, macos-13 is default GHA Intel. + # Cirrus and macos-14 are M1, macos-15-intel is default GHA Intel. # macOS 13 only runs tests against the GIL-enabled CPython. # Cirrus used for upstream, macos-14 for forks. os: - ghcr.io/cirruslabs/macos-runner:sonoma - macos-14 - - macos-13 + - macos-15-intel is-fork: # only used for the exclusion trick - ${{ github.repository_owner != 'python' }} free-threading: @@ -219,7 +219,7 @@ jobs: is-fork: true - os: macos-14 is-fork: false - - os: macos-13 + - os: macos-15-intel free-threading: true uses: ./.github/workflows/reusable-macos.yml with: diff --git a/.github/workflows/jit.yml b/.github/workflows/jit.yml index 80e4ae603a2..c32bf4fd63c 100644 --- a/.github/workflows/jit.yml +++ b/.github/workflows/jit.yml @@ -81,7 +81,7 @@ jobs: runner: windows-11-arm - target: x86_64-apple-darwin/clang architecture: x86_64 - runner: macos-13 + runner: macos-15-intel - target: aarch64-apple-darwin/clang architecture: aarch64 runner: macos-14 @@ -106,15 +106,10 @@ jobs: ./PCbuild/build.bat --experimental-jit ${{ matrix.debug && '-d' || '' }} -p ${{ matrix.architecture }} ./PCbuild/rt.bat ${{ matrix.debug && '-d' || '' }} -p ${{ matrix.architecture }} -q --multiprocess 0 --timeout 4500 --verbose2 --verbose3 - # The `find` line is required as a result of https://github.com/actions/runner-images/issues/9966. - # This is a bug in the macOS runner image where the pre-installed Python is installed in the same - # directory as the Homebrew Python, which causes the build to fail for macos-13. This line removes - # the symlink to the pre-installed Python so that the Homebrew Python is used instead. - name: macOS if: runner.os == 'macOS' run: | brew update - find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete brew install llvm@${{ matrix.llvm }} export SDKROOT="$(xcrun --show-sdk-path)" # Set MACOSX_DEPLOYMENT_TARGET and -Werror=unguarded-availability to diff --git a/.github/workflows/reusable-macos.yml b/.github/workflows/reusable-macos.yml index de0c4022136..87bcd5786e7 100644 --- a/.github/workflows/reusable-macos.yml +++ b/.github/workflows/reusable-macos.yml @@ -60,15 +60,15 @@ jobs: --prefix=/opt/python-dev \ --with-openssl="$(brew --prefix openssl@3.0)" - name: Build CPython - if : ${{ inputs.free-threading || inputs.os != 'macos-13' }} + if : ${{ inputs.free-threading || inputs.os != 'macos-15-intel' }} run: gmake -j8 - name: Build CPython for compiler warning check - if : ${{ !inputs.free-threading && inputs.os == 'macos-13' }} + if : ${{ !inputs.free-threading && inputs.os == 'macos-15-intel' }} run: set -o pipefail; gmake -j8 --output-sync 2>&1 | tee compiler_output_macos.txt - name: Display build info run: make pythoninfo - name: Check compiler warnings - if : ${{ !inputs.free-threading && inputs.os == 'macos-13' }} + if : ${{ !inputs.free-threading && inputs.os == 'macos-15-intel' }} run: >- python3 Tools/build/check_warnings.py --compiler-output-file-path=compiler_output_macos.txt diff --git a/.github/workflows/tail-call.yml b/.github/workflows/tail-call.yml index d4a84223b7b..e99e317182e 100644 --- a/.github/workflows/tail-call.yml +++ b/.github/workflows/tail-call.yml @@ -58,7 +58,7 @@ jobs: # runner: windows-2022 - target: x86_64-apple-darwin/clang architecture: x86_64 - runner: macos-13 + runner: macos-15-intel - target: aarch64-apple-darwin/clang architecture: aarch64 runner: macos-14 @@ -101,17 +101,10 @@ jobs: set LLVMInstallDir=C:\Program Files\LLVM ./PCbuild/build.bat --tail-call-interp -p ${{ matrix.architecture }} - # The `find` line is required as a result of https://github.com/actions/runner-images/issues/9966. - # This is a bug in the macOS runner image where the pre-installed Python is installed in the same - # directory as the Homebrew Python, which causes the build to fail for macos-13. This line removes - # the symlink to the pre-installed Python so that the Homebrew Python is used instead. - # Note: when a new LLVM is released, the homebrew installation directory changes, so the builds will fail. - # We either need to upgrade LLVM or change the directory being pointed to. - name: Native macOS (release) if: runner.os == 'macOS' run: | brew update - find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete brew install llvm@${{ matrix.llvm }} export SDKROOT="$(xcrun --show-sdk-path)" export PATH="/usr/local/opt/llvm@${{ matrix.llvm }}/bin:$PATH" From 18d4e2ecd4e7414ef2285de457fcf81108f36a21 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sat, 4 Oct 2025 00:52:45 +0300 Subject: [PATCH 008/373] gh-133210: Fix `test_types` with `--without-doc-strings` (#139548) --- Lib/test/test_types.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 9b0ae709d79..4595e7e5d3e 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -714,7 +714,10 @@ def call(part): def test_frame_locals_proxy_type(self): self.assertIsInstance(types.FrameLocalsProxyType, type) - self.assertIsInstance(types.FrameLocalsProxyType.__doc__, str) + if MISSING_C_DOCSTRINGS: + self.assertIsNone(types.FrameLocalsProxyType.__doc__) + else: + self.assertIsInstance(types.FrameLocalsProxyType.__doc__, str) self.assertEqual(types.FrameLocalsProxyType.__module__, 'builtins') self.assertEqual(types.FrameLocalsProxyType.__name__, 'FrameLocalsProxy') From 37d16f7f620d79af7db1c00c8d638122af889bf9 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Sat, 4 Oct 2025 06:09:37 +0100 Subject: [PATCH 009/373] Replace workflow with project automations (#136831) Remove unnecessary workflow --- .github/workflows/project-updater.yml | 31 --------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .github/workflows/project-updater.yml diff --git a/.github/workflows/project-updater.yml b/.github/workflows/project-updater.yml deleted file mode 100644 index 1d9d637ec84..00000000000 --- a/.github/workflows/project-updater.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Update GH projects - -on: - issues: - types: - - opened - - labeled - -permissions: - contents: read - -jobs: - add-to-project: - name: Add issues to projects - runs-on: ubuntu-latest - timeout-minutes: 10 - strategy: - fail-fast: false - matrix: - include: - # if an issue has any of these labels, it will be added - # to the corresponding project - - { project: 2, label: "release-blocker, deferred-blocker" } - - { project: 32, label: sprint } - - steps: - - uses: actions/add-to-project@v1.0.0 - with: - project-url: https://github.com/orgs/python/projects/${{ matrix.project }} - github-token: ${{ secrets.ADD_TO_PROJECT_PAT }} - labeled: ${{ matrix.label }} From c33dc154b4d219a696f3fbe8965a38907c371780 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Sat, 4 Oct 2025 13:58:51 +0100 Subject: [PATCH 010/373] GH-123299: Copyedit 3.14 What's New: New and Improved Modules (#139530) --- Doc/library/compression.rst | 2 + Doc/whatsnew/3.14.rst | 450 ++++++++++++++++++++++-------------- 2 files changed, 274 insertions(+), 178 deletions(-) diff --git a/Doc/library/compression.rst b/Doc/library/compression.rst index 618b4a3c2bd..98719be9992 100644 --- a/Doc/library/compression.rst +++ b/Doc/library/compression.rst @@ -1,6 +1,8 @@ The :mod:`!compression` package =============================== +.. module:: compression + .. versionadded:: 3.14 The :mod:`!compression` package contains the canonical compression modules diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 3e204eafac8..647b20bf6a9 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1212,10 +1212,30 @@ Default interactive shell New modules =========== -* :mod:`annotationlib`: For introspecting :term:`annotations `. - See :pep:`749` for more details. +* :mod:`annotationlib`: + For introspecting :term:`annotations `. + See :ref:`PEP 749 ` for more details. (Contributed by Jelle Zijlstra in :gh:`119180`.) +* :mod:`compression` (including :mod:`compression.zstd`): + A package for compression-related modules, + including a new module to support the Zstandard compression format. + See :ref:`PEP 784 ` for more details. + (Contributed by Emma Harper Smith, Adam Turner, Gregory P. Smith, Tomas Roun, + Victor Stinner, and Rogdham in :gh:`132983`.) + +* :mod:`concurrent.interpreters`: + Support for multiple interpreters in the standard library. + See :ref:`PEP 734 ` for more details. + (Contributed by Eric Snow in :gh:`134939`.) + +* :mod:`string.templatelib`: + Support for template string literals (t-strings). + See :ref:`PEP 750 ` for more details. + (Contributed by Jim Baker, Guido van Rossum, Paul Everitt, Koudai Aono, + Lysandros Nikolaou, Dave Peck, Adam Turner, Jelle Zijlstra, Bénédikt Tran, + and Pablo Galindo Salgado in :gh:`132661`.) + Improved modules ================ @@ -1233,7 +1253,7 @@ argparse and subparser names if mistyped by the user. (Contributed by Savannah Ostrowski in :gh:`124456`.) - .. _whatsnew314-color-argparse: +.. _whatsnew314-color-argparse: * Enable color for help text, which can be disabled with the optional *color* parameter to :class:`argparse.ArgumentParser`. @@ -1245,7 +1265,7 @@ argparse ast --- -* Add :func:`ast.compare` for comparing two ASTs. +* Add :func:`~ast.compare`, a function for comparing two ASTs. (Contributed by Batuhan Taskaya and Jeremy Hylton in :gh:`60191`.) * Add support for :func:`copy.replace` for AST nodes. @@ -1254,15 +1274,17 @@ ast * Docstrings are now removed from an optimized AST in optimization level 2. (Contributed by Irit Katriel in :gh:`123958`.) -* The ``repr()`` output for AST nodes now includes more information. +* The :func:`repr` output for AST nodes now includes more information. (Contributed by Tomas Roun in :gh:`116022`.) -* :func:`ast.parse`, when called with an AST as input, now always verifies - that the root node type is appropriate. +* When called with an AST as input, the :func:`~ast.parse` function + now always verifies that the root node type is appropriate. (Contributed by Irit Katriel in :gh:`130139`.) -* Add new ``--feature-version``, ``--optimize``, ``--show-empty`` options to - the command-line interface. +* Add new options to the command-line interface: + :option:`--feature-version `, + :option:`--optimize `, and + :option:`--show-empty `. (Contributed by Semyon Moroz in :gh:`133367`.) @@ -1281,20 +1303,23 @@ asyncio :meth:`asyncio.create_task`, :meth:`asyncio.loop.create_task`, :meth:`asyncio.TaskGroup.create_task`. + (Contributed by Thomas Grainger in :gh:`128307`.) * There are two new utility functions for introspecting and printing a program's call graph: :func:`~asyncio.capture_call_graph` and :func:`~asyncio.print_call_graph`. + See :ref:`Asyncio introspection capabilities + ` for more details. (Contributed by Yury Selivanov, Pablo Galindo Salgado, and Łukasz Langa in :gh:`91048`.) - .. _whatsnew314-color-calendar: - calendar -------- +.. _whatsnew314-color-calendar: + * By default, today's date is highlighted in color in :mod:`calendar`'s :ref:`command-line ` text output. This can be controlled by :ref:`environment variables @@ -1307,10 +1332,14 @@ concurrent.futures .. _whatsnew314-concurrent-futures-interp-pool: -* Add :class:`~concurrent.futures.InterpreterPoolExecutor`, - which exposes "subinterpreters" (multiple Python interpreters in the - same process) to Python code. This is separate from the proposed API - in :pep:`734`. +* Add a new executor class, :class:`~concurrent.futures.InterpreterPoolExecutor`, + which exposes multiple Python interpreters in the same process + ('subinterpreters') to Python code. + This uses a pool of independent Python interpreters to execute calls + asynchronously. + + This is separate from the new :mod:`~concurrent.interpreters` module + introduced by :ref:`PEP 734 `. (Contributed by Eric Snow in :gh:`124548`.) .. _whatsnew314-concurrent-futures-start-method: @@ -1334,31 +1363,36 @@ concurrent.futures (Contributed by Gregory P. Smith in :gh:`84559`.) -* Add :meth:`concurrent.futures.ProcessPoolExecutor.terminate_workers` and - :meth:`concurrent.futures.ProcessPoolExecutor.kill_workers` as - ways to terminate or kill all living worker processes in the given pool. +* Add two new methods to :class:`~concurrent.futures.ProcessPoolExecutor`, + :meth:`~concurrent.futures.ProcessPoolExecutor.terminate_workers` + and :meth:`~concurrent.futures.ProcessPoolExecutor.kill_workers`, + as ways to terminate or kill all living worker processes in the given pool. (Contributed by Charles Machalow in :gh:`130849`.) -* Add the optional ``buffersize`` parameter to - :meth:`concurrent.futures.Executor.map` to limit the number of submitted +* Add the optional *buffersize* parameter to :meth:`Executor.map + ` to limit the number of submitted tasks whose results have not yet been yielded. If the buffer is full, iteration over the *iterables* pauses until a result is yielded from the buffer. (Contributed by Enzo Bonnal and Josh Rosenberg in :gh:`74028`.) + configparser ------------ -* Security fix: will no longer write config files it cannot read. Attempting - to :meth:`configparser.ConfigParser.write` keys containing delimiters or - beginning with the section header pattern will raise a - :class:`configparser.InvalidWriteError`. +* :mod:`!configparser` will no longer write config files it cannot read, + to improve security. + Attempting to :meth:`~configparser.ConfigParser.write` keys containing + delimiters or beginning with the section header pattern will raise an + :class:`~configparser.InvalidWriteError`. (Contributed by Jacob Lincoln in :gh:`129270`.) + contextvars ----------- -* Support context manager protocol by :class:`contextvars.Token`. +* Support the :term:`context manager` protocol + for :class:`~contextvars.Token` objects. (Contributed by Andrew Svetlov in :gh:`129889`.) @@ -1366,8 +1400,8 @@ ctypes ------ * The layout of :ref:`bit fields ` - in :class:`~ctypes.Structure` and :class:`~ctypes.Union` - now matches platform defaults (GCC/Clang or MSVC) more closely. + in :class:`~ctypes.Structure` and :class:`~ctypes.Union` objects + is now a closer match to platform defaults (GCC/Clang or MSVC). In particular, fields no longer overlap. (Contributed by Matthias Görgens in :gh:`97702`.) @@ -1386,7 +1420,7 @@ ctypes * On Windows, the :func:`~ctypes.CopyComPointer` function is now public. (Contributed by Jun Komoda in :gh:`127275`.) -* :func:`ctypes.memoryview_at` now exists to create a +* Add :func:`~ctypes.memoryview_at`, a function to create a :class:`memoryview` object that refers to the supplied pointer and length. This works like :func:`ctypes.string_at` except it avoids a buffer copy, and is typically useful when implementing pure Python @@ -1394,7 +1428,7 @@ ctypes (Contributed by Rian Hunter in :gh:`112018`.) * Complex types, :class:`~ctypes.c_float_complex`, - :class:`~ctypes.c_double_complex` and :class:`~ctypes.c_longdouble_complex`, + :class:`~ctypes.c_double_complex`, and :class:`~ctypes.c_longdouble_complex`, are now available if both the compiler and the ``libffi`` library support complex C types. (Contributed by Sergey B Kirpichev in :gh:`61103`.) @@ -1404,50 +1438,57 @@ ctypes (Contributed by Brian Ward in :gh:`119349`.) * Move :func:`ctypes.POINTER` types cache from a global internal cache - (``_pointer_type_cache``) to the :attr:`ctypes._CData.__pointer_type__` - attribute of the corresponding :mod:`ctypes` types. + (``_pointer_type_cache``) to the :attr:`_CData.__pointer_type__ + ` attribute of the corresponding + :mod:`!ctypes` types. This will stop the cache from growing without limits in some situations. (Contributed by Sergey Miryanov in :gh:`100926`.) -* The :class:`ctypes.py_object` type now supports subscription, +* The :class:`~ctypes.py_object` type now supports subscription, making it a :term:`generic type`. (Contributed by Brian Schubert in :gh:`132168`.) -* :mod:`ctypes` now supports :term:`free-threading builds `. +* :mod:`!ctypes` now supports :term:`free-threading builds `. (Contributed by Kumar Aditya and Peter Bierma in :gh:`127945`.) + curses ------ * Add the :func:`~curses.assume_default_colors` function, a refinement of the :func:`~curses.use_default_colors` function which - allows to change the color pair ``0``. + allows changing the color pair ``0``. (Contributed by Serhiy Storchaka in :gh:`133139`.) + datetime -------- -* Add :meth:`datetime.time.strptime` and :meth:`datetime.date.strptime`. +* Add the :meth:`~datetime.date.strptime` method to the + :class:`datetime.date` and :class:`datetime.time` classes. (Contributed by Wannes Boeykens in :gh:`41431`.) + decimal ------- -* Add alternative :class:`~decimal.Decimal` constructor - :meth:`Decimal.from_number() `. +* Add :meth:`.Decimal.from_number` as an alternative constructor for + :class:`~decimal.Decimal`. (Contributed by Serhiy Storchaka in :gh:`121798`.) -* Expose :func:`decimal.IEEEContext` to support creation of contexts +* Expose :func:`~decimal.IEEEContext` to support creation of contexts corresponding to the IEEE 754 (2008) decimal interchange formats. (Contributed by Sergey B Kirpichev in :gh:`53032`.) + difflib ------- * Comparison pages with highlighted changes generated by the - :class:`difflib.HtmlDiff` class now support dark mode. + :class:`~difflib.HtmlDiff` class now support 'dark mode'. (Contributed by Jiahao Li in :gh:`129939`.) + dis --- @@ -1472,7 +1513,7 @@ dis errno ----- -* Add :data:`errno.EHWPOISON` error code. +* Add the :data:`~errno.EHWPOISON` error code constant. (Contributed by James Roy in :gh:`126585`.) @@ -1480,39 +1521,43 @@ faulthandler ------------ * Add support for printing the C stack trace on systems that - :ref:`support it ` via :func:`faulthandler.dump_c_stack` - or via the *c_stack* argument in :func:`faulthandler.enable`. + :ref:`support it ` via the new + :func:`~faulthandler.dump_c_stack` function or via the *c_stack* argument + in :func:`faulthandler.enable`. (Contributed by Peter Bierma in :gh:`127604`.) fnmatch ------- -* Added :func:`fnmatch.filterfalse` for excluding names matching a pattern. +* Add :func:`~fnmatch.filterfalse`, a function to reject names + matching a given pattern. (Contributed by Bénédikt Tran in :gh:`74598`.) fractions --------- -* Add support for converting any objects that have the - :meth:`!as_integer_ratio` method to a :class:`~fractions.Fraction`. +* A :class:`~fractions.Fraction` object may now be constructed from any + object with the :meth:`!as_integer_ratio` method. (Contributed by Serhiy Storchaka in :gh:`82017`.) -* Add alternative :class:`~fractions.Fraction` constructor - :meth:`Fraction.from_number() `. +* Add :meth:`.Fraction.from_number` as an alternative constructor for + :class:`~fractions.Fraction`. (Contributed by Serhiy Storchaka in :gh:`121797`.) functools --------- -* Add support to :func:`functools.partial` and - :func:`functools.partialmethod` for :data:`functools.Placeholder` sentinels - to reserve a place for positional arguments. +* Add the :data:`~functools.Placeholder` sentinel. + This may be used with the :func:`~functools.partial` + or :func:`~functools.partialmethod` functions to reserve a place + for positional arguments in the returned :ref:`partial object + `. (Contributed by Dominykas Grigonis in :gh:`119127`.) -* Allow the *initial* parameter of :func:`functools.reduce` to be passed +* Allow the *initial* parameter of :func:`~functools.reduce` to be passed as a keyword argument. (Contributed by Sayandip Dutta in :gh:`125916`.) @@ -1530,16 +1575,17 @@ getopt getpass ------- -* Support keyboard feedback by :func:`getpass.getpass` via the keyword-only - optional argument ``echo_char``. Placeholder characters are rendered whenever - a character is entered, and removed when a character is deleted. +* Support keyboard feedback in the :func:`~getpass.getpass` function via + the keyword-only optional argument *echo_char*. + Placeholder characters are rendered whenever a character is entered, + and removed when a character is deleted. (Contributed by Semyon Moroz in :gh:`77065`.) graphlib -------- -* Allow :meth:`graphlib.TopologicalSorter.prepare` to be called more than once +* Allow :meth:`.TopologicalSorter.prepare` to be called more than once as long as sorting has not started. (Contributed by Daniel Pope in :gh:`130914`.) @@ -1547,13 +1593,14 @@ graphlib heapq ----- -* Add functions for working with max-heaps: +* The :mod:`!heapq` module has improved support for working with max-heaps, + via the following new functions: - * :func:`heapq.heapify_max`, - * :func:`heapq.heappush_max`, - * :func:`heapq.heappop_max`, - * :func:`heapq.heapreplace_max` - * :func:`heapq.heappushpop_max` + * :func:`~heapq.heapify_max` + * :func:`~heapq.heappush_max` + * :func:`~heapq.heappop_max` + * :func:`~heapq.heapreplace_max` + * :func:`~heapq.heappushpop_max` hmac @@ -1578,9 +1625,12 @@ http the command-line interface (``python -m http.server``) through the following options: - * ``--tls-cert ``: Path to the TLS certificate file. - * ``--tls-key ``: Optional path to the private key file. - * ``--tls-password-file ``: Optional path to the password file for the private key. + * :option:`--tls-cert \ `: + Path to the TLS certificate file. + * :option:`--tls-key \ `: + Optional path to the private key file. + * :option:`--tls-password-file \ `: + Optional path to the password file for the private key. (Contributed by Semyon Moroz in :gh:`85162`.) @@ -1588,7 +1638,7 @@ http imaplib ------- -* Add :meth:`IMAP4.idle() `, implementing the IMAP4 +* Add :meth:`.IMAP4.idle`, implementing the IMAP4 ``IDLE`` command as defined in :rfc:`2177`. (Contributed by Forest in :gh:`55454`.) @@ -1596,15 +1646,16 @@ imaplib inspect ------- -* :func:`inspect.signature` takes a new argument *annotation_format* to control +* :func:`~inspect.signature` takes a new argument *annotation_format* to control the :class:`annotationlib.Format` used for representing annotations. (Contributed by Jelle Zijlstra in :gh:`101552`.) -* :meth:`inspect.Signature.format` takes a new argument *unquote_annotations*. - If true, string :term:`annotations ` are displayed without surrounding quotes. +* :meth:`.Signature.format` takes a new argument *unquote_annotations*. + If true, string :term:`annotations ` are displayed without + surrounding quotes. (Contributed by Jelle Zijlstra in :gh:`101552`.) -* Add function :func:`inspect.ispackage` to determine whether an object is a +* Add function :func:`~inspect.ispackage` to determine whether an object is a :term:`package` or not. (Contributed by Zhikang Yan in :gh:`125634`.) @@ -1616,7 +1667,7 @@ io :exc:`BlockingIOError` if the operation cannot immediately return bytes. (Contributed by Giovanni Siragusa in :gh:`109523`.) -* Add protocols :class:`io.Reader` and :class:`io.Writer` as simpler +* Add the :class:`~io.Reader` and :class:`~io.Writer` protocols as simpler alternatives to the pseudo-protocols :class:`typing.IO`, :class:`typing.TextIO`, and :class:`typing.BinaryIO`. (Contributed by Sebastian Rittau in :gh:`127648`.) @@ -1625,16 +1676,18 @@ io json ---- -* Add notes for JSON serialization errors that allow to identify the source - of the error. +* Add exception notes for JSON serialization errors that allow + identifying the source of the error. (Contributed by Serhiy Storchaka in :gh:`122163`.) -* Enable the :mod:`json` module to work as a script using the :option:`-m` - switch: :program:`python -m json`. +* Allow using the :mod:`json` module as a script using the :option:`-m` switch: + :program:`python -m json`. + This is now preferred to :program:`python -m json.tool`, + which is :term:`soft deprecated`. See the :ref:`JSON command-line interface ` documentation. (Contributed by Trey Hunner in :gh:`122873`.) - .. _whatsnew314-color-json: +.. _whatsnew314-color-json: * By default, the output of the :ref:`JSON command-line interface ` is highlighted in color. @@ -1642,18 +1695,19 @@ json `. (Contributed by Tomas Roun in :gh:`131952`.) + linecache --------- -* :func:`linecache.getline` can retrieve source code for frozen modules. +* :func:`~linecache.getline` can now retrieve source code for frozen modules. (Contributed by Tian Gao in :gh:`131638`.) logging.handlers ---------------- -* :class:`logging.handlers.QueueListener` now implements the context - manager protocol, allowing it to be used in a :keyword:`with` statement. +* :class:`~logging.handlers.QueueListener` objects now support the + :term:`context manager` protocol. (Contributed by Charles Machalow in :gh:`132106`.) * :meth:`QueueListener.start ` now @@ -1671,14 +1725,13 @@ math mimetypes --------- -* Document the command-line for :mod:`mimetypes`. - It now exits with ``1`` on failure instead of ``0`` - and ``2`` on incorrect command-line parameters instead of ``1``. - Also, errors are printed to stderr instead of stdout and their text is made - tighter. +* Add a public :ref:`command-line ` for the module, + invoked via :program:`python -m mimetypes`. (Contributed by Oleg Iarygin and Hugo van Kemenade in :gh:`93096`.) -* Add MS and :rfc:`8081` MIME types for fonts: +* Add several new MIME types based on RFCs and common usage: + + .. rubric:: Microsoft and :rfc:`8081` MIME types for fonts * Embedded OpenType: ``application/vnd.ms-fontobject`` * OpenType Layout (OTF) ``font/otf`` @@ -1686,18 +1739,14 @@ mimetypes * WOFF 1.0 ``font/woff`` * WOFF 2.0 ``font/woff2`` - (Contributed by Sahil Prajapati and Hugo van Kemenade in :gh:`84852`.) - -* Add :rfc:`9559` MIME types for Matroska audiovisual data container - structures, containing: + .. rubric:: :rfc:`9559` MIME types for Matroska audiovisual + data container structures * audio with no video: ``audio/matroska`` (``.mka``) * video: ``video/matroska`` (``.mkv``) * stereoscopic video: ``video/matroska-3d`` (``.mk3d``) - (Contributed by Hugo van Kemenade in :gh:`89416`.) - -* Add MIME types for images with RFCs: + .. rubric:: Images with RFCs * :rfc:`1494`: CCITT Group 3 (``.g3``) * :rfc:`3362`: Real-time Facsimile, T.38 (``.t38``) @@ -1706,9 +1755,7 @@ mimetypes * :rfc:`4047`: Flexible Image Transport System (``.fits``) * :rfc:`7903`: Enhanced Metafile (``.emf``) and Windows Metafile (``.wmf``) - (Contributed by Hugo van Kemenade in :gh:`85957`.) - -* More MIME type changes: + .. rubric:: Other MIME type additions and changes * :rfc:`2361`: Change type for ``.avi`` to ``video/vnd.avi`` and for ``.wav`` to ``audio/vnd.wave`` @@ -1716,6 +1763,8 @@ mimetypes * :rfc:`5334`: Add Ogg media (``.oga``, ``.ogg`` and ``.ogx``) * :rfc:`6713`: Add gzip ``application/gzip`` (``.gz``) * :rfc:`9639`: Add FLAC ``audio/flac`` (``.flac``) + * :rfc:`9512` ``application/yaml`` MIME type for YAML files (``.yaml`` + and ``.yml``) * Add 7z ``application/x-7z-compressed`` (``.7z``) * Add Android Package ``application/vnd.android.package-archive`` (``.apk``) when not strict @@ -1738,11 +1787,9 @@ mimetypes * `W3C `__: Add EPUB ``application/epub+zip`` (``.epub``) - (Contributed by Hugo van Kemenade in :gh:`129965`.) - -* Add :rfc:`9512` ``application/yaml`` MIME type for YAML files (``.yaml`` - and ``.yml``). (Contributed by Sasha "Nelie" Chernykh and Hugo van Kemenade - in :gh:`132056`.) + (Contributed by Sahil Prajapati and Hugo van Kemenade in :gh:`84852`, + by Sasha "Nelie" Chernykh and Hugo van Kemenade in :gh:`132056`, + and by Hugo van Kemenade in :gh:`89416`, :gh:`85957`, and :gh:`129965`.) multiprocessing @@ -1758,8 +1805,8 @@ multiprocessing ` remains the default start method. If the threading incompatible *fork* method is required, you must explicitly - request it via a context from :func:`multiprocessing.get_context` (preferred) - or change the default via :func:`multiprocessing.set_start_method`. + request it via a context from :func:`~multiprocessing.get_context` (preferred) + or change the default via :func:`~multiprocessing.set_start_method`. See :ref:`forkserver restrictions ` for information and differences with the *fork* method and how this change @@ -1768,7 +1815,7 @@ multiprocessing (Contributed by Gregory P. Smith in :gh:`84559`.) -* :mod:`multiprocessing`'s ``"forkserver"`` start method now authenticates +* :mod:`multiprocessing`'s ``'forkserver'`` start method now authenticates its control socket to avoid solely relying on filesystem permissions to restrict what other processes could cause the forkserver to spawn workers and run code. @@ -1784,20 +1831,22 @@ multiprocessing (Contributed by Roy Hyunjin Han for :gh:`103134`.) * Add support for shared :class:`set` objects via - :meth:`SyncManager.set() `. - The :func:`set` in :func:`multiprocessing.Manager` method is now available. + :meth:`.SyncManager.set`. + The :func:`set` in :func:`~multiprocessing.Manager` method is now available. (Contributed by Mingyu Park in :gh:`129949`.) -* Add :func:`multiprocessing.Process.interrupt` which terminates the child +* Add the :meth:`~multiprocessing.Process.interrupt` + to :class:`multiprocessing.Process` objects, which terminates the child process by sending :py:const:`~signal.SIGINT`. This enables :keyword:`finally` clauses to print a stack trace for the terminated process. (Contributed by Artem Pulkin in :gh:`131913`.) + operator -------- -* Two new functions :func:`operator.is_none` and :func:`operator.is_not_none` - have been added, such that ``operator.is_none(obj)`` is equivalent +* Add :func:`~operator.is_none` and :func:`~operator.is_not_none` as a pair + of functions, such that ``operator.is_none(obj)`` is equivalent to ``obj is None`` and ``operator.is_not_none(obj)`` is equivalent to ``obj is not None``. (Contributed by Raymond Hettinger and Nico Mexis in :gh:`115808`.) @@ -1806,17 +1855,17 @@ operator os -- -* Add the :func:`os.reload_environ` function to update :data:`os.environ` and +* Add the :func:`~os.reload_environ` function to update :data:`os.environ` and :data:`os.environb` with changes to the environment made by :func:`os.putenv`, by :func:`os.unsetenv`, or made outside Python in the same process. (Contributed by Victor Stinner in :gh:`120057`.) * Add the :data:`~os.SCHED_DEADLINE` and :data:`~os.SCHED_NORMAL` constants - to the :mod:`os` module. + to the :mod:`!os` module. (Contributed by James Roy in :gh:`127688`.) -* Add the :func:`os.readinto` function to read into a +* Add the :func:`~os.readinto` function to read into a :ref:`buffer object ` from a file descriptor. (Contributed by Cody Maloney in :gh:`129205`.) @@ -1824,8 +1873,8 @@ os os.path ------- -* The *strict* parameter to :func:`os.path.realpath` accepts a new value, - :data:`os.path.ALLOW_MISSING`. +* The *strict* parameter to :func:`~os.path.realpath` accepts a new value, + :data:`~os.path.ALLOW_MISSING`. If used, errors other than :exc:`FileNotFoundError` will be re-raised; the resulting path can be missing but it will be free of symlinks. (Contributed by Petr Viktorin for :cve:`2025-4517`.) @@ -1844,8 +1893,8 @@ pathlib (Contributed by Barney Gale in :gh:`73991`.) -* Add :attr:`pathlib.Path.info` attribute, which stores an object - implementing the :class:`pathlib.types.PathInfo` protocol (also new). The +* Add the :attr:`~pathlib.Path.info` attribute, which stores an object + implementing the new :class:`pathlib.types.PathInfo` protocol. The object supports querying the file type and internally caching :func:`~os.stat` results. Path objects generated by :meth:`~pathlib.Path.iterdir` are initialized with file type information @@ -1856,7 +1905,7 @@ pathlib pdb --- -* Hardcoded breakpoints (:func:`breakpoint` and :func:`pdb.set_trace`) now +* Hardcoded breakpoints (:func:`breakpoint` and :func:`~pdb.set_trace`) now reuse the most recent :class:`~pdb.Pdb` instance that calls :meth:`~pdb.Pdb.set_trace`, instead of creating a new one each time. As a result, all the instance specific data like :pdbcmd:`display` and @@ -1906,15 +1955,16 @@ pickle * Set the default protocol version on the :mod:`pickle` module to 5. For more details, see :ref:`pickle protocols `. -* Add notes for pickle serialization errors that allow to identify the source - of the error. +* Add exception notes for pickle serialization errors that allow + identifying the source of the error. (Contributed by Serhiy Storchaka in :gh:`122213`.) platform -------- -* Add :func:`platform.invalidate_caches` to invalidate the cached results. +* Add :func:`~platform.invalidate_caches`, a function to invalidate + cached results in the :mod:`!platform` module. (Contributed by Bénédikt Tran in :gh:`122549`.) @@ -1934,8 +1984,8 @@ re unlike ``\Z``, which has subtly different behavior. (Contributed by Serhiy Storchaka in :gh:`133306`.) -* ``\B`` in :mod:`regular expression ` now matches the empty input string. - Now it is always the opposite of ``\b``. +* ``\B`` in :mod:`regular expression ` now matches the empty input string, + meaning that it is now always the opposite of ``\b``. (Contributed by Serhiy Storchaka in :gh:`124130`.) @@ -1965,11 +2015,12 @@ socket * Add many new constants. (Contributed by Serhiy Storchaka in :gh:`132734`.) + ssl --- -* Indicate through :data:`ssl.HAS_PHA` whether the :mod:`ssl` module supports - TLSv1.3 post-handshake client authentication (PHA). +* Indicate through the :data:`~ssl.HAS_PHA` Boolean whether the :mod:`!ssl` + module supports TLSv1.3 post-handshake client authentication (PHA). (Contributed by Will Childs-Klein in :gh:`128036`.) @@ -1985,7 +2036,7 @@ struct symtable -------- -* Expose the following :class:`symtable.Symbol` methods: +* Expose the following :class:`~symtable.Symbol` methods: * :meth:`~symtable.Symbol.is_comp_cell` * :meth:`~symtable.Symbol.is_comp_iter` @@ -2000,28 +2051,41 @@ sys * The previously undocumented special function :func:`sys.getobjects`, which only exists in specialized builds of Python, may now return objects from other interpreters than the one it's called in. + (Contributed by Eric Snow in :gh:`125286`.) * Add :func:`sys._is_immortal` for determining if an object is :term:`immortal`. (Contributed by Peter Bierma in :gh:`128509`.) -* On FreeBSD, :data:`sys.platform` doesn't contain the major version anymore. +* On FreeBSD, :data:`sys.platform` no longer contains the major version number. It is always ``'freebsd'``, instead of ``'freebsd13'`` or ``'freebsd14'``. + (Contributed by Michael Osipov in :gh:`129393`.) * Raise :exc:`DeprecationWarning` for :func:`sys._clear_type_cache`. This function was deprecated in Python 3.13 but it didn't raise a runtime warning. +* Add :func:`sys.remote_exec` to implement the new external debugger interface. + See :ref:`PEP 768 ` for details. + (Contributed by Pablo Galindo Salgado, Matt Wozniski, and Ivona Stojanovic + in :gh:`131591`.) + +* Add the :data:`sys._jit` namespace, containing utilities for introspecting + just-in-time compilation. + (Contributed by Brandt Bucher in :gh:`133231`.) + sys.monitoring -------------- -* Two new events are added: :monitoring-event:`BRANCH_LEFT` and - :monitoring-event:`BRANCH_RIGHT`. The ``BRANCH`` event is deprecated. +* Add two new monitoring events, :monitoring-event:`BRANCH_LEFT` and + :monitoring-event:`BRANCH_RIGHT`. + These replace and deprecate the :monitoring-event:`!BRANCH` event. + (Contributed by Mark Shannon in :gh:`122548`.) sysconfig --------- -* Add ``ABIFLAGS`` key to :func:`sysconfig.get_config_vars` on Windows. +* Add ``ABIFLAGS`` key to :func:`~sysconfig.get_config_vars` on Windows. (Contributed by Xuehai Pan in :gh:`131799`.) @@ -2031,15 +2095,18 @@ tarfile * :func:`~tarfile.data_filter` now normalizes symbolic link targets in order to avoid path traversal attacks. (Contributed by Petr Viktorin in :gh:`127987` and :cve:`2025-4138`.) + * :func:`~tarfile.TarFile.extractall` now skips fixing up directory attributes when a directory was removed or replaced by another kind of file. (Contributed by Petr Viktorin in :gh:`127987` and :cve:`2024-12718`.) + * :func:`~tarfile.TarFile.extract` and :func:`~tarfile.TarFile.extractall` now (re-)apply the extraction filter when substituting a link (hard or symbolic) with a copy of another archive member, and when fixing up directory attributes. The former raises a new exception, :exc:`~tarfile.LinkFallbackError`. (Contributed by Petr Viktorin for :cve:`2025-4330` and :cve:`2024-12718`.) + * :func:`~tarfile.TarFile.extract` and :func:`~tarfile.TarFile.extractall` no longer extract rejected members when :func:`~tarfile.TarFile.errorlevel` is zero. @@ -2059,17 +2126,18 @@ tkinter ------- * Make :mod:`tkinter` widget methods :meth:`!after` and :meth:`!after_idle` - accept arguments passed by keyword. + accept keyword arguments. (Contributed by Zhikang Yan in :gh:`126899`.) -* Add ability to specify name for :class:`!tkinter.OptionMenu` and +* Add ability to specify a name for :class:`!tkinter.OptionMenu` and :class:`!tkinter.ttk.OptionMenu`. (Contributed by Zhikang Yan in :gh:`130482`.) + turtle ------ -* Add context managers for :func:`turtle.fill`, :func:`turtle.poly` +* Add context managers for :func:`turtle.fill`, :func:`turtle.poly`, and :func:`turtle.no_animation`. (Contributed by Marie Roald and Yngve Mardal Moe in :gh:`126350`.) @@ -2087,44 +2155,59 @@ typing .. _whatsnew314-typing-union: -* :class:`types.UnionType` and :class:`typing.Union` are now aliases for each other, - meaning that both old-style unions (created with ``Union[int, str]``) and new-style - unions (``int | str``) now create instances of the same runtime type. This unifies - the behavior between the two syntaxes, but leads to some differences in behavior that +* The :class:`types.UnionType` and :class:`typing.Union` types are now + aliases for each other, meaning that both old-style unions + (created with ``Union[int, str]``) and new-style unions (``int | str``) + now create instances of the same runtime type. This unifies the behavior + between the two syntaxes, but leads to some differences in behavior that may affect users who introspect types at runtime: - - Both syntaxes for creating a union now produce the same string representation in - ``repr()``. For example, ``repr(Union[int, str])`` - is now ``"int | str"`` instead of ``"typing.Union[int, str]"``. - - Unions created using the old syntax are no longer cached. Previously, running - ``Union[int, str]`` multiple times would return the same object - (``Union[int, str] is Union[int, str]`` would be ``True``), but now it will - return two different objects. Users should use ``==`` to compare unions for equality, not - ``is``. New-style unions have never been cached this way. - This change could increase memory usage for some programs that use a large number of - unions created by subscripting ``typing.Union``. However, several factors offset this cost: - unions used in annotations are no longer evaluated by default in Python 3.14 - because of :pep:`649`; an instance of :class:`types.UnionType` is - itself much smaller than the object returned by ``Union[]`` was on prior Python - versions; and removing the cache also saves some space. It is therefore - unlikely that this change will cause a significant increase in memory usage for most - users. + - Both syntaxes for creating a union now produce the same string + representation in :func:`repr`. + For example, ``repr(Union[int, str])`` is now ``"int | str"`` instead of + ``"typing.Union[int, str]"``. + + - Unions created using the old syntax are no longer cached. + Previously, running ``Union[int, str]`` multiple times would return + the same object (``Union[int, str] is Union[int, str]`` would be ``True``), + but now it will return two different objects. + Use ``==`` to compare unions for equality, not ``is``. + New-style unions have never been cached this way. + This change could increase memory usage for some programs that use + a large number of unions created by subscripting ``typing.Union``. + However, several factors offset this cost: + unions used in annotations are no longer evaluated by default in Python + 3.14 because of :pep:`649`; an instance of :class:`types.UnionType` is + itself much smaller than the object returned by ``Union[]`` was on prior + Python versions; and removing the cache also saves some space. + It is therefore unlikely that this change will cause a significant increase + in memory usage for most users. + - Previously, old-style unions were implemented using the private class - ``typing._UnionGenericAlias``. This class is no longer needed for the implementation, - but it has been retained for backward compatibility, with removal scheduled for Python - 3.17. Users should use documented introspection helpers like :func:`typing.get_origin` - and :func:`typing.get_args` instead of relying on private implementation details. - - It is now possible to use :class:`typing.Union` itself in :func:`isinstance` checks. - For example, ``isinstance(int | str, typing.Union)`` will return ``True``; previously - this raised :exc:`TypeError`. - - The ``__args__`` attribute of :class:`typing.Union` objects is no longer writable. - - It is no longer possible to set any attributes on :class:`typing.Union` objects. + ``typing._UnionGenericAlias``. + This class is no longer needed for the implementation, + but it has been retained for backward compatibility, + with removal scheduled for Python 3.17. + Users should use documented introspection helpers like + :func:`~typing.get_origin` and :func:`typing.get_args` instead of + relying on private implementation details. + + - It is now possible to use :class:`typing.Union` itself in + :func:`isinstance` checks. + For example, ``isinstance(int | str, typing.Union)`` will return ``True``; + previously this raised :exc:`TypeError`. + + - The :attr:`!__args__` attribute of :class:`typing.Union` objects is + no longer writable. + + - It is no longer possible to set any attributes on :class:`~typing.Union` + objects. This only ever worked for dunder attributes on previous versions, was never documented to work, and was subtly broken in many cases. (Contributed by Jelle Zijlstra in :gh:`105499`.) -* :class:`typing.TypeAliasType` now supports star unpacking. +* :class:`~typing.TypeAliasType` now supports star unpacking. unicodedata @@ -2133,11 +2216,11 @@ unicodedata * The Unicode database has been updated to Unicode 16.0.0. -.. _whatsnew314-color-unittest: - unittest -------- +.. _whatsnew314-color-unittest: + * :mod:`unittest` output is now colored by default. This can be controlled by :ref:`environment variables `. @@ -2175,7 +2258,7 @@ urllib * Improve ergonomics and standards compliance when parsing and emitting ``file:`` URLs. - In :func:`urllib.request.url2pathname`: + In :func:`~urllib.request.url2pathname`: - Accept a complete URL when the new *require_scheme* argument is set to true. @@ -2186,7 +2269,7 @@ urllib - Raise :exc:`~urllib.error.URLError` if a URL authority isn't local, except on Windows where we return a UNC path as before. - In :func:`urllib.request.pathname2url`: + In :func:`~urllib.request.pathname2url`: - Return a complete URL when the new *add_scheme* argument is set to true. - Include an empty URL authority when a path begins with a slash. For @@ -2202,16 +2285,17 @@ urllib uuid ---- -* Add support for UUID versions 6, 7, and 8 via :func:`uuid.uuid6`, - :func:`uuid.uuid7`, and :func:`uuid.uuid8` respectively, as specified +* Add support for UUID versions 6, 7, and 8 via :func:`~uuid.uuid6`, + :func:`~uuid.uuid7`, and :func:`~uuid.uuid8` respectively, as specified in :rfc:`9562`. (Contributed by Bénédikt Tran in :gh:`89083`.) -* :const:`uuid.NIL` and :const:`uuid.MAX` are now available to represent the +* :const:`~uuid.NIL` and :const:`~uuid.MAX` are now available to represent the Nil and Max UUID formats as defined by :rfc:`9562`. (Contributed by Nick Pope in :gh:`128427`.) -* Allow to generate multiple UUIDs at once via :option:`python -m uuid --count `. +* Allow generating multiple UUIDs simultaneously on the command-line via + :option:`python -m uuid --count `. (Contributed by Simon Legner in :gh:`131236`.) @@ -2229,14 +2313,13 @@ webbrowser zipfile ------- -* Added :func:`ZipInfo._for_archive ` +* Added :meth:`ZipInfo._for_archive `, a method to resolve suitable defaults for a :class:`~zipfile.ZipInfo` object as used by :func:`ZipFile.writestr `. (Contributed by Bénédikt Tran in :gh:`123424`.) -* :meth:`zipfile.ZipFile.writestr` now respects ``SOURCE_DATE_EPOCH`` that - distributions can set centrally and have build tools consume this in order - to produce reproducible output. +* :meth:`.ZipFile.writestr` now respects the :envvar:`SOURCE_DATE_EPOCH` + environment variable in order to better support reproducible builds. (Contributed by Jiahao Li in :gh:`91279`.) @@ -2256,17 +2339,18 @@ Optimizations (Contributed by Adam Turner, Bénédikt Tran, Chris Markiewicz, Eli Schwartz, Hugo van Kemenade, Jelle Zijlstra, and others in :gh:`118761`.) -* The interpreter avoids some reference count modifications internally when - it's safe to do so. This can lead to different values returned from - :func:`sys.getrefcount` and :c:func:`Py_REFCNT` compared to previous versions - of Python. See :ref:`below ` for details. +* The interpreter now avoids some reference count modifications internally + when it's safe to do so. + This can lead to different values being returned from :func:`sys.getrefcount` + and :c:func:`Py_REFCNT` compared to previous versions of Python. + See :ref:`below ` for details. asyncio ------- -* Standard benchmark results have improved by 10-20%, following the - implementation of a new per-thread double linked list +* Standard benchmark results have improved by 10-20% following the + implementation of a new per-thread doubly linked list for :class:`native tasks `, also reducing memory usage. This enables external introspection tools such as @@ -3267,6 +3351,16 @@ Changes in the Python API with annotations should continue to work, some undocumented details may behave differently. +* As part of making the :mod:`mimetypes` CLI public, + it now exits with ``1`` on failure instead of ``0`` + and ``2`` on incorrect command-line parameters instead of ``1``. + Error messages are now printed to stderr. + +* The ``\B`` pattern in regular expression now matches the empty string + when given as the entire pattern, which may cause behavioural changes. + +* On FreeBSD, :data:`sys.platform` no longer contains the major version number. + Changes in the C API -------------------- From db53ca30d761abba525bc8e47b16920b1fc43a83 Mon Sep 17 00:00:00 2001 From: Ho Kim Date: Sat, 4 Oct 2025 22:26:20 +0900 Subject: [PATCH 011/373] gh-138489: Add missing `build-details.json` step for building wasm (#139302) * fix: add missing `build-details.json` step for building wasm Signed-off-by: Ho Kim * gh-138489: Add missing build-details.json step for building wasm Signed-off-by: Ho Kim * Update Makefile.pre.in Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --------- Signed-off-by: Ho Kim Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Makefile.pre.in | 2 +- .../Build/2025-09-24-13-59-26.gh-issue-138489.1AcuZM.rst | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Build/2025-09-24-13-59-26.gh-issue-138489.1AcuZM.rst diff --git a/Makefile.pre.in b/Makefile.pre.in index 6651b093e20..37bde125166 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -801,7 +801,7 @@ build_all: check-clean-src check-app-store-compliance $(BUILDPYTHON) platform sh .PHONY: build_wasm build_wasm: check-clean-src $(BUILDPYTHON) platform sharedmods \ - python-config checksharedmods + python-config checksharedmods build-details.json .PHONY: build_emscripten build_emscripten: build_wasm web_example web_example_pyrepl_jspi diff --git a/Misc/NEWS.d/next/Build/2025-09-24-13-59-26.gh-issue-138489.1AcuZM.rst b/Misc/NEWS.d/next/Build/2025-09-24-13-59-26.gh-issue-138489.1AcuZM.rst new file mode 100644 index 00000000000..b11098a3f87 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2025-09-24-13-59-26.gh-issue-138489.1AcuZM.rst @@ -0,0 +1,6 @@ +When cross-compiling for WASI by ``build_wasm`` or ``build_emscripten``, the +``build-details.json`` step is now included in the build process, just like +with native builds. + +This fixes the ``libinstall`` task which requires the ``build-details.json`` +file during the process. From 8d17d79299ba3aad9f8fd2aded5ee776dc71668d Mon Sep 17 00:00:00 2001 From: Semyon Moroz Date: Sat, 4 Oct 2025 17:53:43 +0400 Subject: [PATCH 012/373] gh-138044: Remove deprecated parameter alias for `importlib.resources.files` (#138059) --- Doc/library/importlib.resources.rst | 13 ++++--- Lib/importlib/resources/_common.py | 34 ------------------- .../test_importlib/resources/test_files.py | 8 ----- ...-08-22-12-48-14.gh-issue-138044.lEQULC.rst | 2 ++ 4 files changed, 8 insertions(+), 49 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-08-22-12-48-14.gh-issue-138044.lEQULC.rst diff --git a/Doc/library/importlib.resources.rst b/Doc/library/importlib.resources.rst index 7a11f4fe069..8cb43f0625f 100644 --- a/Doc/library/importlib.resources.rst +++ b/Doc/library/importlib.resources.rst @@ -72,13 +72,12 @@ for example, a package and its resources can be imported from a zip file using .. versionadded:: 3.9 - .. versionchanged:: 3.12 - *package* parameter was renamed to *anchor*. *anchor* can now - be a non-package module and if omitted will default to the caller's - module. *package* is still accepted for compatibility but will raise - a :exc:`DeprecationWarning`. Consider passing the anchor positionally or - using ``importlib_resources >= 5.10`` for a compatible interface - on older Pythons. + .. deprecated-removed:: 3.12 3.15 + *package* parameter was renamed to *anchor*. *anchor* can now be a + non-package module and if omitted will default to the caller's module. + *package* is no longer accepted since Python 3.15. Consider passing the + anchor positionally or using ``importlib_resources >= 5.10`` for a + compatible interface on older Pythons. .. function:: as_file(traversable) diff --git a/Lib/importlib/resources/_common.py b/Lib/importlib/resources/_common.py index 4e9014c45a0..d16ebe4520f 100644 --- a/Lib/importlib/resources/_common.py +++ b/Lib/importlib/resources/_common.py @@ -6,7 +6,6 @@ import types import importlib import inspect -import warnings import itertools from typing import Union, Optional, cast @@ -16,39 +15,6 @@ Anchor = Package -def package_to_anchor(func): - """ - Replace 'package' parameter as 'anchor' and warn about the change. - - Other errors should fall through. - - >>> files('a', 'b') - Traceback (most recent call last): - TypeError: files() takes from 0 to 1 positional arguments but 2 were given - - Remove this compatibility in Python 3.14. - """ - undefined = object() - - @functools.wraps(func) - def wrapper(anchor=undefined, package=undefined): - if package is not undefined: - if anchor is not undefined: - return func(anchor, package) - warnings.warn( - "First parameter to files is renamed to 'anchor'", - DeprecationWarning, - stacklevel=2, - ) - return func(package) - elif anchor is undefined: - return func() - return func(anchor) - - return wrapper - - -@package_to_anchor def files(anchor: Optional[Anchor] = None) -> Traversable: """ Get a Traversable resource for an anchor. diff --git a/Lib/test/test_importlib/resources/test_files.py b/Lib/test/test_importlib/resources/test_files.py index 3ce44999f98..c935b1e10ac 100644 --- a/Lib/test/test_importlib/resources/test_files.py +++ b/Lib/test/test_importlib/resources/test_files.py @@ -38,14 +38,6 @@ def test_joinpath_with_multiple_args(self): binfile = files.joinpath('subdirectory', 'binary.file') self.assertTrue(binfile.is_file()) - def test_old_parameter(self): - """ - Files used to take a 'package' parameter. Make sure anyone - passing by name is still supported. - """ - with suppress_known_deprecation(): - resources.files(package=self.data) - class OpenDiskTests(FilesTests, util.DiskSetup, unittest.TestCase): pass diff --git a/Misc/NEWS.d/next/Library/2025-08-22-12-48-14.gh-issue-138044.lEQULC.rst b/Misc/NEWS.d/next/Library/2025-08-22-12-48-14.gh-issue-138044.lEQULC.rst new file mode 100644 index 00000000000..99ed3adef91 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-08-22-12-48-14.gh-issue-138044.lEQULC.rst @@ -0,0 +1,2 @@ +Remove compatibility shim for deprecated parameter *package* in +:func:`importlib.resources.files`. Patch by Semyon Moroz. From a7a485558c05a59665d24f9bc048db8a41daf532 Mon Sep 17 00:00:00 2001 From: Cycloctane Date: Sat, 4 Oct 2025 21:55:17 +0800 Subject: [PATCH 013/373] gh-133951: Remove lib64->lib symlink in venv creation (#137139) * Remove lib64->lib symlink in venv directory * fix test * remove unused import * add news --- Lib/test/test_venv.py | 10 ++-------- Lib/venv/__init__.py | 9 +++------ .../2025-07-27-17-03-17.gh-issue-133951.7kwt78.rst | 2 ++ 3 files changed, 7 insertions(+), 14 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-07-27-17-03-17.gh-issue-133951.7kwt78.rst diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 3c18c9c2900..d46b45e5437 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -12,7 +12,6 @@ import pathlib import re import shutil -import struct import subprocess import sys import sysconfig @@ -138,14 +137,9 @@ def _check_output_of_default_create(self): self.isdir(self.bindir) self.isdir(self.include) self.isdir(*self.lib) - # Issue 21197 p = self.get_env_file('lib64') - conditions = ((struct.calcsize('P') == 8) and (os.name == 'posix') and - (sys.platform != 'darwin')) - if conditions: - self.assertTrue(os.path.islink(p)) - else: - self.assertFalse(os.path.exists(p)) + if os.path.exists(p): + self.assertFalse(os.path.islink(p)) data = self.get_text_file_contents('pyvenv.cfg') executable = sys._base_executable path = os.path.dirname(executable) diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index dc9c5991df7..e5addcc393a 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -174,6 +174,7 @@ def create_if_needed(d): context.python_exe = exename binpath = self._venv_path(env_dir, 'scripts') libpath = self._venv_path(env_dir, 'purelib') + platlibpath = self._venv_path(env_dir, 'platlib') # PEP 405 says venvs should create a local include directory. # See https://peps.python.org/pep-0405/#include-files @@ -191,12 +192,8 @@ def create_if_needed(d): create_if_needed(incpath) context.lib_path = libpath create_if_needed(libpath) - # Issue 21197: create lib64 as a symlink to lib on 64-bit non-OS X POSIX - if ((sys.maxsize > 2**32) and (os.name == 'posix') and - (sys.platform != 'darwin')): - link_path = os.path.join(env_dir, 'lib64') - if not os.path.exists(link_path): # Issue #21643 - os.symlink('lib', link_path) + context.platlib_path = platlibpath + create_if_needed(platlibpath) context.bin_path = binpath context.bin_name = os.path.relpath(binpath, env_dir) context.env_exe = os.path.join(binpath, exename) diff --git a/Misc/NEWS.d/next/Library/2025-07-27-17-03-17.gh-issue-133951.7kwt78.rst b/Misc/NEWS.d/next/Library/2025-07-27-17-03-17.gh-issue-133951.7kwt78.rst new file mode 100644 index 00000000000..dfda8e8f10c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-07-27-17-03-17.gh-issue-133951.7kwt78.rst @@ -0,0 +1,2 @@ +Remove lib64-lib symlink creation when creating new virtual environments in +:mod:`venv` module From ae6e7f572c18d9183cb23e6e53c3324dd9bf0c64 Mon Sep 17 00:00:00 2001 From: Furkan Onder Date: Sat, 4 Oct 2025 13:56:43 +0000 Subject: [PATCH 014/373] gh-139308: Skip test_special_chars_csh on NetBSD due to csh variable expansion issue (#139341) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skip test_special_chars_csh on NetBSD due to csh variable expansion issue Co-authored-by: Filipe Laíns 🇵🇸 --- Lib/test/test_venv.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index d46b45e5437..68bcf535ead 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -516,6 +516,8 @@ def test_special_chars_bash(self): # gh-124651: test quoted strings @unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows') + @unittest.skipIf(sys.platform.startswith('netbsd'), + "NetBSD csh fails with quoted special chars; see gh-139308") def test_special_chars_csh(self): """ Test that the template strings are quoted properly (csh) From 9e3542a6c26a849012216f5f754f16043d775c42 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 4 Oct 2025 16:57:12 +0300 Subject: [PATCH 015/373] gh-136097: Fix sysconfig._parse_makefile() (#136166) * Fix potential infinite recursion. * Fix a bug when reference can cross boundaries of substitutions, e.g. a=$( b=$(a)a) * Fix potential quadratic complexity. * Fix KeyError for undefined CFLAGS, LDFLAGS, or CPPFLAGS. * Fix infinite recursion when keep_unresolved=False. * Unify behavior with keep_unresolved=False for bogus $ occurred before and after variable references. --- Lib/sysconfig/__main__.py | 148 ++++++++---------- Lib/test/test_sysconfig.py | 71 ++++++++- ...-07-01-14-44-03.gh-issue-136097.bI1n14.rst | 2 + 3 files changed, 133 insertions(+), 88 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-07-01-14-44-03.gh-issue-136097.bI1n14.rst diff --git a/Lib/sysconfig/__main__.py b/Lib/sysconfig/__main__.py index bc2197cfe79..0cf0cf4dbb9 100644 --- a/Lib/sysconfig/__main__.py +++ b/Lib/sysconfig/__main__.py @@ -21,8 +21,9 @@ # Regexes needed for parsing Makefile (and similar syntaxes, # like old-style Setup files). _variable_rx = r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)" -_findvar1_rx = r"\$\(([A-Za-z][A-Za-z0-9_]*)\)" -_findvar2_rx = r"\${([A-Za-z][A-Za-z0-9_]*)}" +_findvar_rx = (r"\$(\([A-Za-z][A-Za-z0-9_]*\)" + r"|\{[A-Za-z][A-Za-z0-9_]*\}" + r"|\$?)") def _parse_makefile(filename, vars=None, keep_unresolved=True): @@ -49,26 +50,7 @@ def _parse_makefile(filename, vars=None, keep_unresolved=True): m = re.match(_variable_rx, line) if m: n, v = m.group(1, 2) - v = v.strip() - # `$$' is a literal `$' in make - tmpv = v.replace('$$', '') - - if "$" in tmpv: - notdone[n] = v - else: - try: - if n in _ALWAYS_STR: - raise ValueError - - v = int(v) - except ValueError: - # insert literal `$' - done[n] = v.replace('$$', '$') - else: - done[n] = v - - # do variable interpolation here - variables = list(notdone.keys()) + notdone[n] = v.strip() # Variables with a 'PY_' prefix in the makefile. These need to # be made available without that prefix through sysconfig. @@ -76,72 +58,64 @@ def _parse_makefile(filename, vars=None, keep_unresolved=True): # if the expansion uses the name without a prefix. renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS') - while len(variables) > 0: - for name in tuple(variables): - value = notdone[name] - m1 = re.search(_findvar1_rx, value) - m2 = re.search(_findvar2_rx, value) - if m1 and m2: - m = m1 if m1.start() < m2.start() else m2 - else: - m = m1 if m1 else m2 - if m is not None: - n = m.group(1) - found = True - if n in done: - item = str(done[n]) - elif n in notdone: - # get it on a subsequent round - found = False - elif n in os.environ: - # do it like make: fall back to environment - item = os.environ[n] - - elif n in renamed_variables: - if (name.startswith('PY_') and - name[3:] in renamed_variables): - item = "" - - elif 'PY_' + n in notdone: - found = False - - else: - item = str(done['PY_' + n]) - - else: - done[n] = item = "" - - if found: - after = value[m.end():] - value = value[:m.start()] + item + after - if "$" in after: - notdone[name] = value - else: - try: - if name in _ALWAYS_STR: - raise ValueError - value = int(value) - except ValueError: - done[name] = value.strip() - else: - done[name] = value - variables.remove(name) - - if name.startswith('PY_') \ - and name[3:] in renamed_variables: - - name = name[3:] - if name not in done: - done[name] = value - - else: - # Adds unresolved variables to the done dict. - # This is disabled when called from distutils.sysconfig + def resolve_var(name): + def repl(m): + n = m[1] + if n == '$': + return '$' + elif n == '': + # bogus variable reference (e.g. "prefix=$/opt/python") if keep_unresolved: - done[name] = value - # bogus variable reference (e.g. "prefix=$/opt/python"); - # just drop it since we can't deal - variables.remove(name) + return m[0] + raise ValueError + elif n[0] == '(' and n[-1] == ')': + n = n[1:-1] + elif n[0] == '{' and n[-1] == '}': + n = n[1:-1] + + if n in done: + return str(done[n]) + elif n in notdone: + return str(resolve_var(n)) + elif n in os.environ: + # do it like make: fall back to environment + return os.environ[n] + elif n in renamed_variables: + if name.startswith('PY_') and name[3:] in renamed_variables: + return "" + n = 'PY_' + n + if n in notdone: + return str(resolve_var(n)) + else: + assert n not in done + return "" + else: + done[n] = "" + return "" + + assert name not in done + done[name] = "" + try: + value = re.sub(_findvar_rx, repl, notdone[name]) + except ValueError: + del done[name] + return "" + value = value.strip() + if name not in _ALWAYS_STR: + try: + value = int(value) + except ValueError: + pass + done[name] = value + if name.startswith('PY_') and name[3:] in renamed_variables: + name = name[3:] + if name not in done: + done[name] = value + return value + + for n in notdone: + if n not in done: + resolve_var(n) # strip spurious spaces for k, v in done.items(): diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index 6a0681a4148..59ea623bb92 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -764,8 +764,12 @@ def test_parse_makefile(self): print("var3=42", file=makefile) print("var4=$/invalid", file=makefile) print("var5=dollar$$5", file=makefile) - print("var6=${var3}/lib/python3.5/config-$(VAR2)$(var5)" + print("var6=${var7}/lib/python3.5/config-$(VAR2)$(var5)" "-x86_64-linux-gnu", file=makefile) + print("var7=${var3}", file=makefile) + print("var8=$$(var3)", file=makefile) + print("var9=$(var10)(var3)", file=makefile) + print("var10=$$", file=makefile) vars = _parse_makefile(TESTFN) self.assertEqual(vars, { 'var1': 'ab42', @@ -774,6 +778,71 @@ def test_parse_makefile(self): 'var4': '$/invalid', 'var5': 'dollar$5', 'var6': '42/lib/python3.5/config-b42dollar$5-x86_64-linux-gnu', + 'var7': 42, + 'var8': '$(var3)', + 'var9': '$(var3)', + 'var10': '$', + }) + + def _test_parse_makefile_recursion(self): + self.addCleanup(unlink, TESTFN) + with open(TESTFN, "w") as makefile: + print("var1=var1=$(var1)", file=makefile) + print("var2=var3=$(var3)", file=makefile) + print("var3=var2=$(var2)", file=makefile) + vars = _parse_makefile(TESTFN) + self.assertEqual(vars, { + 'var1': 'var1=', + 'var2': 'var3=var2=', + 'var3': 'var2=', + }) + + def test_parse_makefile_renamed_vars(self): + self.addCleanup(unlink, TESTFN) + with open(TESTFN, "w") as makefile: + print("var1=$(CFLAGS)", file=makefile) + print("PY_CFLAGS=-Wall $(CPPFLAGS)", file=makefile) + print("PY_LDFLAGS=-lm", file=makefile) + print("var2=$(LDFLAGS)", file=makefile) + print("var3=$(CPPFLAGS)", file=makefile) + vars = _parse_makefile(TESTFN) + self.assertEqual(vars, { + 'var1': '-Wall', + 'CFLAGS': '-Wall', + 'PY_CFLAGS': '-Wall', + 'LDFLAGS': '-lm', + 'PY_LDFLAGS': '-lm', + 'var2': '-lm', + 'var3': '', + }) + + def test_parse_makefile_keep_unresolved(self): + self.addCleanup(unlink, TESTFN) + with open(TESTFN, "w") as makefile: + print("var1=value", file=makefile) + print("var2=$/", file=makefile) + print("var3=$/$(var1)", file=makefile) + print("var4=var5=$(var5)", file=makefile) + print("var5=$/$(var1)", file=makefile) + print("var6=$(var1)$/", file=makefile) + print("var7=var8=$(var8)", file=makefile) + print("var8=$(var1)$/", file=makefile) + vars = _parse_makefile(TESTFN) + self.assertEqual(vars, { + 'var1': 'value', + 'var2': '$/', + 'var3': '$/value', + 'var4': 'var5=$/value', + 'var5': '$/value', + 'var6': 'value$/', + 'var7': 'var8=value$/', + 'var8': 'value$/', + }) + vars = _parse_makefile(TESTFN, keep_unresolved=False) + self.assertEqual(vars, { + 'var1': 'value', + 'var4': 'var5=', + 'var7': 'var8=', }) diff --git a/Misc/NEWS.d/next/Library/2025-07-01-14-44-03.gh-issue-136097.bI1n14.rst b/Misc/NEWS.d/next/Library/2025-07-01-14-44-03.gh-issue-136097.bI1n14.rst new file mode 100644 index 00000000000..209ca745a14 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-07-01-14-44-03.gh-issue-136097.bI1n14.rst @@ -0,0 +1,2 @@ +Fix potential infinite recursion and KeyError in ``sysconfig +--generate-posix-vars``. From fc48a3c9cac5b9e8c6a56ec2294b01a8da9f8dee Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Sat, 4 Oct 2025 16:19:06 +0200 Subject: [PATCH 016/373] gh-90949: Fix an "unused function" compiler warning introduced in GH-139234 (#139558) Fix a compiler warning `-Wunused-function` after f04bea44c37793561d753dd4ca6e7cd658137553. The `set_invalid_arg` function in `Modules/pyexpat.c` may be unused if the underlying Expat version is less than 2.4.0. --- Modules/pyexpat.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index 7f6d84ad864..9949e185dce 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -198,12 +198,14 @@ set_error(pyexpat_state *state, xmlparseobject *self, enum XML_Error code) return NULL; } +#if XML_COMBINED_VERSION >= 20400 static PyObject * set_invalid_arg(pyexpat_state *state, xmlparseobject *self, const char *errmsg) { SET_XML_ERROR(state, self, XML_ERROR_INVALID_ARGUMENT, errmsg); return NULL; } +#endif #undef SET_XML_ERROR From f191db2e0eed421c4d11b007458549002785d96e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns=20=F0=9F=87=B5=F0=9F=87=B8?= Date: Sat, 4 Oct 2025 15:25:07 +0100 Subject: [PATCH 017/373] Add FFY00 to CODEOWNERS for generate-build-details (#139561) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- .github/CODEOWNERS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ec1670e8e0b..1086b426204 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -86,6 +86,10 @@ Modules/makesetup @erlend-aasland @AA-Turner @emmatyping Modules/Setup* @erlend-aasland @AA-Turner @emmatyping Tools/build/regen-configure.sh @AA-Turner +# generate-build-details +Tools/build/generate-build-details.py @FFY00 +Lib/test/test_build_details.py @FFY00 + # ---------------------------------------------------------------------------- # Documentation From 04a2f80a604ccb214902f35deebf0d075543737c Mon Sep 17 00:00:00 2001 From: rowanbudge Date: Sat, 4 Oct 2025 16:59:39 +0100 Subject: [PATCH 018/373] gh-101100: Fix some Sphinx reference warnings in ``whatsnew/2.6.rst`` (#139236) Co-authored-by: rowanvil Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com> --- Doc/whatsnew/2.6.rst | 174 +++++++++++++++++++++++-------------------- 1 file changed, 92 insertions(+), 82 deletions(-) diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index cbab2b57cbb..f5e3a47037c 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -56,7 +56,7 @@ Python 2.6 incorporates new features and syntax from 3.0 while remaining compatible with existing code by not removing older features or syntax. When it's not possible to do that, Python 2.6 tries to do what it can, adding compatibility functions in a -:mod:`future_builtins` module and a :option:`!-3` switch to warn about +:mod:`!future_builtins` module and a :option:`!-3` switch to warn about usages that will become unsupported in 3.0. Some significant new packages have been added to the standard library, @@ -109,7 +109,7 @@ are: Python 3.0 adds several new built-in functions and changes the semantics of some existing builtins. Functions that are new in 3.0 such as :func:`bin` have simply been added to Python 2.6, but existing -builtins haven't been changed; instead, the :mod:`future_builtins` +builtins haven't been changed; instead, the :mod:`!future_builtins` module has versions with the new 3.0 semantics. Code written to be compatible with 3.0 can do ``from future_builtins import hex, map`` as necessary. @@ -118,7 +118,7 @@ A new command-line switch, :option:`!-3`, enables warnings about features that will be removed in Python 3.0. You can run code with this switch to see how much work will be necessary to port code to 3.0. The value of this switch is available -to Python code as the boolean variable :data:`sys.py3kwarning`, +to Python code as the boolean variable :data:`!sys.py3kwarning`, and to C extension code as :c:data:`!Py_Py3kWarningFlag`. .. seealso:: @@ -307,9 +307,9 @@ The :mod:`threading` module's locks and condition variables also support the The lock is acquired before the block is executed and always released once the block is complete. -The :func:`localcontext` function in the :mod:`decimal` module makes it easy -to save and restore the current decimal context, which encapsulates the desired -precision and rounding characteristics for computations:: +The :func:`~decimal.localcontext` function in the :mod:`decimal` module makes +it easy to save and restore the current decimal context, which encapsulates +the desired precision and rounding characteristics for computations:: from decimal import Decimal, Context, localcontext @@ -337,12 +337,12 @@ underlying implementation and should keep reading. A high-level explanation of the context management protocol is: * The expression is evaluated and should result in an object called a "context - manager". The context manager must have :meth:`~object.__enter__` and :meth:`~object.__exit__` - methods. + manager". The context manager must have :meth:`~object.__enter__` and + :meth:`~object.__exit__` methods. -* The context manager's :meth:`~object.__enter__` method is called. The value returned - is assigned to *VAR*. If no ``as VAR`` clause is present, the value is simply - discarded. +* The context manager's :meth:`~object.__enter__` method is called. The value + returned is assigned to *VAR*. If no ``as VAR`` clause is present, the + value is simply discarded. * The code in *BLOCK* is executed. @@ -378,7 +378,7 @@ be to let the user write code like this:: The transaction should be committed if the code in the block runs flawlessly or rolled back if there's an exception. Here's the basic interface for -:class:`DatabaseConnection` that I'll assume:: +:class:`!DatabaseConnection` that I'll assume:: class DatabaseConnection: # Database interface @@ -431,14 +431,15 @@ The contextlib module The :mod:`contextlib` module provides some functions and a decorator that are useful when writing objects for use with the ':keyword:`with`' statement. -The decorator is called :func:`contextmanager`, and lets you write a single -generator function instead of defining a new class. The generator should yield -exactly one value. The code up to the :keyword:`yield` will be executed as the -:meth:`~object.__enter__` method, and the value yielded will be the method's return -value that will get bound to the variable in the ':keyword:`with`' statement's -:keyword:`!as` clause, if any. The code after the :keyword:`!yield` will be -executed in the :meth:`~object.__exit__` method. Any exception raised in the block will -be raised by the :keyword:`!yield` statement. +The decorator is called :func:`~contextlib.contextmanager`, and lets you write +a single generator function instead of defining a new class. The generator +should yield exactly one value. The code up to the :keyword:`yield` will be +executed as the :meth:`~object.__enter__` method, and the value yielded will +be the method's return value that will get bound to the variable in the +':keyword:`with`' statement's :keyword:`!as` clause, if any. The code after +the :keyword:`!yield` will be executed in the :meth:`~object.__exit__` method. +Any exception raised in the block will be raised by the :keyword:`!yield` +statement. Using this decorator, our database example from the previous section could be written as:: @@ -469,7 +470,7 @@ statement both starts a database transaction and acquires a thread lock:: with nested (db_transaction(db), lock) as (cursor, locked): ... -Finally, the :func:`closing` function returns its argument so that it can be +Finally, the :func:`~contextlib.closing` function returns its argument so that it can be bound to a variable, and calls the argument's ``.close()`` method at the end of the block. :: @@ -538,7 +539,7 @@ If you don't like the default directory, it can be overridden by an environment variable. :envvar:`PYTHONUSERBASE` sets the root directory used for all Python versions supporting this feature. On Windows, the directory for application-specific data can be changed by -setting the :envvar:`APPDATA` environment variable. You can also +setting the :envvar:`!APPDATA` environment variable. You can also modify the :file:`site.py` file for your Python installation. The feature can be disabled entirely by running Python with the @@ -568,11 +569,12 @@ The :mod:`multiprocessing` module started out as an exact emulation of the :mod:`threading` module using processes instead of threads. That goal was discarded along the path to Python 2.6, but the general approach of the module is still similar. The fundamental class -is the :class:`Process`, which is passed a callable object and -a collection of arguments. The :meth:`start` method +is the :class:`~multiprocessing.Process`, which is passed a callable object and +a collection of arguments. The :meth:`~multiprocessing.Process.start` method sets the callable running in a subprocess, after which you can call -the :meth:`is_alive` method to check whether the subprocess is still running -and the :meth:`join` method to wait for the process to exit. +the :meth:`~multiprocessing.Process.is_alive` method to check whether the +subprocess is still running and the :meth:`~multiprocessing.Process.join` +method to wait for the process to exit. Here's a simple example where the subprocess will calculate a factorial. The function doing the calculation is written strangely so @@ -619,13 +621,16 @@ the object to communicate. (If the parent were to change the value of the global variable, the child's value would be unaffected, and vice versa.) -Two other classes, :class:`Pool` and :class:`Manager`, provide -higher-level interfaces. :class:`Pool` will create a fixed number of -worker processes, and requests can then be distributed to the workers -by calling :meth:`apply` or :meth:`apply_async` to add a single request, -and :meth:`map` or :meth:`map_async` to add a number of -requests. The following code uses a :class:`Pool` to spread requests -across 5 worker processes and retrieve a list of results:: +Two other classes, :class:`~multiprocessing.pool.Pool` and +:class:`~multiprocessing.Manager`, provide higher-level interfaces. +:class:`~multiprocessing.pool.Pool` will create a fixed number of worker +processes, and requests can then be distributed to the workers by calling +:meth:`~multiprocessing.pool.Pool.apply` or +:meth:`~multiprocessing.pool.Pool.apply_async` to add a single request, and +:meth:`~multiprocessing.pool.Pool.map` or +:meth:`~multiprocessing.pool.Pool.map_async` to add a number of +requests. The following code uses a :class:`~multiprocessing.pool.Pool` to +spread requests across 5 worker processes and retrieve a list of results:: from multiprocessing import Pool @@ -646,15 +651,18 @@ This produces the following output:: 33452526613163807108170062053440751665152000000000 ... -The other high-level interface, the :class:`Manager` class, creates a -separate server process that can hold master copies of Python data +The other high-level interface, the :class:`~multiprocessing.Manager` class, +creates a separate server process that can hold master copies of Python data structures. Other processes can then access and modify these data structures using proxy objects. The following example creates a shared dictionary by calling the :meth:`dict` method; the worker processes then insert values into the dictionary. (Locking is not done for you automatically, which doesn't matter in this example. -:class:`Manager`'s methods also include :meth:`Lock`, :meth:`RLock`, -and :meth:`Semaphore` to create shared locks.) +:class:`~multiprocessing.Manager`'s methods also include +:meth:`~multiprocessing.managers.SyncManager.Lock`, +:meth:`~multiprocessing.managers.SyncManager.RLock`, +and :meth:`~multiprocessing.managers.SyncManager.Semaphore` to create +shared locks.) :: @@ -824,7 +832,7 @@ documentation for a :ref:`complete list `; here's a sample: format, followed by a percent sign. ===== ======================================================================== -Classes and types can define a :meth:`__format__` method to control how they're +Classes and types can define a :meth:`~object.__format__` method to control how they're formatted. It receives a single argument, the format specifier:: def __format__(self, format_spec): @@ -834,7 +842,7 @@ formatted. It receives a single argument, the format specifier:: return str(self) There's also a :func:`format` builtin that will format a single -value. It calls the type's :meth:`__format__` method with the +value. It calls the type's :meth:`~object.__format__` method with the provided specifier:: >>> format(75.6564, '.2f') @@ -1029,56 +1037,58 @@ PEP 3116: New I/O Library Python's built-in file objects support a number of methods, but file-like objects don't necessarily support all of them. Objects that -imitate files usually support :meth:`read` and :meth:`write`, but they -may not support :meth:`readline`, for example. Python 3.0 introduces -a layered I/O library in the :mod:`io` module that separates buffering -and text-handling features from the fundamental read and write -operations. +imitate files usually support :meth:`!read` and +:meth:`!write`, but they may not support :meth:`!readline`, +for example. Python 3.0 introduces a layered I/O library in the :mod:`io` +module that separates buffering and text-handling features from the +fundamental read and write operations. There are three levels of abstract base classes provided by the :mod:`io` module: -* :class:`RawIOBase` defines raw I/O operations: :meth:`read`, - :meth:`readinto`, - :meth:`write`, :meth:`seek`, :meth:`tell`, :meth:`truncate`, - and :meth:`close`. +* :class:`~io.RawIOBase` defines raw I/O operations: :meth:`~io.RawIOBase.read`, + :meth:`~io.RawIOBase.readinto`, :meth:`~io.RawIOBase.write`, + :meth:`~io.IOBase.seek`, :meth:`~io.IOBase.tell`, :meth:`~io.IOBase.truncate`, + and :meth:`~io.IOBase.close`. Most of the methods of this class will often map to a single system call. - There are also :meth:`readable`, :meth:`writable`, and :meth:`seekable` - methods for determining what operations a given object will allow. + There are also :meth:`~io.IOBase.readable`, :meth:`~io.IOBase.writable`, + and :meth:`~io.IOBase.seekable` methods for determining what operations a + given object will allow. Python 3.0 has concrete implementations of this class for files and sockets, but Python 2.6 hasn't restructured its file and socket objects in this way. -* :class:`BufferedIOBase` is an abstract base class that +* :class:`~io.BufferedIOBase` is an abstract base class that buffers data in memory to reduce the number of system calls used, making I/O processing more efficient. - It supports all of the methods of :class:`RawIOBase`, - and adds a :attr:`raw` attribute holding the underlying raw object. + It supports all of the methods of :class:`~io.RawIOBase`, + and adds a :attr:`~io.BufferedIOBase.raw` attribute holding the underlying + raw object. There are five concrete classes implementing this ABC. - :class:`BufferedWriter` and :class:`BufferedReader` are for objects - that support write-only or read-only usage that have a :meth:`seek` - method for random access. :class:`BufferedRandom` objects support + :class:`~io.BufferedWriter` and :class:`~io.BufferedReader` are for objects + that support write-only or read-only usage that have a :meth:`~io.IOBase.seek` + method for random access. :class:`~io.BufferedRandom` objects support read and write access upon the same underlying stream, and - :class:`BufferedRWPair` is for objects such as TTYs that have both + :class:`~io.BufferedRWPair` is for objects such as TTYs that have both read and write operations acting upon unconnected streams of data. - The :class:`BytesIO` class supports reading, writing, and seeking + The :class:`~io.BytesIO` class supports reading, writing, and seeking over an in-memory buffer. .. index:: single: universal newlines; What's new -* :class:`TextIOBase`: Provides functions for reading and writing +* :class:`~io.TextIOBase`: Provides functions for reading and writing strings (remember, strings will be Unicode in Python 3.0), - and supporting :term:`universal newlines`. :class:`TextIOBase` defines + and supporting :term:`universal newlines`. :class:`~io.TextIOBase` defines the :meth:`readline` method and supports iteration upon objects. - There are two concrete implementations. :class:`TextIOWrapper` + There are two concrete implementations. :class:`~io.TextIOWrapper` wraps a buffered I/O object, supporting all of the methods for - text I/O and adding a :attr:`buffer` attribute for access - to the underlying object. :class:`StringIO` simply buffers + text I/O and adding a :attr:`~io.TextIOBase.buffer` attribute for access + to the underlying object. :class:`~io.StringIO` simply buffers everything in memory without ever writing anything to disk. (In Python 2.6, :class:`io.StringIO` is implemented in @@ -1162,7 +1172,7 @@ Some object-oriented languages such as Java support interfaces, declaring that a class has a given set of methods or supports a given access protocol. Abstract Base Classes (or ABCs) are an equivalent feature for Python. The ABC support consists of an :mod:`abc` module -containing a metaclass called :class:`ABCMeta`, special handling of +containing a metaclass called :class:`~abc.ABCMeta`, special handling of this metaclass by the :func:`isinstance` and :func:`issubclass` builtins, and a collection of basic ABCs that the Python developers think will be widely useful. Future versions of Python will probably @@ -1172,17 +1182,17 @@ Let's say you have a particular class and wish to know whether it supports dictionary-style access. The phrase "dictionary-style" is vague, however. It probably means that accessing items with ``obj[1]`` works. Does it imply that setting items with ``obj[2] = value`` works? -Or that the object will have :meth:`keys`, :meth:`values`, and :meth:`items` -methods? What about the iterative variants such as :meth:`iterkeys`? :meth:`copy` -and :meth:`update`? Iterating over the object with :func:`iter`? +Or that the object will have :meth:`!keys`, :meth:`!values`, and :meth:`!items` +methods? What about the iterative variants such as :meth:`!iterkeys`? +:meth:`!copy`and :meth:`!update`? Iterating over the object with :func:`!iter`? The Python 2.6 :mod:`collections` module includes a number of different ABCs that represent these distinctions. :class:`Iterable` -indicates that a class defines :meth:`__iter__`, and -:class:`Container` means the class defines a :meth:`__contains__` +indicates that a class defines :meth:`~object.__iter__`, and +:class:`Container` means the class defines a :meth:`~object.__contains__` method and therefore supports ``x in y`` expressions. The basic dictionary interface of getting items, setting items, and -:meth:`keys`, :meth:`values`, and :meth:`items`, is defined by the +:meth:`!keys`, :meth:`!values`, and :meth:`!items`, is defined by the :class:`MutableMapping` ABC. You can derive your own classes from a particular ABC @@ -1196,7 +1206,7 @@ to indicate they support that ABC's interface:: Alternatively, you could write the class without deriving from the desired ABC and instead register the class by -calling the ABC's :meth:`register` method:: +calling the ABC's :meth:`~abc.ABCMeta.register` method:: import collections @@ -1206,10 +1216,10 @@ calling the ABC's :meth:`register` method:: collections.MutableMapping.register(Storage) For classes that you write, deriving from the ABC is probably clearer. -The :meth:`register` method is useful when you've written a new +The :meth:`~abc.ABCMeta.register` method is useful when you've written a new ABC that can describe an existing type or class, or if you want to declare that some third-party class implements an ABC. -For example, if you defined a :class:`PrintableType` ABC, +For example, if you defined a :class:`!PrintableType` ABC, it's legal to do:: # Register Python's types @@ -1256,16 +1266,16 @@ metaclass in a class definition:: ... -In the :class:`Drawable` ABC above, the :meth:`draw_doubled` method +In the :class:`!Drawable` ABC above, the :meth:`!draw_doubled` method renders the object at twice its size and can be implemented in terms -of other methods described in :class:`Drawable`. Classes implementing +of other methods described in :class:`!Drawable`. Classes implementing this ABC therefore don't need to provide their own implementation -of :meth:`draw_doubled`, though they can do so. An implementation -of :meth:`draw` is necessary, though; the ABC can't provide +of :meth:`!draw_doubled`, though they can do so. An implementation +of :meth:`!draw` is necessary, though; the ABC can't provide a useful generic implementation. -You can apply the ``@abstractmethod`` decorator to methods such as -:meth:`draw` that must be implemented; Python will then raise an +You can apply the :deco:`~abc.abstractmethod` decorator to methods such as +:meth:`!draw` that must be implemented; Python will then raise an exception for classes that don't define the method. Note that the exception is only raised when you actually try to create an instance of a subclass lacking the method:: @@ -1289,7 +1299,7 @@ Abstract data attributes can be declared using the def readonly(self): return self._x -Subclasses must then define a :meth:`readonly` property. +Subclasses must then define a ``readonly`` property. .. seealso:: @@ -2739,13 +2749,13 @@ numbers. .. ====================================================================== -The :mod:`future_builtins` module +The :mod:`!future_builtins` module -------------------------------------- Python 3.0 makes many changes to the repertoire of built-in functions, and most of the changes can't be introduced in the Python 2.x series because they would break compatibility. -The :mod:`future_builtins` module provides versions +The :mod:`!future_builtins` module provides versions of these built-in functions that can be imported when writing 3.0-compatible code. From 0b2168275e8ec491fe7ea6f8c662e804437dfdab Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Sat, 4 Oct 2025 21:44:05 +0530 Subject: [PATCH 019/373] gh-138703: clarify data buffer requirement of `asyncio.StreamWriter.write` (#139564) --- Doc/library/asyncio-stream.rst | 5 +++-- Lib/asyncio/selector_events.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst index e1568ae330b..05445219510 100644 --- a/Doc/library/asyncio-stream.rst +++ b/Doc/library/asyncio-stream.rst @@ -316,13 +316,14 @@ StreamWriter If that fails, the data is queued in an internal write buffer until it can be sent. + The *data* buffer should be a bytes, bytearray, or C-contiguous one-dimensional + memoryview object. + The method should be used along with the ``drain()`` method:: stream.write(data) await stream.drain() - .. note:: - The *data* buffer should be a C contiguous one-dimensional :term:`bytes-like object `. .. method:: writelines(data) diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index a7e27ccf0aa..ff7e16df3c6 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -1050,8 +1050,8 @@ def _read_ready__on_eof(self): def write(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): - raise TypeError(f'data argument must be a bytes-like object, ' - f'not {type(data).__name__!r}') + raise TypeError(f'data argument must be a bytes, bytearray, or memoryview ' + f'object, not {type(data).__name__!r}') if self._eof: raise RuntimeError('Cannot call write() after write_eof()') if self._empty_waiter is not None: From 880c9526f91960b9cba557a18b54e2c32d2f254e Mon Sep 17 00:00:00 2001 From: Dave Peck Date: Sat, 4 Oct 2025 17:06:56 -0400 Subject: [PATCH 020/373] gh-138558: Improve description of ``Interpolation.expression`` (#139187) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/string.templatelib.rst | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Doc/library/string.templatelib.rst b/Doc/library/string.templatelib.rst index 85d65fa9de1..a5b2d796aaf 100644 --- a/Doc/library/string.templatelib.rst +++ b/Doc/library/string.templatelib.rst @@ -24,7 +24,7 @@ Template strings Template strings are a mechanism for custom string processing. They have the full flexibility of Python's :ref:`f-strings`, but return a :class:`Template` instance that gives access -to the static and interpolated (in curly braces) parts of a string +to the static and interpolated (in curly brackets) parts of a string *before* they are combined. To write a t-string, use a ``'t'`` prefix instead of an ``'f'``, like so: @@ -258,13 +258,16 @@ Types .. attribute:: expression :type: str - The text of a valid Python expression, or an empty string. + For interpolations created by t-string literals, :attr:`!expression` + is the expression text found inside the curly brackets (``{`` & ``}``), + including any whitespace, excluding the curly brackets themselves, + and ending before the first ``!``, ``:``, or ``=`` if any is present. + For manually created interpolations, :attr:`!expression` is the arbitrary + string provided when constructing the interpolation instance. - The :attr:`.expression` is the original text of the - interpolation's Python expression, if the interpolation was created - from a t-string literal. Developers creating interpolations manually - should either set this to an empty string or choose a suitable valid - Python expression. + We recommend using valid Python expressions or the empty string for the + ``expression`` field of manually created :class:`!Interpolation` + instances, although this is not enforced at runtime. >>> t'{1 + 2}'.interpolations[0].expression '1 + 2' From 0f0fc5a16368ea45541137cff6b90d63bad5eb26 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Sun, 5 Oct 2025 01:15:29 +0100 Subject: [PATCH 021/373] gh-139573: Update Android to OpenSSL 3.0.18 (#139562) Update Android to OpenSSL 3.0.18. --- Android/android.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Android/android.py b/Android/android.py index 15046b6fe1e..b810a6bfb3c 100755 --- a/Android/android.py +++ b/Android/android.py @@ -184,10 +184,16 @@ def make_build_python(context): run(["make", "-j", str(os.cpu_count())]) +# To create new builds of these dependencies, usually all that's necessary is to +# push a tag to the cpython-android-source-deps repository, and GitHub Actions +# will do the rest. +# +# If you're a member of the Python core team, and you'd like to be able to push +# these tags yourself, please contact Malcolm Smith or Russell Keith-Magee. def unpack_deps(host, prefix_dir): os.chdir(prefix_dir) deps_url = "https://github.com/beeware/cpython-android-source-deps/releases/download" - for name_ver in ["bzip2-1.0.8-3", "libffi-3.4.4-3", "openssl-3.0.15-4", + for name_ver in ["bzip2-1.0.8-3", "libffi-3.4.4-3", "openssl-3.0.18-0", "sqlite-3.50.4-0", "xz-5.4.6-1", "zstd-1.5.7-1"]: filename = f"{name_ver}-{host}.tar.gz" download(f"{deps_url}/{name_ver}/{filename}") From 98e748b3a0d97bd2c785efc63693f971113b3b63 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Sat, 4 Oct 2025 19:43:17 -0500 Subject: [PATCH 022/373] gh-139573: Update OpenSSL in CI (GH-139577) --- .github/workflows/build.yml | 6 +++--- .github/workflows/reusable-ubuntu.yml | 2 +- Doc/using/configure.rst | 2 +- Modules/_ssl_data_35.h | 9 +++++++-- Tools/ssl/multissltests.py | 10 +++++----- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ebfaf32e193..47d38b75429 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -273,7 +273,7 @@ jobs: # Keep 1.1.1w in our list despite it being upstream EOL and otherwise # unsupported as it most resembles other 1.1.1-work-a-like ssl APIs # supported by important vendors such as AWS-LC. - openssl_ver: [1.1.1w, 3.0.17, 3.2.5, 3.3.4, 3.4.2, 3.5.2] + openssl_ver: [1.1.1w, 3.0.18, 3.2.6, 3.3.5, 3.4.3, 3.5.4] # See Tools/ssl/make_ssl_data.py for notes on adding a new version env: OPENSSL_VER: ${{ matrix.openssl_ver }} @@ -438,7 +438,7 @@ jobs: needs: build-context if: needs.build-context.outputs.run-tests == 'true' env: - OPENSSL_VER: 3.0.16 + OPENSSL_VER: 3.0.18 PYTHONSTRICTEXTENSIONBUILD: 1 steps: - uses: actions/checkout@v4 @@ -558,7 +558,7 @@ jobs: matrix: os: [ubuntu-24.04] env: - OPENSSL_VER: 3.0.16 + OPENSSL_VER: 3.0.18 PYTHONSTRICTEXTENSIONBUILD: 1 ASAN_OPTIONS: detect_leaks=0:allocator_may_return_null=1:handle_segv=0 steps: diff --git a/.github/workflows/reusable-ubuntu.yml b/.github/workflows/reusable-ubuntu.yml index 76b19fd5d1a..7f8b9fdf5d6 100644 --- a/.github/workflows/reusable-ubuntu.yml +++ b/.github/workflows/reusable-ubuntu.yml @@ -30,7 +30,7 @@ jobs: runs-on: ${{ inputs.os }} timeout-minutes: 60 env: - OPENSSL_VER: 3.0.15 + OPENSSL_VER: 3.0.18 PYTHONSTRICTEXTENSIONBUILD: 1 TERM: linux steps: diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index b3a9e081edc..b05e0600114 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -22,7 +22,7 @@ Features and minimum versions required to build CPython: * Support for threads. -* OpenSSL 1.1.1 is the minimum version and OpenSSL 3.0.16 is the recommended +* OpenSSL 1.1.1 is the minimum version and OpenSSL 3.0.18 is the recommended minimum version for the :mod:`ssl` and :mod:`hashlib` extension modules. * SQLite 3.15.2 for the :mod:`sqlite3` extension module. diff --git a/Modules/_ssl_data_35.h b/Modules/_ssl_data_35.h index 9e69eaa910f..e4919b550e3 100644 --- a/Modules/_ssl_data_35.h +++ b/Modules/_ssl_data_35.h @@ -1,6 +1,6 @@ /* File generated by Tools/ssl/make_ssl_data.py */ -/* Generated on 2025-08-13T16:42:33.155822+00:00 */ -/* Generated from Git commit openssl-3.5.2-0-g0893a6235 */ +/* Generated on 2025-10-04T17:49:19.148321+00:00 */ +/* Generated from Git commit openssl-3.5.4-0-gc1eeb9406 */ /* generated from args.lib2errnum */ static struct py_ssl_library_code library_codes[] = { @@ -5338,6 +5338,11 @@ static struct py_ssl_error_code error_codes[] = { #else {"FIPS_MODULE_ENTERING_ERROR_STATE", 57, 224}, #endif + #ifdef PROV_R_FIPS_MODULE_IMPORT_PCT_ERROR + {"FIPS_MODULE_IMPORT_PCT_ERROR", ERR_LIB_PROV, PROV_R_FIPS_MODULE_IMPORT_PCT_ERROR}, + #else + {"FIPS_MODULE_IMPORT_PCT_ERROR", 57, 253}, + #endif #ifdef PROV_R_FIPS_MODULE_IN_ERROR_STATE {"FIPS_MODULE_IN_ERROR_STATE", ERR_LIB_PROV, PROV_R_FIPS_MODULE_IN_ERROR_STATE}, #else diff --git a/Tools/ssl/multissltests.py b/Tools/ssl/multissltests.py index c0559446982..56976de4998 100755 --- a/Tools/ssl/multissltests.py +++ b/Tools/ssl/multissltests.py @@ -48,11 +48,11 @@ ] OPENSSL_RECENT_VERSIONS = [ - "3.0.16", - "3.2.5", - "3.3.4", - "3.4.2", - "3.5.2", + "3.0.18", + "3.2.6", + "3.3.5", + "3.4.3", + "3.5.4", # See make_ssl_data.py for notes on adding a new version. ] From 063cef9999d7490e62cb2ad2711634a992c090fb Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Sat, 4 Oct 2025 19:56:59 -0500 Subject: [PATCH 023/373] gh-139573: Update Windows builds to use OpenSSL 3.0.18 (GH-139574) --- .../2025-10-04-12-18-45.gh-issue-139573.EO9kVB.rst | 1 + Misc/externals.spdx.json | 8 ++++---- PCbuild/get_externals.bat | 4 ++-- PCbuild/python.props | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2025-10-04-12-18-45.gh-issue-139573.EO9kVB.rst diff --git a/Misc/NEWS.d/next/Windows/2025-10-04-12-18-45.gh-issue-139573.EO9kVB.rst b/Misc/NEWS.d/next/Windows/2025-10-04-12-18-45.gh-issue-139573.EO9kVB.rst new file mode 100644 index 00000000000..02a3bfb41ce --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2025-10-04-12-18-45.gh-issue-139573.EO9kVB.rst @@ -0,0 +1 @@ +Updated bundled version of OpenSSL to 3.0.18. diff --git a/Misc/externals.spdx.json b/Misc/externals.spdx.json index a87af7f9173..59aceedb94d 100644 --- a/Misc/externals.spdx.json +++ b/Misc/externals.spdx.json @@ -70,21 +70,21 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "6bb739ecddbd2cfb6d255eb5898437a9b5739277dee931338d3275bac5d96ba2" + "checksumValue": "9b07560b6c1afa666bd78b8d3aa5c83fdda02149afdf048596d5b0e0dac1ee55" } ], - "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/openssl-3.0.16.tar.gz", + "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/openssl-3.0.18.tar.gz", "externalRefs": [ { "referenceCategory": "SECURITY", - "referenceLocator": "cpe:2.3:a:openssl:openssl:3.0.16:*:*:*:*:*:*:*", + "referenceLocator": "cpe:2.3:a:openssl:openssl:3.0.18:*:*:*:*:*:*:*", "referenceType": "cpe23Type" } ], "licenseConcluded": "NOASSERTION", "name": "openssl", "primaryPackagePurpose": "SOURCE", - "versionInfo": "3.0.16" + "versionInfo": "3.0.18" }, { "SPDXID": "SPDXRef-PACKAGE-sqlite", diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index eff8d1ccd7f..50a227b563a 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -54,7 +54,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.4 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-3.0.16 +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-3.0.18 set libraries=%libraries% mpdecimal-4.0.0 set libraries=%libraries% sqlite-3.50.4.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.15.0 @@ -79,7 +79,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.4 -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-3.0.16.2 +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-3.0.18 if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.15.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 if NOT "%IncludeLLVM%"=="false" set binaries=%binaries% llvm-19.1.7.0 diff --git a/PCbuild/python.props b/PCbuild/python.props index 06af15a269c..cc157252655 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -82,8 +82,8 @@ $(libffiDir)$(ArchName)\ $(libffiOutDir)include $(ExternalsDir)\mpdecimal-4.0.0\ - $(ExternalsDir)openssl-3.0.16\ - $(ExternalsDir)openssl-bin-3.0.16.2\$(ArchName)\ + $(ExternalsDir)openssl-3.0.18\ + $(ExternalsDir)openssl-bin-3.0.18\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.3.1\ From 9964320d155907bcb3858e5ee99ea48b140d4772 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Sat, 4 Oct 2025 20:05:37 -0700 Subject: [PATCH 024/373] GH-137218: Fix unnecessary recompile of `Programs/_freeze_module` (#139241) --- Makefile.pre.in | 2 +- configure | 54 ++++++++++++++++++++++++++----------------------- configure.ac | 50 ++++++++++++++++++++++----------------------- 3 files changed, 55 insertions(+), 51 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index 37bde125166..244e25c348f 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -3132,7 +3132,7 @@ JIT_DEPS = \ jit_stencils.h @JIT_STENCILS_H@: $(JIT_DEPS) @REGEN_JIT_COMMAND@ -Python/jit.o: $(srcdir)/Python/jit.c jit_stencils.h @JIT_STENCILS_H@ +Python/jit.o: $(srcdir)/Python/jit.c @JIT_STENCILS_H@ $(CC) -c $(PY_CORE_CFLAGS) -o $@ $< .PHONY: regen-jit diff --git a/configure b/configure index 7624cbf0d2a..0d1f6a29e9b 100755 --- a/configure +++ b/configure @@ -644,6 +644,7 @@ ac_includes_default="\ ac_header_c_list= ac_subst_vars='LTLIBOBJS MODULE_BLOCK +JIT_STENCILS_H MODULE_XXLIMITED_35_FALSE MODULE_XXLIMITED_35_TRUE MODULE_XXLIMITED_FALSE @@ -904,7 +905,6 @@ LDSHARED SHLIB_SUFFIX DSYMUTIL_PATH DSYMUTIL -JIT_STENCILS_H REGEN_JIT_COMMAND UNIVERSAL_ARCH_FLAGS WASM_STDLIB @@ -10876,7 +10876,6 @@ then : else case e in #( e) as_fn_append CFLAGS_NODIST " $jit_flags" REGEN_JIT_COMMAND="\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py ${ARCH_TRIPLES:-$host} --output-dir . --pyconfig-dir . --cflags=\"$CFLAGS_JIT\"" - JIT_STENCILS_H="jit_stencils.h" if test "x$Py_DEBUG" = xtrue then : as_fn_append REGEN_JIT_COMMAND " --debug" @@ -10884,7 +10883,6 @@ fi ;; esac fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $tier2_flags $jit_flags" >&5 printf "%s\n" "$tier2_flags $jit_flags" >&6; } @@ -34196,29 +34194,35 @@ printf "%s\n" "$py_cv_module_xxlimited_35" >&6; } # Determine JIT stencils header files based on target platform JIT_STENCILS_H="" -case "$host" in - aarch64-apple-darwin*) - JIT_STENCILS_H="jit_stencils-aarch64-apple-darwin.h" - ;; - x86_64-apple-darwin*) - JIT_STENCILS_H="jit_stencils-x86_64-apple-darwin.h" - ;; - aarch64-pc-windows-msvc) - JIT_STENCILS_H="jit_stencils-aarch64-pc-windows-msvc.h" - ;; - i686-pc-windows-msvc) - JIT_STENCILS_H="jit_stencils-i686-pc-windows-msvc.h" - ;; - x86_64-pc-windows-msvc) - JIT_STENCILS_H="jit_stencils-x86_64-pc-windows-msvc.h" - ;; - aarch64-*-linux-gnu) - JIT_STENCILS_H="jit_stencils-$host.h" - ;; - x86_64-*-linux-gnu) - JIT_STENCILS_H="jit_stencils-$host.h" - ;; +if test "x$enable_experimental_jit" = xno +then : + +else case e in #( + e) case "$host" in + aarch64-apple-darwin*) + JIT_STENCILS_H="jit_stencils-aarch64-apple-darwin.h" + ;; + x86_64-apple-darwin*) + JIT_STENCILS_H="jit_stencils-x86_64-apple-darwin.h" + ;; + aarch64-pc-windows-msvc) + JIT_STENCILS_H="jit_stencils-aarch64-pc-windows-msvc.h" + ;; + i686-pc-windows-msvc) + JIT_STENCILS_H="jit_stencils-i686-pc-windows-msvc.h" + ;; + x86_64-pc-windows-msvc) + JIT_STENCILS_H="jit_stencils-x86_64-pc-windows-msvc.h" + ;; + aarch64-*-linux-gnu) + JIT_STENCILS_H="jit_stencils-$host.h" + ;; + x86_64-*-linux-gnu) + JIT_STENCILS_H="jit_stencils-$host.h" + ;; + esac ;; esac +fi diff --git a/configure.ac b/configure.ac index 7a7e32d4294..7b5da6e0d15 100644 --- a/configure.ac +++ b/configure.ac @@ -2787,13 +2787,11 @@ AS_VAR_IF([jit_flags], [AS_VAR_APPEND([CFLAGS_NODIST], [" $jit_flags"]) AS_VAR_SET([REGEN_JIT_COMMAND], ["\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py ${ARCH_TRIPLES:-$host} --output-dir . --pyconfig-dir . --cflags=\"$CFLAGS_JIT\""]) - AS_VAR_SET([JIT_STENCILS_H], ["jit_stencils.h"]) AS_VAR_IF([Py_DEBUG], [true], [AS_VAR_APPEND([REGEN_JIT_COMMAND], [" --debug"])], [])]) AC_SUBST([REGEN_JIT_COMMAND]) -AC_SUBST([JIT_STENCILS_H]) AC_MSG_RESULT([$tier2_flags $jit_flags]) if test "$disable_gil" = "yes" -a "$enable_experimental_jit" != "no"; then @@ -8175,29 +8173,31 @@ PY_STDLIB_MOD([xxlimited_35], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_d # Determine JIT stencils header files based on target platform JIT_STENCILS_H="" -case "$host" in - aarch64-apple-darwin*) - JIT_STENCILS_H="jit_stencils-aarch64-apple-darwin.h" - ;; - x86_64-apple-darwin*) - JIT_STENCILS_H="jit_stencils-x86_64-apple-darwin.h" - ;; - aarch64-pc-windows-msvc) - JIT_STENCILS_H="jit_stencils-aarch64-pc-windows-msvc.h" - ;; - i686-pc-windows-msvc) - JIT_STENCILS_H="jit_stencils-i686-pc-windows-msvc.h" - ;; - x86_64-pc-windows-msvc) - JIT_STENCILS_H="jit_stencils-x86_64-pc-windows-msvc.h" - ;; - aarch64-*-linux-gnu) - JIT_STENCILS_H="jit_stencils-$host.h" - ;; - x86_64-*-linux-gnu) - JIT_STENCILS_H="jit_stencils-$host.h" - ;; -esac +AS_VAR_IF([enable_experimental_jit], [no], + [], + [case "$host" in + aarch64-apple-darwin*) + JIT_STENCILS_H="jit_stencils-aarch64-apple-darwin.h" + ;; + x86_64-apple-darwin*) + JIT_STENCILS_H="jit_stencils-x86_64-apple-darwin.h" + ;; + aarch64-pc-windows-msvc) + JIT_STENCILS_H="jit_stencils-aarch64-pc-windows-msvc.h" + ;; + i686-pc-windows-msvc) + JIT_STENCILS_H="jit_stencils-i686-pc-windows-msvc.h" + ;; + x86_64-pc-windows-msvc) + JIT_STENCILS_H="jit_stencils-x86_64-pc-windows-msvc.h" + ;; + aarch64-*-linux-gnu) + JIT_STENCILS_H="jit_stencils-$host.h" + ;; + x86_64-*-linux-gnu) + JIT_STENCILS_H="jit_stencils-$host.h" + ;; + esac]) AC_SUBST([JIT_STENCILS_H]) From 20758f9bb1c3baa35c6cb44d0074ae488ca9f4d9 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Sun, 5 Oct 2025 13:03:41 +0800 Subject: [PATCH 025/373] gh-139573: Update OpenSSL version used in iOS builds (#139582) Update OpenSSL version used in iOS builds. --- Apple/__main__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Apple/__main__.py b/Apple/__main__.py index 88b54e91ac8..96c2d34fbe0 100644 --- a/Apple/__main__.py +++ b/Apple/__main__.py @@ -312,11 +312,18 @@ def unpack_deps( On iOS, as a safety mechanism, any dynamic libraries will be purged from the unpacked dependencies. """ + # To create new builds of these dependencies, usually all that's necessary + # is to push a tag to the cpython-apple-source-deps repository, and GitHub + # Actions will do the rest. + # + # If you're a member of the Python core team, and you'd like to be able to + # push these tags yourself, please contact Malcolm Smith or Russell + # Keith-Magee. deps_url = "https://github.com/beeware/cpython-apple-source-deps/releases/download" for name_ver in [ "BZip2-1.0.8-2", "libFFI-3.4.7-2", - "OpenSSL-3.0.17-1", + "OpenSSL-3.0.18-1", "XZ-5.6.4-2", "mpdecimal-4.0.0-2", "zstd-1.5.7-1", From 41712c4e095b2cc988febfe3887616c2779c6210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 5 Oct 2025 10:27:16 +0200 Subject: [PATCH 026/373] gh-139310: skip `test_aead_aes_gcm` for Linux kernel between 6.16.0 and 6.17.x (#139552) Currently, Fedora 42 uses a custom Linux Kernel 6.16.9 that backported an upstream change from 6.17-rc7 [1,3] but not its subsequent fix [2]. Until the issue is resolved upstream, we skip the failing test `test_socket.test_aead_aes_gcm` for kernel versions between 6.16 and 6.17.x. [1] https://github.com/torvalds/linux/commit/1b34cbbf4f011a121ef7b2d7d6e6920a036d5285 [2] https://github.com/torvalds/linux/commit/d0ca0df179c4b21e2a6c4a4fb637aa8fa14575cb. [3] https://gitlab.com/cki-project/kernel-ark/-/commit/45bcf60fe49b37daab1acee57b27211ad1574042 --- Lib/test/support/__init__.py | 10 ++++++++++ Lib/test/test_socket.py | 8 +++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 8d614ab3d42..098bdcc0542 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -310,6 +310,16 @@ def requires(resource, msg=None): if resource == 'gui' and not _is_gui_available(): raise ResourceDenied(_is_gui_available.reason) +def _get_kernel_version(sysname="Linux"): + import platform + if platform.system() != sysname: + return None + version_txt = platform.release().split('-', 1)[0] + try: + return tuple(map(int, version_txt.split('.'))) + except ValueError: + return None + def _requires_unix_version(sysname, min_version): """Decorator raising SkipTest if the OS is `sysname` and the version is less than `min_version`. diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index e4e7fa20f8a..24ee0f2c280 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7139,8 +7139,14 @@ def test_aes_cbc(self): self.assertEqual(len(dec), msglen * multiplier) self.assertEqual(dec, msg * multiplier) - @support.requires_linux_version(4, 9) # see issue29324 + @support.requires_linux_version(4, 9) # see gh-73510 def test_aead_aes_gcm(self): + kernel_version = support._get_kernel_version("Linux") + if kernel_version is not None: + if kernel_version >= (6, 16) and kernel_version < (6, 18): + # See https://github.com/python/cpython/issues/139310. + self.skipTest("upstream Linux kernel issue") + key = bytes.fromhex('c939cc13397c1d37de6ae0e1cb7c423c') iv = bytes.fromhex('b3d8cc017cbb89b39e0f67e2') plain = bytes.fromhex('c3b3c41f113a31b73d9a5cd432103069') From efd223da0c4be9384e07f9d43328bf9528d02145 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 5 Oct 2025 12:49:03 +0200 Subject: [PATCH 027/373] gh-64327: Remove skipped pydoc tests (#139512) Tests skipped since 2014: since commit a46ef70bdfa0273a9d2cc40014c0ab74967fe654. --- Lib/test/test_pydoc/test_pydoc.py | 41 ------------------------------- 1 file changed, 41 deletions(-) diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py index 1793ef14870..65ad7649b9e 100644 --- a/Lib/test/test_pydoc/test_pydoc.py +++ b/Lib/test/test_pydoc/test_pydoc.py @@ -1334,47 +1334,6 @@ def test_url_search_package_error(self): finally: sys.path[:] = saved_paths - @unittest.skip('causes undesirable side-effects (#20128)') - def test_modules(self): - # See Helper.listmodules(). - num_header_lines = 2 - num_module_lines_min = 5 # Playing it safe. - num_footer_lines = 3 - expected = num_header_lines + num_module_lines_min + num_footer_lines - - output = StringIO() - helper = pydoc.Helper(output=output) - helper('modules') - result = output.getvalue().strip() - num_lines = len(result.splitlines()) - - self.assertGreaterEqual(num_lines, expected) - - @unittest.skip('causes undesirable side-effects (#20128)') - def test_modules_search(self): - # See Helper.listmodules(). - expected = 'pydoc - ' - - output = StringIO() - helper = pydoc.Helper(output=output) - with captured_stdout() as help_io: - helper('modules pydoc') - result = help_io.getvalue() - - self.assertIn(expected, result) - - @unittest.skip('some buildbots are not cooperating (#20128)') - def test_modules_search_builtin(self): - expected = 'gc - ' - - output = StringIO() - helper = pydoc.Helper(output=output) - with captured_stdout() as help_io: - helper('modules garbage') - result = help_io.getvalue() - - self.assertStartsWith(result, expected) - def test_importfile(self): try: loaded_pydoc = pydoc.importfile(pydoc.__file__) From 29616f3d2c86ac7575177a9f2b30d51f3b2004d2 Mon Sep 17 00:00:00 2001 From: Jost Migenda Date: Sun, 5 Oct 2025 12:03:54 +0100 Subject: [PATCH 028/373] gh-118767: Remove ``bool(NotImplemented)`` from pending-removal document (#139526) --- Doc/deprecations/pending-removal-in-future.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/Doc/deprecations/pending-removal-in-future.rst b/Doc/deprecations/pending-removal-in-future.rst index edb672ed8ad..7ed430625f3 100644 --- a/Doc/deprecations/pending-removal-in-future.rst +++ b/Doc/deprecations/pending-removal-in-future.rst @@ -15,7 +15,6 @@ although there is currently no date scheduled for their removal. * :mod:`builtins`: - * ``bool(NotImplemented)``. * Generators: ``throw(type, exc, tb)`` and ``athrow(type, exc, tb)`` signature is deprecated: use ``throw(exc)`` and ``athrow(exc)`` instead, the single argument signature. From 13dc2fde8cec1e8aad04c7635b3da4ff3e3dcb00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 5 Oct 2025 14:03:25 +0200 Subject: [PATCH 029/373] gh-70765: avoid waiting for HTTP headers when parsing HTTP/0.9 requests (#139514) --- Lib/http/server.py | 7 ++++ Lib/test/test_httpservers.py | 37 +++++++++++++++++++ ...5-10-02-17-40-10.gh-issue-70765.zVlLZn.rst | 5 +++ 3 files changed, 49 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-10-02-17-40-10.gh-issue-70765.zVlLZn.rst diff --git a/Lib/http/server.py b/Lib/http/server.py index a2ffbe2e44d..160d3eefc7c 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -302,6 +302,7 @@ def parse_request(self): error response has already been sent back. """ + is_http_0_9 = False self.command = None # set in case of error on the first line self.request_version = version = self.default_request_version self.close_connection = True @@ -359,6 +360,7 @@ def parse_request(self): HTTPStatus.BAD_REQUEST, "Bad HTTP/0.9 request type (%r)" % command) return False + is_http_0_9 = True self.command, self.path = command, path # gh-87389: The purpose of replacing '//' with '/' is to protect @@ -368,6 +370,11 @@ def parse_request(self): if self.path.startswith('//'): self.path = '/' + self.path.lstrip('/') # Reduce to a single / + # For HTTP/0.9, headers are not expected at all. + if is_http_0_9: + self.headers = {} + return True + # Examine the headers and look for a Connection directive. try: self.headers = http.client.parse_headers(self.rfile, diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 2548a7c5f29..85d3a346439 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -362,6 +362,43 @@ def test_head_via_send_error(self): self.assertEqual(b'', data) +class HTTP09ServerTestCase(BaseTestCase): + + class request_handler(NoLogRequestHandler, BaseHTTPRequestHandler): + """Request handler for HTTP/0.9 server.""" + + def do_GET(self): + self.wfile.write(f'OK: here is {self.path}\r\n'.encode()) + + def setUp(self): + super().setUp() + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock = self.enterContext(self.sock) + self.sock.connect((self.HOST, self.PORT)) + + def test_simple_get(self): + self.sock.send(b'GET /index.html\r\n') + res = self.sock.recv(1024) + self.assertEqual(res, b"OK: here is /index.html\r\n") + + def test_invalid_request(self): + self.sock.send(b'POST /index.html\r\n') + res = self.sock.recv(1024) + self.assertIn(b"Bad HTTP/0.9 request type ('POST')", res) + + def test_single_request(self): + self.sock.send(b'GET /foo.html\r\n') + res = self.sock.recv(1024) + self.assertEqual(res, b"OK: here is /foo.html\r\n") + + self.sock.send(b'GET /bar.html\r\n') + res = self.sock.recv(1024) + # The server will not parse more input as it closed the connection. + # Note that the socket connection itself is still opened since the + # client is responsible for also closing it on their side. + self.assertEqual(res, b'') + + def certdata_file(*path): return os.path.join(os.path.dirname(__file__), "certdata", *path) diff --git a/Misc/NEWS.d/next/Library/2025-10-02-17-40-10.gh-issue-70765.zVlLZn.rst b/Misc/NEWS.d/next/Library/2025-10-02-17-40-10.gh-issue-70765.zVlLZn.rst new file mode 100644 index 00000000000..e1a9bbe9afe --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-02-17-40-10.gh-issue-70765.zVlLZn.rst @@ -0,0 +1,5 @@ +:mod:`http.server`: fix default handling of HTTP/0.9 requests in +:class:`~http.server.BaseHTTPRequestHandler`. Previously, +:meth:`!BaseHTTPRequestHandler.parse_request`` incorrectly +waited for headers in the request although those are not +supported in HTTP/0.9. Patch by Bénédikt Tran. From 6edb2ddb5f3695cf4938979d645f31d7fba43ec8 Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Sun, 5 Oct 2025 17:37:42 +0200 Subject: [PATCH 030/373] gh-139400: Make sure that parent parsers outlive their subparsers in `pyexpat` (#139403) * Modules/pyexpat.c: Disallow collection of in-use parent parsers. Within libexpat, a parser created via `XML_ExternalEntityParserCreate` is relying on its parent parser throughout its entire lifetime. Prior to this fix, is was possible for the parent parser to be garbage-collected too early. --- Lib/test/test_pyexpat.py | 36 +++++++++++++++++++ ...-09-29-00-01-28.gh-issue-139400.X2T-jO.rst | 4 +++ Modules/pyexpat.c | 25 +++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-29-00-01-28.gh-issue-139400.X2T-jO.rst diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py index 8e0f7374b26..b4ce72dfd51 100644 --- a/Lib/test/test_pyexpat.py +++ b/Lib/test/test_pyexpat.py @@ -771,6 +771,42 @@ def resolve_entity(context, base, system_id, public_id): self.assertEqual(handler_call_args, [("bar", "baz")]) +class ParentParserLifetimeTest(unittest.TestCase): + """ + Subparsers make use of their parent XML_Parser inside of Expat. + As a result, parent parsers need to outlive subparsers. + + See https://github.com/python/cpython/issues/139400. + """ + + def test_parent_parser_outlives_its_subparsers__single(self): + parser = expat.ParserCreate() + subparser = parser.ExternalEntityParserCreate(None) + + # Now try to cause garbage collection of the parent parser + # while it's still being referenced by a related subparser. + del parser + + def test_parent_parser_outlives_its_subparsers__multiple(self): + parser = expat.ParserCreate() + subparser_one = parser.ExternalEntityParserCreate(None) + subparser_two = parser.ExternalEntityParserCreate(None) + + # Now try to cause garbage collection of the parent parser + # while it's still being referenced by a related subparser. + del parser + + def test_parent_parser_outlives_its_subparsers__chain(self): + parser = expat.ParserCreate() + subparser = parser.ExternalEntityParserCreate(None) + subsubparser = subparser.ExternalEntityParserCreate(None) + + # Now try to cause garbage collection of the parent parsers + # while they are still being referenced by a related subparser. + del parser + del subparser + + class ReparseDeferralTest(unittest.TestCase): def test_getter_setter_round_trip(self): parser = expat.ParserCreate() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-09-29-00-01-28.gh-issue-139400.X2T-jO.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-29-00-01-28.gh-issue-139400.X2T-jO.rst new file mode 100644 index 00000000000..a5dea3b5f81 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-29-00-01-28.gh-issue-139400.X2T-jO.rst @@ -0,0 +1,4 @@ +:mod:`xml.parsers.expat`: Make sure that parent Expat parsers are only +garbage-collected once they are no longer referenced by subparsers created +by :meth:`~xml.parsers.expat.xmlparser.ExternalEntityParserCreate`. +Patch by Sebastian Pipping. diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index 9949e185dce..9c252be9cf2 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -76,6 +76,15 @@ typedef struct { PyObject_HEAD XML_Parser itself; + /* + * Strong reference to a parent `xmlparseobject` if this parser + * is a child parser. Set to NULL if this parser is a root parser. + * This is needed to keep the parent parser alive as long as it has + * at least one child parser. + * + * See https://github.com/python/cpython/issues/139400 for details. + */ + PyObject *parent; int ordered_attributes; /* Return attributes as a list. */ int specified_attributes; /* Report only specified attributes. */ int in_callback; /* Is a callback active? */ @@ -1067,6 +1076,11 @@ pyexpat_xmlparser_ExternalEntityParserCreate_impl(xmlparseobject *self, return NULL; } + // The new subparser will make use of the parent XML_Parser inside of Expat. + // So we need to take subparsers into account with the reference counting + // of their parent parser. + Py_INCREF(self); + new_parser->buffer_size = self->buffer_size; new_parser->buffer_used = 0; new_parser->buffer = NULL; @@ -1076,6 +1090,7 @@ pyexpat_xmlparser_ExternalEntityParserCreate_impl(xmlparseobject *self, new_parser->ns_prefixes = self->ns_prefixes; new_parser->itself = XML_ExternalEntityParserCreate(self->itself, context, encoding); + new_parser->parent = (PyObject *)self; new_parser->handlers = 0; new_parser->intern = Py_XNewRef(self->intern); @@ -1083,11 +1098,13 @@ pyexpat_xmlparser_ExternalEntityParserCreate_impl(xmlparseobject *self, new_parser->buffer = PyMem_Malloc(new_parser->buffer_size); if (new_parser->buffer == NULL) { Py_DECREF(new_parser); + Py_DECREF(self); return PyErr_NoMemory(); } } if (!new_parser->itself) { Py_DECREF(new_parser); + Py_DECREF(self); return PyErr_NoMemory(); } @@ -1101,6 +1118,7 @@ pyexpat_xmlparser_ExternalEntityParserCreate_impl(xmlparseobject *self, new_parser->handlers = PyMem_New(PyObject *, i); if (!new_parser->handlers) { Py_DECREF(new_parser); + Py_DECREF(self); return PyErr_NoMemory(); } clear_handlers(new_parser, 1); @@ -1481,6 +1499,7 @@ newxmlparseobject(pyexpat_state *state, const char *encoding, /* namespace_separator is either NULL or contains one char + \0 */ self->itself = XML_ParserCreate_MM(encoding, &ExpatMemoryHandler, namespace_separator); + self->parent = NULL; if (self->itself == NULL) { PyErr_SetString(PyExc_RuntimeError, "XML_ParserCreate failed"); @@ -1517,6 +1536,7 @@ xmlparse_traverse(PyObject *op, visitproc visit, void *arg) for (size_t i = 0; handler_info[i].name != NULL; i++) { Py_VISIT(self->handlers[i]); } + Py_VISIT(self->parent); Py_VISIT(Py_TYPE(op)); return 0; } @@ -1527,6 +1547,10 @@ xmlparse_clear(PyObject *op) xmlparseobject *self = xmlparseobject_CAST(op); clear_handlers(self, 0); Py_CLEAR(self->intern); + // NOTE: We cannot call Py_CLEAR(self->parent) prior to calling + // XML_ParserFree(self->itself), or a subparser could lose its parent + // XML_Parser while still making use of it internally. + // https://github.com/python/cpython/issues/139400 return 0; } @@ -1540,6 +1564,7 @@ xmlparse_dealloc(PyObject *op) XML_ParserFree(self->itself); } self->itself = NULL; + Py_CLEAR(self->parent); if (self->handlers != NULL) { PyMem_Free(self->handlers); From 1fe89d324e6b96dc44a7bd593c428a90d1f39d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 5 Oct 2025 18:51:16 +0200 Subject: [PATCH 031/373] gh-70765: fix an HTTP/0.9 flaky test post GH-139514 (#139610) Fix a flaky test introduced in 13dc2fde8cec1e8aad04c7635b3da4ff3e3dcb00. After a single HTTP/0.9 request, both client and server are expected to close the connection on their side. In particular, if a client sends two requests with the same connection, only the first one should be handled. In the tests, it might happen that checking for the second request to be ignored did not take into account that the server may have already closed the connection. This flaky behavior was first observed on macOS CI workers but could not be reproduced locally on a Linux machine. --- Lib/test/test_httpservers.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 85d3a346439..7da5e3a1957 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -391,12 +391,13 @@ def test_single_request(self): res = self.sock.recv(1024) self.assertEqual(res, b"OK: here is /foo.html\r\n") - self.sock.send(b'GET /bar.html\r\n') - res = self.sock.recv(1024) - # The server will not parse more input as it closed the connection. - # Note that the socket connection itself is still opened since the - # client is responsible for also closing it on their side. - self.assertEqual(res, b'') + # Ignore errors if the connection is already closed, + # as this is the expected behavior of HTTP/0.9. + with contextlib.suppress(OSError): + self.sock.send(b'GET /bar.html\r\n') + res = self.sock.recv(1024) + # The server should not process our request. + self.assertEqual(res, b'') def certdata_file(*path): From 5389234fca91f474bf9df8bff38cea2d30091d80 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Sun, 5 Oct 2025 18:05:29 +0100 Subject: [PATCH 032/373] GH-123299: Copyedit 3.14 What's New: New Features (#139543) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Doc/whatsnew/3.14.rst | 1428 ++++++++++++++++++++--------------------- 1 file changed, 711 insertions(+), 717 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 647b20bf6a9..9179861ed0b 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -80,404 +80,71 @@ plus syntax highlighting in the REPL, as well as the usual deprecations and removals, and improvements in user-friendliness and correctness. +-------------- + .. PEP-sized items next. -* :ref:`PEP 779: Free-threaded Python is officially supported ` -* :ref:`PEP 649 and 749: deferred evaluation of annotations ` -* :ref:`PEP 734: Multiple interpreters in the stdlib ` -* :ref:`PEP 741: Python configuration C API ` -* :ref:`PEP 750: Template strings ` -* :ref:`PEP 758: Allow except and except* expressions without parentheses ` -* :ref:`PEP 761: Discontinuation of PGP signatures ` -* :ref:`PEP 765: Disallow return/break/continue that exit a finally block ` -* :ref:`Free-threaded mode improvements ` -* :ref:`PEP 768: Safe external debugger interface for CPython ` -* :ref:`PEP 784: Adding Zstandard to the standard library ` -* :ref:`A new type of interpreter ` -* :ref:`Syntax highlighting in the default interactive shell `, - and color output in :ref:`unittest `, - :ref:`argparse `, - :ref:`json ` and - :ref:`calendar ` CLIs -* :ref:`Binary releases for the experimental just-in-time compiler ` +Interpreter improvements: +* :pep:`649` and :pep:`749`: :ref:`Deferred evaluation of annotations + ` +* :pep:`734`: :ref:`Multiple interpreters in the standard library + ` +* :pep:`750`: :ref:`Template strings ` +* :pep:`758`: :ref:`Allow except and except* expressions without brackets + ` +* :pep:`765`: :ref:`Control flow in finally blocks + ` +* :pep:`768`: :ref:`Safe external debugger interface for CPython + ` +* :ref:`A new type of interpreter ` +* :ref:`Free-threaded mode improvements ` +* :ref:`Improved error messages ` + +Significant improvements in the standard library: + +* :pep:`784`: :ref:`Zstandard support in the standard library + ` +* :ref:`whatsnew314-asyncio-introspection` +* :ref:`whatsnew314-concurrent-warnings-control` +* :ref:`Syntax highlighting in the default interactive shell + `, and color output in several + standard library CLIs + +C API improvements: + +* :pep:`741`: :ref:`Python configuration C API ` + +Platform support: + +* :pep:`776`: Emscripten is now an :ref:`officially supported platform + `, at :pep:`tier 3 <11#tier-3>`. + +Release changes: + +* :pep:`779`: :ref:`Free-threaded Python is officially supported + ` +* :pep:`761`: :ref:`Discontinuation of PGP signatures ` +* :ref:`Windows and macOS binary releases now support the experimental + just-in-time compiler ` +* :ref:`Binary releases for Android are now provided ` New features ============ -.. _whatsnew314-pep779: +.. _whatsnew314-deferred-annotations: -PEP 779: Free-threaded Python is officially supported ------------------------------------------------------ - -The free-threaded build of Python is now supported and no longer experimental. -This is the start of phase II where free-threaded Python is officially supported -but still optional. - -We are confident that the project is on the right path, and we appreciate the -continued dedication from everyone working to make free-threading ready for -broader adoption across the Python community. - -With these recommendations and the acceptance of this PEP, we as the Python -developer community should broadly advertise that free-threading is a supported -Python build option now and into the future, and that it will not be removed -without a proper deprecation schedule. - -Any decision to transition to phase III, with free-threading as the default or -sole build of Python is still undecided, and dependent on many factors both -within CPython itself and the community. This decision is for the future. - -.. seealso:: - :pep:`779` and its `acceptance - `__. - -.. _whatsnew314-pep734: - -PEP 734: Multiple interpreters in the stdlib --------------------------------------------- - -The CPython runtime supports running multiple copies of Python in the -same process simultaneously and has done so for over 20 years. -Each of these separate copies is called an "interpreter". -However, the feature had been available only through the C-API. - -That limitation is removed in the 3.14 release, -with the new :mod:`concurrent.interpreters` module. - -There are at least two notable reasons why using multiple interpreters -is worth considering: - -* they support a new (to Python), human-friendly concurrency model -* true multi-core parallelism - -For some use cases, concurrency in software enables efficiency and -can simplify software, at a high level. At the same time, implementing -and maintaining all but the simplest concurrency is often a struggle -for the human brain. That especially applies to plain threads -(for example, :mod:`threading`), where all memory is shared between all threads. - -With multiple isolated interpreters, you can take advantage of a class -of concurrency models, like CSP or the actor model, that have found -success in other programming languages, like Smalltalk, Erlang, -Haskell, and Go. Think of multiple interpreters like threads -but with opt-in sharing. - -Regarding multi-core parallelism: as of the 3.12 release, interpreters -are now sufficiently isolated from one another to be used in parallel. -(See :pep:`684`.) This unlocks a variety of CPU-intensive use cases -for Python that were limited by the :term:`GIL`. - -Using multiple interpreters is similar in many ways to -:mod:`multiprocessing`, in that they both provide isolated logical -"processes" that can run in parallel, with no sharing by default. -However, when using multiple interpreters, an application will use -fewer system resources and will operate more efficiently (since it -stays within the same process). Think of multiple interpreters as -having the isolation of processes with the efficiency of threads. - -.. XXX Add an example or two. -.. XXX Link to the not-yet-added HOWTO doc. - -While the feature has been around for decades, multiple interpreters -have not been used widely, due to low awareness and the lack of a stdlib -module. Consequently, they currently have several notable limitations, -which will improve significantly now that the feature is finally -going mainstream. - -Current limitations: - -* starting each interpreter has not been optimized yet -* each interpreter uses more memory than necessary - (we will be working next on extensive internal sharing between - interpreters) -* there aren't many options *yet* for truly sharing objects or other - data between interpreters (other than :type:`memoryview`) -* many extension modules on PyPI are not compatible with multiple - interpreters yet (stdlib extension modules *are* compatible) -* the approach to writing applications that use multiple isolated - interpreters is mostly unfamiliar to Python users, for now - -The impact of these limitations will depend on future CPython -improvements, how interpreters are used, and what the community solves -through PyPI packages. Depending on the use case, the limitations may -not have much impact, so try it out! - -Furthermore, future CPython releases will reduce or eliminate overhead -and provide utilities that are less appropriate on PyPI. In the -meantime, most of the limitations can also be addressed through -extension modules, meaning PyPI packages can fill any gap for 3.14, and -even back to 3.12 where interpreters were finally properly isolated and -stopped sharing the :term:`GIL`. Likewise, we expect to slowly see -libraries on PyPI for high-level abstractions on top of interpreters. - -Regarding extension modules, work is in progress to update some PyPI -projects, as well as tools like Cython, pybind11, nanobind, and PyO3. -The steps for isolating an extension module are found at -:ref:`isolating-extensions-howto`. Isolating a module has a lot of -overlap with what is required to support -:ref:`free-threading `, -so the ongoing work in the community in that area will help accelerate -support for multiple interpreters. - -Also added in 3.14: :ref:`concurrent.futures.InterpreterPoolExecutor -`. - -.. seealso:: - :pep:`734`. - - -.. _whatsnew314-pep750: - -PEP 750: Template strings -------------------------- - -Template string literals (t-strings) are a generalization of f-strings, -using a ``t`` in place of the ``f`` prefix. Instead of evaluating -to :class:`str`, t-strings evaluate to a new :class:`!string.templatelib.Template` type: - -.. code-block:: python - - from string.templatelib import Template - - name = "World" - template: Template = t"Hello {name}" - -The template can then be combined with functions that operate on the template's -structure to produce a :class:`str` or a string-like result. -For example, sanitizing input: - -.. code-block:: python - - evil = "" - template = t"

{evil}

" - assert html(template) == "

<script>alert('evil')</script>

" - -As another example, generating HTML attributes from data: - -.. code-block:: python - - attributes = {"src": "shrubbery.jpg", "alt": "looks nice"} - template = t"" - assert html(template) == 'looks nice' - -Compared to using an f-string, the ``html`` function has access to template attributes -containing the original information: static strings, interpolations, and values -from the original scope. Unlike existing templating approaches, t-strings build -from the well-known f-string syntax and rules. Template systems thus benefit -from Python tooling as they are much closer to the Python language, syntax, -scoping, and more. - -Writing template handlers is straightforward: - -.. code-block:: python - - from string.templatelib import Template, Interpolation - - def lower_upper(template: Template) -> str: - """Render static parts lowercased and interpolations uppercased.""" - parts: list[str] = [] - for item in template: - if isinstance(item, Interpolation): - parts.append(str(item.value).upper()) - else: - parts.append(item.lower()) - return "".join(parts) - - name = "world" - assert lower_upper(t"HELLO {name}") == "hello WORLD" - -With this in place, developers can write template systems to sanitize SQL, make -safe shell operations, improve logging, tackle modern ideas in web development -(HTML, CSS, and so on), and implement lightweight, custom business DSLs. - -(Contributed by Jim Baker, Guido van Rossum, Paul Everitt, Koudai Aono, -Lysandros Nikolaou, Dave Peck, Adam Turner, Jelle Zijlstra, Bénédikt Tran, -and Pablo Galindo Salgado in :gh:`132661`.) - -.. seealso:: - :pep:`750`. - - -.. _whatsnew314-pep768: - -PEP 768: Safe external debugger interface for CPython ------------------------------------------------------ - -:pep:`768` introduces a zero-overhead debugging interface that allows debuggers and profilers -to safely attach to running Python processes. This is a significant enhancement to Python's -debugging capabilities allowing debuggers to forego unsafe alternatives. See -:ref:`below ` for how this feature is leveraged to -implement the new :mod:`pdb` module's remote attaching capabilities. - -The new interface provides safe execution points for attaching debugger code without modifying -the interpreter's normal execution path or adding runtime overhead. This enables tools to -inspect and interact with Python applications in real-time without stopping or restarting -them — a crucial capability for high-availability systems and production environments. - -For convenience, CPython implements this interface through the :mod:`sys` module with a -:func:`sys.remote_exec` function:: - - sys.remote_exec(pid, script_path) - -This function allows sending Python code to be executed in a target process at the next safe -execution point. However, tool authors can also implement the protocol directly as described -in the PEP, which details the underlying mechanisms used to safely attach to running processes. - -Here's a simple example that inspects object types in a running Python process: - - .. code-block:: python - - import os - import sys - import tempfile - - # Create a temporary script - with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f: - script_path = f.name - f.write(f"import my_debugger; my_debugger.connect({os.getpid()})") - try: - # Execute in process with PID 1234 - print("Behold! An offering:") - sys.remote_exec(1234, script_path) - finally: - os.unlink(script_path) - -The debugging interface has been carefully designed with security in mind and includes several -mechanisms to control access: - -* A :envvar:`PYTHON_DISABLE_REMOTE_DEBUG` environment variable. -* A :option:`-X disable-remote-debug` command-line option. -* A :option:`--without-remote-debug` configure flag to completely disable the feature at build time. - -A key implementation detail is that the interface piggybacks on the interpreter's existing evaluation -loop and safe points, ensuring zero overhead during normal execution while providing a reliable way -for external processes to coordinate debugging operations. - -(Contributed by Pablo Galindo Salgado, Matt Wozniski, and Ivona Stojanovic in :gh:`131591`.) - -.. seealso:: - :pep:`768`. - - -.. _whatsnew314-pep784: - -PEP 784: Adding Zstandard to the standard library -------------------------------------------------- - -The new ``compression`` package contains modules :mod:`!compression.lzma`, -:mod:`!compression.bz2`, :mod:`!compression.gzip` and :mod:`!compression.zlib` -which re-export the :mod:`lzma`, :mod:`bz2`, :mod:`gzip` and :mod:`zlib` -modules respectively. The new import names under ``compression`` are the -canonical names for importing these compression modules going forward. However, -the existing modules names have not been deprecated. Any deprecation or removal -of the existing compression modules will occur no sooner than five years after -the release of 3.14. - -The new :mod:`!compression.zstd` module provides compression and decompression -APIs for the Zstandard format via bindings to `Meta's zstd library -`__. Zstandard is a widely adopted, highly -efficient, and fast compression format. In addition to the APIs introduced in -:mod:`!compression.zstd`, support for reading and writing Zstandard compressed -archives has been added to the :mod:`tarfile`, :mod:`zipfile`, and -:mod:`shutil` modules. - -Here's an example of using the new module to compress some data: - -.. code-block:: python - - from compression import zstd - import math - - data = str(math.pi).encode() * 20 - - compressed = zstd.compress(data) - - ratio = len(compressed) / len(data) - print(f"Achieved compression ratio of {ratio}") - -As can be seen, the API is similar to the APIs of the :mod:`!lzma` and -:mod:`!bz2` modules. - -(Contributed by Emma Harper Smith, Adam Turner, Gregory P. Smith, Tomas Roun, -Victor Stinner, and Rogdham in :gh:`132983`.) - -.. seealso:: - :pep:`784`. - - -.. _whatsnew314-remote-pdb: - -Remote attaching to a running Python process with PDB ------------------------------------------------------ - -The :mod:`pdb` module now supports remote attaching to a running Python process -using a new ``-p PID`` command-line option: - -.. code-block:: sh - - python -m pdb -p 1234 - -This will connect to the Python process with the given PID and allow you to -debug it interactively. Notice that due to how the Python interpreter works -attaching to a remote process that is blocked in a system call or waiting for -I/O will only work once the next bytecode instruction is executed or when the -process receives a signal. - -This feature uses :pep:`768` and the :func:`sys.remote_exec` function -to attach to the remote process and send the PDB commands to it. - - -(Contributed by Matt Wozniski and Pablo Galindo in :gh:`131591`.) - -.. seealso:: - :pep:`768`. - - -.. _whatsnew314-pep758: - -PEP 758 – Allow except and except* expressions without parentheses ------------------------------------------------------------------- - -The :keyword:`except` and :keyword:`except* ` expressions now allow -parentheses to be omitted when there are multiple exception types and the ``as`` clause is not used. -For example the following expressions are now valid: - -.. code-block:: python - - try: - connect_to_server() - except TimeoutError, ConnectionRefusedError: - print("Network issue encountered.") - - # The same applies to except* (for exception groups): - - try: - connect_to_server() - except* TimeoutError, ConnectionRefusedError: - print("Network issue encountered.") - -Check :pep:`758` for more details. - -(Contributed by Pablo Galindo and Brett Cannon in :gh:`131831`.) - -.. seealso:: - :pep:`758`. - - -.. _whatsnew314-pep649: - -PEP 649 and 749: deferred evaluation of annotations ---------------------------------------------------- +:pep:`649` & :pep:`749`: Deferred evaluation of annotations +------------------------------------------------------------ The :term:`annotations ` on functions, classes, and modules are no longer evaluated eagerly. Instead, annotations are stored in special-purpose :term:`annotate functions ` and evaluated only when necessary (except if ``from __future__ import annotations`` is used). -This is specified in :pep:`649` and :pep:`749`. -This change is designed to make annotations in Python more performant and more -usable in most circumstances. The runtime cost for defining annotations is +This change is designed to improve performance and usability of annotations +in Python in most circumstances. The runtime cost for defining annotations is minimized, but it remains possible to introspect annotations at runtime. It is no longer necessary to enclose annotations in strings if they contain forward references. @@ -505,70 +172,343 @@ This example shows how these formats behave: >>> get_annotations(func, format=Format.STRING) {'arg': 'Undefined'} -Implications for annotated code -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The :ref:`porting ` section contains guidance +on changes that may be needed due to these changes, though in the majority of +cases, code will continue working as-is. -If you define annotations in your code (for example, for use with a static type -checker), then this change probably does not affect you: you can keep -writing annotations the same way you did with previous versions of Python. - -You will likely be able to remove quoted strings in annotations, which are frequently -used for forward references. Similarly, if you use ``from __future__ import annotations`` -to avoid having to write strings in annotations, you may well be able to -remove that import once you support only Python 3.14 and newer. -However, if you rely on third-party libraries that read annotations, -those libraries may need changes to support unquoted annotations before they -work as expected. - -Implications for readers of ``__annotations__`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your code reads the ``__annotations__`` attribute on objects, you may want -to make changes in order to support code that relies on deferred evaluation of -annotations. For example, you may want to use :func:`annotationlib.get_annotations` -with the :attr:`~annotationlib.Format.FORWARDREF` format, as the :mod:`dataclasses` -module now does. - -The external :pypi:`typing_extensions` package provides partial backports of some of the -functionality of the :mod:`annotationlib` module, such as the :class:`~annotationlib.Format` -enum and the :func:`~annotationlib.get_annotations` function. These can be used to -write cross-version code that takes advantage of the new behavior in Python 3.14. - -Related changes -^^^^^^^^^^^^^^^ - -The changes in Python 3.14 are designed to rework how ``__annotations__`` -works at runtime while minimizing breakage to code that contains -annotations in source code and to code that reads ``__annotations__``. However, -if you rely on undocumented details of the annotation behavior or on private -functions in the standard library, there are many ways in which your code may -not work in Python 3.14. To safeguard your code against future changes, -use only the documented functionality of the :mod:`annotationlib` module. - -In particular, do not read annotations directly from the namespace dictionary -attribute of type objects. Use :func:`annotationlib.get_annotate_from_class_namespace` -during class construction and :func:`annotationlib.get_annotations` afterwards. - -In previous releases, it was sometimes possible to access class annotations from -an instance of an annotated class. This behavior was undocumented and accidental, -and will no longer work in Python 3.14. - -``from __future__ import annotations`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In Python 3.7, :pep:`563` introduced the ``from __future__ import annotations`` -directive, which turns all annotations into strings. This directive is now -considered deprecated and it is expected to be removed in a future version of Python. -However, this removal will not happen until after Python 3.13, the last version of -Python without deferred evaluation of annotations, reaches its end of life in 2029. -In Python 3.14, the behavior of code using ``from __future__ import annotations`` -is unchanged. - -(Contributed by Jelle Zijlstra in :gh:`119180`; :pep:`649` was written by Larry Hastings.) +(Contributed by Jelle Zijlstra in :pep:`749` and :gh:`119180`; +:pep:`649` was written by Larry Hastings.) .. seealso:: - :pep:`649` and :pep:`749`. + :pep:`649` + Deferred Evaluation Of Annotations Using Descriptors + :pep:`749` + Implementing PEP 649 + + +.. _whatsnew314-multiple-interpreters: + +:pep:`734`: Multiple interpreters in the standard library +--------------------------------------------------------- + +The CPython runtime supports running multiple copies of Python in the +same process simultaneously and has done so for over 20 years. +Each of these separate copies is called an 'interpreter'. +However, the feature had been available only through +the :ref:`C-API `. + +That limitation is removed in Python 3.14, +with the new :mod:`concurrent.interpreters` module. + +There are at least two notable reasons why using multiple interpreters +has significant benefits: + +* they support a new (to Python), human-friendly concurrency model +* true multi-core parallelism + +For some use cases, concurrency in software enables efficiency and +can simplify design, at a high level. +At the same time, implementing and maintaining all but the simplest concurrency +is often a struggle for the human brain. +That especially applies to plain threads (for example, :mod:`threading`), +where all memory is shared between all threads. + +With multiple isolated interpreters, you can take advantage of a class +of concurrency models, like CSP or the actor model, that have found +success in other programming languages, like Smalltalk, Erlang, +Haskell, and Go. Think of multiple interpreters like threads +but with opt-in sharing. + +Regarding multi-core parallelism: as of Python 3.12, interpreters +are now sufficiently isolated from one another to be used in parallel +(see :pep:`684`). This unlocks a variety of CPU-intensive use cases +for Python that were limited by the :term:`GIL`. + +Using multiple interpreters is similar in many ways to +:mod:`multiprocessing`, in that they both provide isolated logical +"processes" that can run in parallel, with no sharing by default. +However, when using multiple interpreters, an application will use +fewer system resources and will operate more efficiently (since it +stays within the same process). Think of multiple interpreters as +having the isolation of processes with the efficiency of threads. + +.. XXX Add an example or two. +.. XXX Link to the not-yet-added HOWTO doc. + +While the feature has been around for decades, multiple interpreters +have not been used widely, due to low awareness and the lack of a +standard library module. Consequently, they currently have several +notable limitations, which will improve significantly now that the +feature is finally going mainstream. + +Current limitations: + +* starting each interpreter has not been optimized yet +* each interpreter uses more memory than necessary + (work continues on extensive internal sharing between interpreters) +* there aren't many options *yet* for truly sharing objects or other + data between interpreters (other than :type:`memoryview`) +* many third-party extension modules on PyPI are not yet compatible + with multiple interpreters + (all standard library extension modules *are* compatible) +* the approach to writing applications that use multiple isolated + interpreters is mostly unfamiliar to Python users, for now + +The impact of these limitations will depend on future CPython +improvements, how interpreters are used, and what the community solves +through PyPI packages. Depending on the use case, the limitations may +not have much impact, so try it out! + +Furthermore, future CPython releases will reduce or eliminate overhead +and provide utilities that are less appropriate on PyPI. In the +meantime, most of the limitations can also be addressed through +extension modules, meaning PyPI packages can fill any gap for 3.14, and +even back to 3.12 where interpreters were finally properly isolated and +stopped sharing the :term:`GIL`. Likewise, libraries on PyPI are expected +to emerge for high-level abstractions on top of interpreters. + +Regarding extension modules, work is in progress to update some PyPI +projects, as well as tools like Cython, pybind11, nanobind, and PyO3. +The steps for isolating an extension module are found at +:ref:`isolating-extensions-howto`. +Isolating a module has a lot of overlap with what is required to support +:ref:`free-threading `, so the ongoing +work in the community in that area will help accelerate support +for multiple interpreters. + +Also added in 3.14: :ref:`concurrent.futures.InterpreterPoolExecutor +`. + +(Contributed by Eric Snow in :gh:`134939`.) + +.. seealso:: :pep:`734` + + +.. _whatsnew314-template-string-literals: + +:pep:`750`: Template string literals +------------------------------------ + +Template strings are a new mechanism for custom string processing. +They share the familiar syntax of f-strings but, unlike f-strings, +return an object representing the static and interpolated parts of +the string, instead of a simple :class:`str`. + +To write a t-string, use a ``'t'`` prefix instead of an ``'f'``: + +.. doctest:: + + >>> variety = 'Stilton' + >>> template = t'Try some {variety} cheese!' + >>> type(template) + + +:class:`~string.templatelib.Template` objects provide access to the static +and interpolated (in curly braces) parts of a string *before* they are combined. +Iterate over :class:`!Template` instances to access their parts in order: + +.. testsetup:: + + variety = 'Stilton' + template = t'Try some {variety} cheese!' + +.. doctest:: + + >>> list(template) + ['Try some ', Interpolation('Stilton', 'variety', None, ''), ' cheese!'] + +It's easy to write (or call) code to process :class:`!Template` instances. +For example, here's a function that renders static parts lowercase and +:class:`~string.templatelib.Interpolation` instances uppercase: + +.. code-block:: python + + from string.templatelib import Interpolation + + def lower_upper(template): + """Render static parts lowercase and interpolations uppercase.""" + parts = [] + for part in template: + if isinstance(part, Interpolation): + parts.append(str(part.value).upper()) + else: + parts.append(part.lower()) + return ''.join(parts) + + name = 'Wenslydale' + template = t'Mister {name}' + assert lower_upper(template) == 'mister WENSLYDALE' + +Because :class:`!Template` instances distinguish between static strings and +interpolations at runtime, they can be useful for sanitising user input. +Writing a :func:`!html` function that escapes user input in HTML is an exercise +left to the reader! +Template processing code can provide improved flexibility. +For instance, a more advanced :func:`!html` function could accept +a :class:`!dict` of HTML attributes directly in the template: + +.. code-block:: python + + attributes = {'src': 'limburger.jpg', 'alt': 'lovely cheese'} + template = t'' + assert html(template) == 'lovely cheese' + +Of course, template processing code does not need to return a string-like result. +An even *more* advanced :func:`!html` could return a custom type representing +a DOM-like structure. + +With t-strings in place, developers can write systems that sanitise SQL, +make safe shell operations, improve logging, tackle modern ideas in web +development (HTML, CSS, and so on), and implement lightweight custom business DSLs. + +(Contributed by Jim Baker, Guido van Rossum, Paul Everitt, Koudai Aono, +Lysandros Nikolaou, Dave Peck, Adam Turner, Jelle Zijlstra, Bénédikt Tran, +and Pablo Galindo Salgado in :gh:`132661`.) + +.. seealso:: :pep:`750`. + + +.. _whatsnew314-remote-debugging: + +:pep:`768`: Safe external debugger interface +-------------------------------------------- + +Python 3.14 introduces a zero-overhead debugging interface that allows +debuggers and profilers to safely attach to running Python processes +without stopping or restarting them. +This is a significant enhancement to Python's debugging capabilities, +meaning that unsafe alternatives are no longer required. + +The new interface provides safe execution points for attaching debugger code +without modifying the interpreter's normal execution path +or adding any overhead at runtime. +Due to this, tools can now inspect and interact with Python applications +in real-time, which is a crucial capability for high-availability systems +and production environments. + +For convenience, this interface is implemented in the :func:`sys.remote_exec` +function. For example: + +.. code-block:: python + + import sys + from tempfile import NamedTemporaryFile + + with NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f: + script_path = f.name + f.write(f'import my_debugger; my_debugger.connect({os.getpid()})') + + # Execute in process with PID 1234 + print('Behold! An offering:') + sys.remote_exec(1234, script_path) + + +This function allows sending Python code to be executed in a target process +at the next safe execution point. +However, tool authors can also implement the protocol directly as described +in the PEP, which details the underlying mechanisms used to safely attach to +running processes. + +The debugging interface has been carefully designed with security in mind +and includes several mechanisms to control access: + +* A :envvar:`PYTHON_DISABLE_REMOTE_DEBUG` environment variable. +* A :option:`-X disable-remote-debug` command-line option. +* A :option:`--without-remote-debug` configure flag to completely disable + the feature at build time. + +(Contributed by Pablo Galindo Salgado, Matt Wozniski, and Ivona Stojanovic +in :gh:`131591`.) + +.. seealso:: :pep:`768`. + + +.. _whatsnew314-tail-call-interpreter: + +A new type of interpreter +------------------------- + +A new type of interpreter has been added to CPython. +It uses tail calls between small C functions that implement individual +Python opcodes, rather than one large C ``case`` statement. +For certain newer compilers, this interpreter provides +significantly better performance. Preliminary benchmarks suggest a geometric +mean of 3-5% faster on the standard ``pyperformance`` benchmark suite, +depending on platform and architecture. +The baseline is Python 3.14 built with Clang 19, without this new interpreter. + +This interpreter currently only works with Clang 19 and newer +on x86-64 and AArch64 architectures. However, a future release +of GCC is expected will support this as well. + +This feature is opt-in for now. Enabling profile-guided optimization is highly +recommendeded when using the new interpreter as it is the only configuration +that has been tested and validated for improved performance. +For further information, see :option:`--with-tail-call-interp`. + +.. note:: + + This is not to be confused with `tail call optimization`__ of Python + functions, which is currently not implemented in CPython. + + This new interpreter type is an internal implementation detail of the CPython + interpreter. It doesn't change the visible behavior of Python programs at + all. It can improve their performance, but doesn't change anything else. + + __ https://en.wikipedia.org/wiki/Tail_call + +(Contributed by Ken Jin in :gh:`128563`, with ideas on how to implement this +in CPython by Mark Shannon, Garrett Gu, Haoran Xu, and Josh Haberman.) + + +.. _whatsnew314-free-threaded-cpython: + +Free-threaded mode improvements +------------------------------- + +CPython's free-threaded mode (:pep:`703`), initially added in 3.13, +has been significantly improved in Python 3.14. +The implementation described in PEP 703 has been finished, including C API +changes, and temporary workarounds in the interpreter were replaced with +more permanent solutions. +The specializing adaptive interpreter (:pep:`659`) is now enabled +in free-threaded mode, which along with many other optimizations +greatly improves its performance. +The performance penalty on single-threaded code in free-threaded mode +is now roughly 5-10%, depending on the platform and C compiler used. + +From Python 3.14, when compiling extension modules for the free-threaded build of +CPython on Windows, the preprocessor variable ``Py_GIL_DISABLED`` now needs to +be specified by the build backend, as it will no longer be determined +automatically by the C compiler. For a running interpreter, the setting that +was used at compile time can be found using :func:`sysconfig.get_config_var`. + +The new :option:`-X context_aware_warnings <-X>` flag controls if +:ref:`concurrent safe warnings control ` +is enabled. The flag defaults to true for the free-threaded build +and false for the GIL-enabled build. + +A new :data:`~sys.flags.thread_inherit_context` flag has been added, +which if enabled means that threads created with :class:`threading.Thread` +start with a copy of the :class:`~contextvars.Context()` of the caller of +:meth:`~threading.Thread.start`. Most significantly, this makes the warning +filtering context established by :class:`~warnings.catch_warnings` be +"inherited" by threads (or asyncio tasks) started within that context. It also +affects other modules that use context variables, such as the :mod:`decimal` +context manager. +This flag defaults to true for the free-threaded build and false for +the GIL-enabled build. + +(Contributed by Sam Gross, Matt Page, Neil Schemenauer, Thomas Wouters, +Donghee Na, Kirill Podoprigora, Ken Jin, Itamar Oren, Brett Simmers, +Dino Viehland, Nathan Goldbaum, Ralf Gommers, Lysandros Nikolaou, Kumar Aditya, +Edgar Margffoy, and many others. +Some of these contributors are employed by Meta, which has continued to provide +significant engineering resources to support this project.) + + +.. _whatsnew314-improved-error-messages: Improved error messages ----------------------- @@ -589,47 +529,12 @@ Improved error messages ^^^^^^ SyntaxError: invalid syntax. Did you mean 'while'? - >>> asynch def fetch_data(): - ... pass - Traceback (most recent call last): - File "", line 1 - asynch def fetch_data(): - ^^^^^^ - SyntaxError: invalid syntax. Did you mean 'async'? - - >>> async def foo(): - ... awaid fetch_data() - Traceback (most recent call last): - File "", line 2 - awaid fetch_data() - ^^^^^ - SyntaxError: invalid syntax. Did you mean 'await'? - - >>> raisee ValueError("Error") - Traceback (most recent call last): - File "", line 1 - raisee ValueError("Error") - ^^^^^^ - SyntaxError: invalid syntax. Did you mean 'raise'? - While the feature focuses on the most common cases, some variations of misspellings may still result in regular syntax errors. (Contributed by Pablo Galindo in :gh:`132449`.) -* When an unpacking assignment fails due to an incorrect number of variables, the - error message prints the received number of values in more cases than before. - (Contributed by Tushar Sadhwani in :gh:`122239`.) - - .. code-block:: pycon - - >>> x, y, z = 1, 2, 3, 4 - Traceback (most recent call last): - File "", line 1, in - x, y, z = 1, 2, 3, 4 - ^^^^^^^ - ValueError: too many values to unpack (expected 3, got 4) - -* :keyword:`elif` statements that follow an :keyword:`else` block now have a specific error message. +* :keyword:`elif` statements that follow an :keyword:`else` block now have + a specific error message. (Contributed by Steele Farnsworth in :gh:`129902`.) .. code-block:: pycon @@ -645,11 +550,9 @@ Improved error messages ^^^^ SyntaxError: 'elif' block follows an 'else' block -* If a statement (:keyword:`pass`, :keyword:`del`, :keyword:`return`, - :keyword:`yield`, :keyword:`raise`, :keyword:`break`, :keyword:`continue`, - :keyword:`assert`, :keyword:`import`, :keyword:`from`) is passed to the - :ref:`if_expr` after :keyword:`else`, or one of :keyword:`pass`, - :keyword:`break`, or :keyword:`continue` is passed before :keyword:`if`, then the +* If a statement is passed to the :ref:`if_expr` after :keyword:`else`, + or one of :keyword:`pass`, :keyword:`break`, or :keyword:`continue` + is passed before :keyword:`if`, then the error message highlights where the :token:`~python-grammar:expression` is required. (Contributed by Sergey Miryanov in :gh:`129515`.) @@ -669,10 +572,9 @@ Improved error messages ^^^^^^^^ SyntaxError: expected expression before 'if', but statement is given - * When incorrectly closed strings are detected, the error message suggests - that the string may be intended to be part of the string. (Contributed by - Pablo Galindo in :gh:`88535`.) + that the string may be intended to be part of the string. + (Contributed by Pablo Galindo in :gh:`88535`.) .. code-block:: pycon @@ -681,8 +583,8 @@ Improved error messages SyntaxError: invalid syntax. Is this intended to be part of the string? * When strings have incompatible prefixes, the error now shows - which prefixes are incompatible. (Contributed by - Nikita Sobolev in :gh:`133197`.) + which prefixes are incompatible. + (Contributed by Nikita Sobolev in :gh:`133197`.) .. code-block:: pycon @@ -699,20 +601,11 @@ Improved error messages - Except handlers: ``except ... as ...`` - Pattern-match cases: ``case ... as ...`` - (Contributed by Nikita Sobolev in :gh:`123539`, - :gh:`123562`, and :gh:`123440`.) - - .. code-block:: pycon - - >>> import ast as arr[0] - File "", line 1 - import ast as arr[0] - ^^^^^^ - SyntaxError: cannot use subscript as import target + (Contributed by Nikita Sobolev in :gh:`123539`, :gh:`123562`, and :gh:`123440`.) * Improved error message when trying to add an instance of an unhashable type to - a :class:`dict` or :class:`set`. (Contributed by CF Bolz-Tereick and Victor Stinner - in :gh:`132828`.) + a :class:`dict` or :class:`set`. + (Contributed by CF Bolz-Tereick and Victor Stinner in :gh:`132828`.) .. code-block:: pycon @@ -739,65 +632,70 @@ Improved error messages (Contributed by Bénédikt Tran in :gh:`128398`.) -.. _whatsnew314-pep741: +.. _whatsnew314-zstandard: -PEP 741: Python configuration C API ------------------------------------ +:pep:`784`: Zstandard support in the standard library +----------------------------------------------------- -Add a :ref:`PyInitConfig C API ` to configure the Python -initialization without relying on C structures and the ability to make -ABI-compatible changes in the future. +The new :mod:`!compression` package contains modules :mod:`!compression.lzma`, +:mod:`!compression.bz2`, :mod:`!compression.gzip` and :mod:`!compression.zlib` +which re-export the :mod:`lzma`, :mod:`bz2`, :mod:`gzip` and :mod:`zlib` +modules respectively. The new import names under :mod:`!compression` are the +preferred names for importing these compression modules from Python 3.14. However, +the existing modules names have not been deprecated. Any deprecation or removal +of the existing compression modules will occur no sooner than five years after +the release of 3.14. -Complete the :pep:`587` :ref:`PyConfig C API ` by adding -:c:func:`PyInitConfig_AddModule` which can be used to add a built-in extension -module; a feature previously referred to as the “inittab”. +The new :mod:`!compression.zstd` module provides compression and decompression +APIs for the Zstandard format via bindings to `Meta's zstd library +`__. Zstandard is a widely adopted, highly +efficient, and fast compression format. In addition to the APIs introduced in +:mod:`!compression.zstd`, support for reading and writing Zstandard compressed +archives has been added to the :mod:`tarfile`, :mod:`zipfile`, and +:mod:`shutil` modules. -Add :c:func:`PyConfig_Get` and :c:func:`PyConfig_Set` functions to get and set -the current runtime configuration. +Here's an example of using the new module to compress some data: -PEP 587 “Python Initialization Configuration” unified all the ways to configure -the Python initialization. This PEP unifies also the configuration of the -Python preinitialization and the Python initialization in a single API. -Moreover, this PEP only provides a single choice to embed Python, instead of -having two “Python” and “Isolated” choices (PEP 587), to simplify the API -further. +.. code-block:: python -The lower level PEP 587 PyConfig API remains available for use cases with an -intentionally higher level of coupling to CPython implementation details (such -as emulating the full functionality of CPython’s CLI, including its -configuration mechanisms). + from compression import zstd + import math -(Contributed by Victor Stinner in :gh:`107954`.) + data = str(math.pi).encode() * 20 + compressed = zstd.compress(data) + ratio = len(compressed) / len(data) + print(f"Achieved compression ratio of {ratio}") + +As can be seen, the API is similar to the APIs of the :mod:`!lzma` and +:mod:`!bz2` modules. + +(Contributed by Emma Harper Smith, Adam Turner, Gregory P. Smith, Tomas Roun, +Victor Stinner, and Rogdham in :gh:`132983`.) + +.. seealso:: :pep:`784`. -.. seealso:: - :pep:`741`. .. _whatsnew314-asyncio-introspection: Asyncio introspection capabilities ---------------------------------- -Added a new command-line interface to inspect running Python processes using -asynchronous tasks, available via: +Added a new command-line interface to inspect running Python processes +using asynchronous tasks, available via ``python -m asyncio ps PID`` +or ``python -m asyncio pstree PID``. -.. code-block:: bash +The ``ps`` subcommand inspects the given process ID (PID) and displays +information about currently running asyncio tasks. +It outputs a task table: a flat listing of all tasks, their names, +their coroutine stacks, and which tasks are awaiting them. - python -m asyncio ps PID -This tool inspects the given process ID (PID) and displays information about -currently running asyncio tasks. It outputs a task table: a flat -listing of all tasks, their names, their coroutine stacks, and which tasks are -awaiting them. - -.. code-block:: bash - - python -m asyncio pstree PID - -This tool fetches the same information, but renders a visual async call tree, -showing coroutine relationships in a hierarchical format. This command is -particularly useful for debugging long-running or stuck asynchronous programs. -It can help developers quickly identify where a program is blocked, what tasks -are pending, and how coroutines are chained together. +The ``pstree`` subcommand fetches the same information, but instead renders a +visual async call tree, showing coroutine relationships in a hierarchical format. +This command is particularly useful for debugging long-running or stuck +asynchronous programs. +It can help developers quickly identify where a program is blocked, +what tasks are pending, and how coroutines are chained together. For example given this code: @@ -805,23 +703,25 @@ For example given this code: import asyncio - async def play(track): + async def play_track(track): await asyncio.sleep(5) - print(f"🎵 Finished: {track}") + print(f'🎵 Finished: {track}') - async def album(name, tracks): + async def play_album(name, tracks): async with asyncio.TaskGroup() as tg: for track in tracks: - tg.create_task(play(track), name=track) + tg.create_task(play_track(track), name=track) async def main(): async with asyncio.TaskGroup() as tg: tg.create_task( - album("Sundowning", ["TNDNBTG", "Levitate"]), name="Sundowning") + play_album('Sundowning', ['TNDNBTG', 'Levitate']), + name='Sundowning') tg.create_task( - album("TMBTE", ["DYWTYLM", "Aqua Regia"]), name="TMBTE") + play_album('TMBTE', ['DYWTYLM', 'Aqua Regia']), + name='TMBTE') - if __name__ == "__main__": + if __name__ == '__main__': asyncio.run(main()) Executing the new tool on the running process will yield a table like this: @@ -886,139 +786,16 @@ prevent tree construction: (Contributed by Pablo Galindo, Łukasz Langa, Yury Selivanov, and Marta Gomez Macias in :gh:`91048`.) -.. _whatsnew314-tail-call: -A new type of interpreter -------------------------- - -A new type of interpreter has been added to CPython. -It uses tail calls between small C functions that implement individual -Python opcodes, rather than one large C case statement. -For certain newer compilers, this interpreter provides -significantly better performance. Preliminary numbers on our machines suggest -anywhere up to 30% faster Python code, and a geometric mean of 3-5% -faster on ``pyperformance`` depending on platform and architecture. The -baseline is Python 3.14 built with Clang 19 without this new interpreter. - -This interpreter currently only works with Clang 19 and newer -on x86-64 and AArch64 architectures. However, we expect -that a future release of GCC will support this as well. - -This feature is opt-in for now. We highly recommend enabling profile-guided -optimization with the new interpreter as it is the only configuration we have -tested and can validate its improved performance. -For further information on how to build Python, see -:option:`--with-tail-call-interp`. - -.. note:: - - This is not to be confused with `tail call optimization`__ of Python - functions, which is currently not implemented in CPython. - - This new interpreter type is an internal implementation detail of the CPython - interpreter. It doesn't change the visible behavior of Python programs at - all. It can improve their performance, but doesn't change anything else. - - __ https://en.wikipedia.org/wiki/Tail_call - -.. attention:: - - This section previously reported a 9-15% geometric mean speedup. This number has since been - cautiously revised down to 3-5%. While we expect performance results to be better - than what we report, our estimates are more conservative due to a - `compiler bug `_ found in - Clang/LLVM 19, which causes the normal interpreter to be slower. We were unaware of this bug, - resulting in inaccurate results. We sincerely apologize for - communicating results that were only accurate for LLVM v19.1.x and v20.1.0. In the meantime, - the bug has been fixed in LLVM v20.1.1 and for the upcoming v21.1, but it will remain - unfixed for LLVM v19.1.x and v20.1.0. Thus - any benchmarks with those versions of LLVM may produce inaccurate numbers. - (Thanks to Nelson Elhage for bringing this to light.) - -(Contributed by Ken Jin in :gh:`128563`, with ideas on how to implement this -in CPython by Mark Shannon, Garrett Gu, Haoran Xu, and Josh Haberman.) - -.. _whatsnew314-free-threaded-cpython: - -Free-threaded mode ------------------- - -Free-threaded mode (:pep:`703`), initially added in 3.13, has been significantly improved. -The implementation described in PEP 703 was finished, including C API changes, -and temporary workarounds in the interpreter were replaced with more permanent solutions. -The specializing adaptive interpreter (:pep:`659`) is now enabled in free-threaded mode, -which along with many other optimizations greatly improves its performance. -The performance penalty on single-threaded code in free-threaded mode is now roughly 5-10%, -depending on platform and C compiler used. - -This work was done by many contributors: Sam Gross, Matt Page, Neil Schemenauer, -Thomas Wouters, Donghee Na, Kirill Podoprigora, Ken Jin, Itamar Oren, -Brett Simmers, Dino Viehland, Nathan Goldbaum, Ralf Gommers, Lysandros Nikolaou, -Kumar Aditya, Edgar Margffoy, and many others. - -Some of these contributors are employed by Meta, which has continued to provide -significant engineering resources to support this project. - -From 3.14, when compiling extension modules for the free-threaded build of -CPython on Windows, the preprocessor variable ``Py_GIL_DISABLED`` now needs to -be specified by the build backend, as it will no longer be determined -automatically by the C compiler. For a running interpreter, the setting that -was used at compile time can be found using :func:`sysconfig.get_config_var`. - -A new flag has been added, :data:`~sys.flags.context_aware_warnings`. This -flag defaults to true for the free-threaded build and false for the GIL-enabled -build. If the flag is true then the :class:`warnings.catch_warnings` context -manager uses a context variable for warning filters. This makes the context -manager behave predictably when used with multiple threads or asynchronous -tasks. - -A new flag has been added, :data:`~sys.flags.thread_inherit_context`. This flag -defaults to true for the free-threaded build and false for the GIL-enabled -build. If the flag is true then threads created with :class:`threading.Thread` -start with a copy of the :class:`~contextvars.Context()` of the caller of -:meth:`~threading.Thread.start`. Most significantly, this makes the warning -filtering context established by :class:`~warnings.catch_warnings` be -"inherited" by threads (or asyncio tasks) started within that context. It also -affects other modules that use context variables, such as the :mod:`decimal` -context manager. - - -.. _whatsnew314-jit-compiler: - -Binary releases for the experimental just-in-time compiler ----------------------------------------------------------- - -The official macOS and Windows release binaries now include an *experimental* -just-in-time (JIT) compiler. Although it is **not** recommended for production -use, it can be tested by setting :envvar:`PYTHON_JIT=1 ` as an -environment variable. Downstream source builds and redistributors can use the -:option:`--enable-experimental-jit=yes-off` configuration option for similar -behavior. - -The JIT is at an early stage and still in active development. As such, the -typical performance impact of enabling it can range from 10% slower to 20% -faster, depending on workload. To aid in testing and evaluation, a set of -introspection functions has been provided in the :data:`sys._jit` namespace. -:func:`sys._jit.is_available` can be used to determine if the current executable -supports JIT compilation, while :func:`sys._jit.is_enabled` can be used to tell -if JIT compilation has been enabled for the current process. - -Currently, the most significant missing functionality is that native debuggers -and profilers like ``gdb`` and ``perf`` are unable to unwind through JIT frames -(Python debuggers and profilers, like :mod:`pdb` or :mod:`profile`, continue to -work without modification). Free-threaded builds do not support JIT compilation. - -Please report any bugs or major performance regressions that you encounter! - -.. seealso:: :pep:`744` +.. _whatsnew314-concurrent-warnings-control: Concurrent safe warnings control -------------------------------- The :class:`warnings.catch_warnings` context manager will now optionally -use a context variable for warning filters. This is enabled by setting +use a context variable for warning filters. This is enabled by setting the :data:`~sys.flags.context_aware_warnings` flag, either with the ``-X`` -command-line option or an environment variable. This gives predictable +command-line option or an environment variable. This gives predictable warnings control when using :class:`~warnings.catch_warnings` combined with multiple threads or asynchronous tasks. The flag defaults to true for the free-threaded build and false for the GIL-enabled build. @@ -1026,30 +803,6 @@ free-threaded build and false for the GIL-enabled build. (Contributed by Neil Schemenauer and Kumar Aditya in :gh:`130010`.) -Platform support -================ - -* :pep:`776`: Emscripten is now an officially supported platform at - :pep:`tier 3 <11#tier-3>`. As a part of this effort, more than 25 bugs in - `Emscripten libc`__ were fixed. Emscripten now includes support - for :mod:`ctypes`, :mod:`termios`, and :mod:`fcntl`, as well as - experimental support for the new :ref:`default interactive shell - `. - - (Contributed by R. Hood Chatham in :gh:`127146`, :gh:`127683`, and :gh:`136931`.) - - __ https://emscripten.org/docs/porting/emscripten-runtime-environment.html - -* iOS and macOS apps can now be configured to redirect ``stdout`` and - ``stderr`` content to the system log. - (Contributed by Russell Keith-Magee in :gh:`127592`.) - -* The iOS testbed is now able to stream test output while the test is running. - The testbed can also be used to run the test suite of projects other than - CPython itself. - (Contributed by Russell Keith-Magee in :gh:`127592`.) - - Other language changes ====================== @@ -1144,6 +897,26 @@ Command line and environment .. _Jython: https://www.jython.org/ +.. _whatsnew314-bracketless-except: + +PEP 758: Allow ``except`` and ``except*`` expressions without brackets +---------------------------------------------------------------------- + +The :keyword:`except` and :keyword:`except* ` expressions +now allow brackets to be omitted when there are multiple exception types +and the ``as`` clause is not used. +For example: + +.. code-block:: python + + try: + connect_to_server() + except TimeoutError, ConnectionRefusedError: + print('The network has ceased to be!') + +(Contributed by Pablo Galindo and Brett Cannon in :pep:`758` and :gh:`131831`.) + + .. _whatsnew314-finally-syntaxwarning: PEP 765: Control flow in :keyword:`finally` blocks @@ -1214,24 +987,24 @@ New modules * :mod:`annotationlib`: For introspecting :term:`annotations `. - See :ref:`PEP 749 ` for more details. + See :ref:`PEP 749 ` for more details. (Contributed by Jelle Zijlstra in :gh:`119180`.) * :mod:`compression` (including :mod:`compression.zstd`): A package for compression-related modules, including a new module to support the Zstandard compression format. - See :ref:`PEP 784 ` for more details. + See :ref:`PEP 784 ` for more details. (Contributed by Emma Harper Smith, Adam Turner, Gregory P. Smith, Tomas Roun, Victor Stinner, and Rogdham in :gh:`132983`.) * :mod:`concurrent.interpreters`: Support for multiple interpreters in the standard library. - See :ref:`PEP 734 ` for more details. + See :ref:`PEP 734 ` for more details. (Contributed by Eric Snow in :gh:`134939`.) * :mod:`string.templatelib`: Support for template string literals (t-strings). - See :ref:`PEP 750 ` for more details. + See :ref:`PEP 750 ` for more details. (Contributed by Jim Baker, Guido van Rossum, Paul Everitt, Koudai Aono, Lysandros Nikolaou, Dave Peck, Adam Turner, Jelle Zijlstra, Bénédikt Tran, and Pablo Galindo Salgado in :gh:`132661`.) @@ -1253,8 +1026,6 @@ argparse and subparser names if mistyped by the user. (Contributed by Savannah Ostrowski in :gh:`124456`.) -.. _whatsnew314-color-argparse: - * Enable color for help text, which can be disabled with the optional *color* parameter to :class:`argparse.ArgumentParser`. This can also be controlled by :ref:`environment variables @@ -1339,7 +1110,7 @@ concurrent.futures asynchronously. This is separate from the new :mod:`~concurrent.interpreters` module - introduced by :ref:`PEP 734 `. + introduced by :ref:`PEP 734 `. (Contributed by Eric Snow in :gh:`124548`.) .. _whatsnew314-concurrent-futures-start-method: @@ -1687,8 +1458,6 @@ json See the :ref:`JSON command-line interface ` documentation. (Contributed by Trey Hunner in :gh:`122873`.) -.. _whatsnew314-color-json: - * By default, the output of the :ref:`JSON command-line interface ` is highlighted in color. This can be controlled by :ref:`environment variables @@ -1905,6 +1674,25 @@ pathlib pdb --- +* The :mod:`pdb` module now supports remote attaching to a running Python process + using a new :option:`-p PID ` command-line option: + + .. code-block:: sh + + python -m pdb -p 1234 + + This will connect to the Python process with the given PID and allow you to + debug it interactively. Notice that due to how the Python interpreter works + attaching to a remote process that is blocked in a system call or waiting for + I/O will only work once the next bytecode instruction is executed or when the + process receives a signal. + + This feature uses :ref:`PEP 768 ` + and the new :func:`sys.remote_exec` function to attach to the remote process + and send the PDB commands to it. + + (Contributed by Matt Wozniski and Pablo Galindo in :gh:`131591`.) + * Hardcoded breakpoints (:func:`breakpoint` and :func:`~pdb.set_trace`) now reuse the most recent :class:`~pdb.Pdb` instance that calls :meth:`~pdb.Pdb.set_trace`, instead of creating a new one each time. @@ -2064,7 +1852,7 @@ sys function was deprecated in Python 3.13 but it didn't raise a runtime warning. * Add :func:`sys.remote_exec` to implement the new external debugger interface. - See :ref:`PEP 768 ` for details. + See :ref:`PEP 768 ` for details. (Contributed by Pablo Galindo Salgado, Matt Wozniski, and Ivona Stojanovic in :gh:`131591`.) @@ -2914,7 +2702,7 @@ CPython bytecode changes opcodes to construct new :class:`~string.templatelib.Interpolation` and :class:`~string.templatelib.Template` instances, respectively. (Contributed by Lysandros Nikolaou and others in :gh:`132661`; - see also :ref:`PEP 750: Template strings `). + see also :ref:`PEP 750: Template strings `). * Remove the :opcode:`!BUILD_CONST_KEY_MAP` opcode. Use :opcode:`BUILD_MAP` instead. @@ -2948,7 +2736,7 @@ Pseudo-instructions * Add the :opcode:`!ANNOTATIONS_PLACEHOLDER` pseudo instruction to support partially executed module-level annotations with - :ref:`deferred evaluation of annotations `. + :ref:`deferred evaluation of annotations `. (Contributed by Jelle Zijlstra in :gh:`130907`.) * Add the :opcode:`!BINARY_OP_EXTEND` pseudo instruction, @@ -2983,6 +2771,39 @@ Pseudo-instructions C API changes ============= +.. _whatsnew314-capi-config: + +Python configuration C API +-------------------------- + +Add a :ref:`PyInitConfig C API ` to configure the Python +initialization without relying on C structures and the ability to make +ABI-compatible changes in the future. + +Complete the :pep:`587` :ref:`PyConfig C API ` by adding +:c:func:`PyInitConfig_AddModule` which can be used to add a built-in extension +module; a feature previously referred to as the "inittab". + +Add :c:func:`PyConfig_Get` and :c:func:`PyConfig_Set` functions to get and set +the current runtime configuration. + +:pep:`587` 'Python Initialization Configuration' unified all the ways +to configure Python's initialization. This PEP also unifies the configuration +of Python's preinitialization and initialization in a single API. +Moreover, this PEP only provides a single choice to embed Python, +instead of having two 'Python' and 'Isolated' choices (PEP 587), +to further simplify the API. + +The lower level PEP 587 PyConfig API remains available for use cases +with an intentionally higher level of coupling to CPython implementation details +(such as emulating the full functionality of CPython's CLI, including its +configuration mechanisms). + +(Contributed by Victor Stinner in :gh:`107954`.) + +.. seealso:: :pep:`741` and :pep:`587` + + New features in the C API ------------------------- @@ -2998,7 +2819,7 @@ New features in the C API * Add functions to manipulate the configuration of the current runtime Python interpreter - (:ref:`PEP 741: Python configuration C API `): + (:ref:`PEP 741: Python configuration C API `): * :c:func:`PyConfig_Get` * :c:func:`PyConfig_GetInt` @@ -3008,7 +2829,7 @@ New features in the C API (Contributed by Victor Stinner in :gh:`107954`.) * Add functions to configure Python initialization - (:ref:`PEP 741: Python configuration C API `): + (:ref:`PEP 741: Python configuration C API `): * :c:func:`Py_InitializeFromInitConfig` * :c:func:`PyInitConfig_AddModule` @@ -3235,9 +3056,25 @@ Deprecated C APIs .. include:: ../deprecations/c-api-pending-removal-in-future.rst +.. _whatsnew314-build-changes: + Build Changes ============= +* :pep:`776`: Emscripten is now an officially supported platform at + :pep:`tier 3 <11#tier-3>`. As a part of this effort, more than 25 bugs in + `Emscripten libc`__ were fixed. Emscripten now includes support + for :mod:`ctypes`, :mod:`termios`, and :mod:`fcntl`, as well as + experimental support for the new :ref:`default interactive shell + `. + (Contributed by R. Hood Chatham in :gh:`127146`, :gh:`127683`, and :gh:`136931`.) + + __ https://emscripten.org/docs/porting/emscripten-runtime-environment.html + +* Official Android binary releases are now provided on python.org__. + + __ https://www.python.org/downloads/android/ + * GNU Autoconf 2.72 is now required to generate :file:`configure`. (Contributed by Erlend Aasland in :gh:`115765`.) @@ -3259,12 +3096,22 @@ Build Changes * The new :file:`configure` option :option:`--with-tail-call-interp` may be used to enable the experimental tail call interpreter. - See :ref:`whatsnew314-tail-call` for further details. + See :ref:`whatsnew314-tail-call-interpreter` for further details. * To disable the new remote debugging support, use the :option:`--without-remote-debug` :file:`configure` option. This may be useful for security reasons. +* iOS and macOS apps can now be configured to redirect ``stdout`` and + ``stderr`` content to the system log. + (Contributed by Russell Keith-Magee in :gh:`127592`.) + +* The iOS testbed is now able to stream test output while the test is running. + The testbed can also be used to run the test suite of projects other than + CPython itself. + (Contributed by Russell Keith-Magee in :gh:`127592`.) + + .. _whatsnew314-build_details: :file:`build-details.json` @@ -3285,6 +3132,7 @@ which can be found by running ``sysconfig.get_path('stdlib')``. :pep:`739` -- ``build-details.json`` 1.0 -- a static description file for Python build details + .. _whatsnew314-no-more-pgp: Discontinuation of PGP signatures @@ -3301,6 +3149,66 @@ This change in release process was specified in :pep:`761`. .. _Sigstore: https://www.sigstore.dev/ +.. _whatsnew314-free-threaded-now-supported: + +Free-threaded Python is officially supported +-------------------------------------------- + +The free-threaded build of Python is now supported and no longer experimental. +This is the start of `phase II `__ where +free-threaded Python is officially supported but still optional. + +The free-threading team are confident that the project is on the right path, +and appreciate the continued dedication from everyone working to make +free-threading ready for broader adoption across the Python community. + +With these recommendations and the acceptance of this PEP, the Python developer +community should broadly advertise that free-threading is a supported +Python build option now and into the future, and that it will not be removed +without a proper deprecation schedule. + +Any decision to transition to `phase III `__, +with free-threading as the default or sole build of Python is still undecided, +and dependent on many factors both within CPython itself and the community. +This decision is for the future. + +.. seealso:: + + :pep:`779` + + `PEP 779's acceptance `__ + + +.. _whatsnew314-jit-compiler: + +Binary releases for the experimental just-in-time compiler +---------------------------------------------------------- + +The official macOS and Windows release binaries now include an *experimental* +just-in-time (JIT) compiler. Although it is **not** recommended for production +use, it can be tested by setting :envvar:`PYTHON_JIT=1 ` as an +environment variable. Downstream source builds and redistributors can use the +:option:`--enable-experimental-jit=yes-off` configuration option for similar +behavior. + +The JIT is at an early stage and still in active development. As such, the +typical performance impact of enabling it can range from 10% slower to 20% +faster, depending on workload. To aid in testing and evaluation, a set of +introspection functions has been provided in the :data:`sys._jit` namespace. +:func:`sys._jit.is_available` can be used to determine if the current executable +supports JIT compilation, while :func:`sys._jit.is_enabled` can be used to tell +if JIT compilation has been enabled for the current process. + +Currently, the most significant missing functionality is that native debuggers +and profilers like ``gdb`` and ``perf`` are unable to unwind through JIT frames +(Python debuggers and profilers, like :mod:`pdb` or :mod:`profile`, continue to +work without modification). Free-threaded builds do not support JIT compilation. + +Please report any bugs or major performance regressions that you encounter! + +.. seealso:: :pep:`744` + + Porting to Python 3.14 ====================== @@ -3347,7 +3255,7 @@ Changes in the Python API (Contributed by Jelle Zijlstra in :gh:`105499`.) * The runtime behavior of annotations has changed in various ways; see - :ref:`above ` for details. While most code that interacts + :ref:`above ` for details. While most code that interacts with annotations should continue to work, some undocumented details may behave differently. @@ -3362,6 +3270,92 @@ Changes in the Python API * On FreeBSD, :data:`sys.platform` no longer contains the major version number. +.. _whatsnew314-porting-annotations: + +Changes in annotations (:pep:`649` and :pep:`749`) +-------------------------------------------------- + +This section contains guidance on changes that may be needed to annotations +or Python code that interacts with or introspects annotations, +due to the changes related to :ref:`deferred evaluation of annotations +`. + +In the majority of cases, working code from older versions of Python +will not require any changes. + + +Implications for annotated code +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you define annotations in your code (for example, for use with a static type +checker), then this change probably does not affect you: you can keep +writing annotations the same way you did with previous versions of Python. + +You will likely be able to remove quoted strings in annotations, which are frequently +used for forward references. Similarly, if you use ``from __future__ import annotations`` +to avoid having to write strings in annotations, you may well be able to +remove that import once you support only Python 3.14 and newer. +However, if you rely on third-party libraries that read annotations, +those libraries may need changes to support unquoted annotations before they +work as expected. + + +Implications for readers of ``__annotations__`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your code reads the :attr:`~object.__annotations__` attribute on objects, +you may want to make changes in order to support code that relies on +deferred evaluation of annotations. +For example, you may want to use :func:`annotationlib.get_annotations` with +the :attr:`~annotationlib.Format.FORWARDREF` format, +as the :mod:`dataclasses` module now does. + +The external :pypi:`typing_extensions` package provides partial backports +of some of the functionality of the :mod:`annotationlib` module, +such as the :class:`~annotationlib.Format` enum and +the :func:`~annotationlib.get_annotations` function. +These can be used to write cross-version code that takes advantage of +the new behavior in Python 3.14. + + +Related changes +^^^^^^^^^^^^^^^ + +The changes in Python 3.14 are designed to rework how :attr:`!__annotations__` +works at runtime while minimizing breakage to code that contains +annotations in source code and to code that reads :attr:`!__annotations__`. +However, if you rely on undocumented details of the annotation behavior +or on private functions in the standard library, there are many ways in which +your code may not work in Python 3.14. +To safeguard your code against future changes, only use the documented +functionality of the :mod:`annotationlib` module. + +In particular, do not read annotations directly from the namespace dictionary +attribute of type objects. +Use :func:`annotationlib.get_annotate_from_class_namespace` during class +construction and :func:`annotationlib.get_annotations` afterwards. + +In previous releases, it was sometimes possible to access class annotations +from an instance of an annotated class. This behavior was undocumented +and accidental, and will no longer work in Python 3.14. + + +``from __future__ import annotations`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In Python 3.7, :pep:`563` introduced the ``from __future__ import annotations`` +:ref:`future statement `, which turns all annotations into strings. + +However, this statement is now deprecated and it is expected to be removed +in a future version of Python. +This removal will not happen until after Python 3.13 reaches its end of life +in 2029, being the last version of Python without support for deferred +evaluation of annotations. + +In Python 3.14, the behavior of code using ``from __future__ import annotations`` +is unchanged. + + Changes in the C API -------------------- From dadbb2662a405a34763db0298025f6af06342849 Mon Sep 17 00:00:00 2001 From: George Ogden <38294960+George-Ogden@users.noreply.github.com> Date: Sun, 5 Oct 2025 19:10:24 +0200 Subject: [PATCH 033/373] Replace ambiguous word "pound" by "hash" in `difflib` docs (#139601) --- Doc/library/difflib.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/difflib.rst b/Doc/library/difflib.rst index c55ecac3409..7a427078909 100644 --- a/Doc/library/difflib.rst +++ b/Doc/library/difflib.rst @@ -231,7 +231,7 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module. *linejunk*: A function that accepts a single string argument, and returns true if the string is junk, or false if not. The default is ``None``. There is also a module-level function :func:`IS_LINE_JUNK`, which filters out lines - without visible characters, except for at most one pound character (``'#'``) + without visible characters, except for at most one hash character (``'#'``) -- however the underlying :class:`SequenceMatcher` class does a dynamic analysis of which lines are so frequent as to constitute noise, and this usually works better than using this function. From d1ca001d357400d3f1f64e7fa48ace99a59c558f Mon Sep 17 00:00:00 2001 From: Skip Montanaro Date: Sun, 5 Oct 2025 13:15:46 -0500 Subject: [PATCH 034/373] gh-127330: Comment correction in _ssl.c (#139603) --- Modules/_ssl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 5d075fe942b..e1bdc4033ba 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -148,7 +148,7 @@ static void _PySSLFixErrno(void) { #endif /* Include generated data (error codes) */ -/* See make_ssl_data.h for notes on adding a new version. */ +/* See Tools/ssl/make_ssl_data.py for notes on adding a new version. */ #if (OPENSSL_VERSION_NUMBER >= 0x30401000L) #include "_ssl_data_35.h" #elif (OPENSSL_VERSION_NUMBER >= 0x30100000L) From 46de475af7225e6ef4c3fd08ee9202115a22de10 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Sun, 5 Oct 2025 20:45:58 +0100 Subject: [PATCH 035/373] GH-123299: Copyedit 3.14 What's New: Trivia (#139618) --- Doc/whatsnew/3.14.rst | 54 ++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 9179861ed0b..c226f57e502 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1,8 +1,9 @@ + **************************** What's new in Python 3.14 **************************** -:Editor: Hugo van Kemenade +:Editors: Adam Turner and Hugo van Kemenade .. Rules for maintenance: @@ -45,41 +46,48 @@ when researching a change. This article explains the new features in Python 3.14, compared to 3.13. - +Python 3.14 will be released on 7 October 2025. For full details, see the :ref:`changelog `. .. seealso:: :pep:`745` -- Python 3.14 release schedule -.. note:: - Prerelease users should be aware that this document is currently in draft - form. It will be updated substantially as Python 3.14 moves towards release, - so it's worth checking back even after reading earlier versions. - - -Summary -- release highlights +Summary -- Release highlights ============================= .. This section singles out the most important changes in Python 3.14. Brevity is key. -Python 3.14 will be the latest stable release of the Python -programming language, with a mix of changes to the language, the -implementation and the standard library. +Python 3.14 will be the latest stable release of the Python programming +language, with a mix of changes to the language, the implementation, +and the standard library. +The biggest changes include :ref:`template string literals +`, +:ref:`deferred evaluation of annotations `, +and support for :ref:`subinterpreters ` in +the standard library. -The biggest changes to the implementation include template strings (:pep:`750`), -deferred evaluation of annotations (:pep:`649`), -and a new type of interpreter that uses tail calls. - -The library changes include the addition of a new :mod:`!annotationlib` module -for introspecting and wrapping annotations (:pep:`749`), -a new :mod:`!compression.zstd` module for Zstandard support (:pep:`784`), -plus syntax highlighting in the REPL, +The library changes include significantly improved capabilities for +:ref:`introspection in asyncio `, +:ref:`support for Zstandard ` via a new +:mod:`compression.zstd` module, syntax highlighting in the REPL, as well as the usual deprecations and removals, and improvements in user-friendliness and correctness. +This article doesn't attempt to provide a complete specification +of all new features, but instead gives a convenient overview. +For full details refer to the documentation, +such as the :ref:`Library Reference ` +and :ref:`Language Reference `. +To understand the complete implementation and design rationale for a change, +refer to the PEP for a particular new feature; +but note that PEPs usually are not kept up-to-date +once a feature has been fully implemented. +See `Porting to Python 3.14`_ for guidance on upgrading from +earlier versions of Python. + -------------- .. PEP-sized items next. @@ -100,6 +108,7 @@ Interpreter improvements: * :ref:`A new type of interpreter ` * :ref:`Free-threaded mode improvements ` * :ref:`Improved error messages ` +* :ref:`Incremental garbage collection ` Significant improvements in the standard library: @@ -124,7 +133,8 @@ Release changes: * :pep:`779`: :ref:`Free-threaded Python is officially supported ` -* :pep:`761`: :ref:`Discontinuation of PGP signatures ` +* :pep:`761`: :ref:`PGP signatures have been discontinued for official releases + ` * :ref:`Windows and macOS binary releases now support the experimental just-in-time compiler ` * :ref:`Binary releases for Android are now provided ` @@ -2440,6 +2450,7 @@ asyncio blocking_code() runner.run(operation_two()) + email ----- @@ -2731,6 +2742,7 @@ CPython bytecode changes * Add the :opcode:`POP_ITER` opcode to support 'virtual' iterators. (Contributed by Mark Shannon in :gh:`132554`.) + Pseudo-instructions ------------------- From 3195da0b1a5dc8a03faa5142d4ab4a1549797e53 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Sun, 5 Oct 2025 21:15:36 +0100 Subject: [PATCH 036/373] gh-105812: Use the ``:deco:`` role in place of manual decorator markup (#139619) --- Doc/library/collections.abc.rst | 4 ++-- Doc/library/dataclasses.rst | 22 +++++++++---------- Doc/library/functools.rst | 5 ++--- Doc/library/typing.rst | 16 +++++++------- Doc/library/warnings.rst | 2 +- Doc/reference/datamodel.rst | 4 ++-- Doc/whatsnew/2.7.rst | 4 ++-- Doc/whatsnew/3.10.rst | 4 ++-- Misc/NEWS.d/3.10.0b1.rst | 6 ++--- Misc/NEWS.d/3.11.0a1.rst | 2 +- Misc/NEWS.d/3.11.0b1.rst | 2 +- Misc/NEWS.d/3.14.0a1.rst | 2 +- ...-08-27-17-05-36.gh-issue-138010.ZZJmPL.rst | 4 ++-- 13 files changed, 38 insertions(+), 39 deletions(-) diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index 3d126bc83f5..e6daccb91f2 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -336,7 +336,7 @@ Collections Abstract Base Classes -- Detailed Descriptions .. note:: In CPython, generator-based coroutines (:term:`generators ` - decorated with :func:`@types.coroutine `) are + decorated with :deco:`types.coroutine`) are *awaitables*, even though they do not have an :meth:`~object.__await__` method. Using ``isinstance(gencoro, Awaitable)`` for them will return ``False``. Use :func:`inspect.isawaitable` to detect them. @@ -354,7 +354,7 @@ Collections Abstract Base Classes -- Detailed Descriptions .. note:: In CPython, generator-based coroutines (:term:`generators ` - decorated with :func:`@types.coroutine `) are + decorated with :deco:`types.coroutine`) are *awaitables*, even though they do not have an :meth:`~object.__await__` method. Using ``isinstance(gencoro, Coroutine)`` for them will return ``False``. Use :func:`inspect.isawaitable` to detect them. diff --git a/Doc/library/dataclasses.rst b/Doc/library/dataclasses.rst index ca432f2768a..cff36e25822 100644 --- a/Doc/library/dataclasses.rst +++ b/Doc/library/dataclasses.rst @@ -317,7 +317,7 @@ Module contents :func:`!field`, then the class attribute for this field will be replaced by the specified *default* value. If *default* is not provided, then the class attribute will be deleted. The intent is - that after the :func:`@dataclass ` decorator runs, the class + that after the :deco:`dataclass` decorator runs, the class attributes will all contain the default values for the fields, just as if the default value itself were specified. For example, after:: @@ -427,7 +427,7 @@ Module contents :data:`typing.Any` is used for ``type``. The values of *init*, *repr*, *eq*, *order*, *unsafe_hash*, *frozen*, *match_args*, *kw_only*, *slots*, and *weakref_slot* have - the same meaning as they do in :func:`@dataclass `. + the same meaning as they do in :deco:`dataclass`. If *module* is defined, the :attr:`!__module__` attribute of the dataclass is set to that value. @@ -435,12 +435,12 @@ Module contents The *decorator* parameter is a callable that will be used to create the dataclass. It should take the class object as a first argument and the same keyword arguments - as :func:`@dataclass `. By default, the :func:`@dataclass ` + as :deco:`dataclass`. By default, the :deco:`dataclass` function is used. This function is not strictly required, because any Python mechanism for creating a new class with :attr:`~object.__annotations__` can - then apply the :func:`@dataclass ` function to convert that class to + then apply the :deco:`dataclass` function to convert that class to a dataclass. This function is provided as a convenience. For example:: @@ -569,7 +569,7 @@ Post-init processing def __post_init__(self): self.c = self.a + self.b -The :meth:`~object.__init__` method generated by :func:`@dataclass ` does not call base +The :meth:`~object.__init__` method generated by :deco:`dataclass` does not call base class :meth:`!__init__` methods. If the base class has an :meth:`!__init__` method that has to be called, it is common to call this method in a :meth:`__post_init__` method:: @@ -599,7 +599,7 @@ parameters to :meth:`!__post_init__`. Also see the warning about how Class variables --------------- -One of the few places where :func:`@dataclass ` actually inspects the type +One of the few places where :deco:`dataclass` actually inspects the type of a field is to determine if a field is a class variable as defined in :pep:`526`. It does this by checking if the type of the field is :data:`typing.ClassVar`. If a field is a ``ClassVar``, it is excluded @@ -612,7 +612,7 @@ module-level :func:`fields` function. Init-only variables ------------------- -Another place where :func:`@dataclass ` inspects a type annotation is to +Another place where :deco:`dataclass` inspects a type annotation is to determine if a field is an init-only variable. It does this by seeing if the type of a field is of type :class:`InitVar`. If a field is an :class:`InitVar`, it is considered a pseudo-field called an init-only @@ -646,7 +646,7 @@ Frozen instances ---------------- It is not possible to create truly immutable Python objects. However, -by passing ``frozen=True`` to the :func:`@dataclass ` decorator you can +by passing ``frozen=True`` to the :deco:`dataclass` decorator you can emulate immutability. In that case, dataclasses will add :meth:`~object.__setattr__` and :meth:`~object.__delattr__` methods to the class. These methods will raise a :exc:`FrozenInstanceError` when invoked. @@ -662,7 +662,7 @@ must use :meth:`!object.__setattr__`. Inheritance ----------- -When the dataclass is being created by the :func:`@dataclass ` decorator, +When the dataclass is being created by the :deco:`dataclass` decorator, it looks through all of the class's base classes in reverse MRO (that is, starting at :class:`object`) and, for each dataclass that it finds, adds the fields from that base class to an ordered mapping of fields. @@ -786,7 +786,7 @@ for :attr:`!x` when creating a class instance will share the same copy of :attr:`!x`. Because dataclasses just use normal Python class creation they also share this behavior. There is no general way for Data Classes to detect this condition. Instead, the -:func:`@dataclass ` decorator will raise a :exc:`ValueError` if it +:deco:`dataclass` decorator will raise a :exc:`ValueError` if it detects an unhashable default parameter. The assumption is that if a value is unhashable, it is mutable. This is a partial solution, but it does protect against many common errors. @@ -820,7 +820,7 @@ default value have the following special behaviors: :meth:`~object.__get__` or :meth:`!__set__` method is called rather than returning or overwriting the descriptor object. -* To determine whether a field contains a default value, :func:`@dataclass ` +* To determine whether a field contains a default value, :deco:`dataclass` will call the descriptor's :meth:`!__get__` method using its class access form: ``descriptor.__get__(obj=None, type=cls)``. If the descriptor returns a value in this case, it will be used as the diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index beec9b942af..f8ffb3f41d1 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -690,7 +690,7 @@ The :mod:`functools` module defines the following functions: return not arg ``@singledispatchmethod`` supports nesting with other decorators such as - :func:`@classmethod`. Note that to allow for + :deco:`classmethod`. Note that to allow for ``dispatcher.register``, ``singledispatchmethod`` must be the *outer most* decorator. Here is the ``Negator`` class with the ``neg`` methods bound to the class, rather than an instance of the class:: @@ -712,8 +712,7 @@ The :mod:`functools` module defines the following functions: return not arg The same pattern can be used for other similar decorators: - :func:`@staticmethod`, - :func:`@abstractmethod`, and others. + :deco:`staticmethod`, :deco:`~abc.abstractmethod`, and others. .. versionadded:: 3.8 diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index e0122986e9b..cfeeb19efbd 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1390,7 +1390,7 @@ These can be used as types in annotations. They all support subscription using Using ``Annotated[T, x]`` as an annotation still allows for static typechecking of ``T``, as type checkers will simply ignore the metadata ``x``. In this way, ``Annotated`` differs from the - :func:`@no_type_check ` decorator, which can also be used for + :deco:`no_type_check` decorator, which can also be used for adding annotations outside the scope of the typing system, but completely disables typechecking for a function or class. @@ -2815,7 +2815,7 @@ Protocols --------- The following protocols are provided by the :mod:`!typing` module. All are decorated -with :func:`@runtime_checkable `. +with :deco:`runtime_checkable`. .. class:: SupportsAbs @@ -2996,7 +2996,7 @@ Functions and decorators The presence of ``@dataclass_transform()`` tells a static type checker that the decorated object performs runtime "magic" that transforms a class in a similar way to - :func:`@dataclasses.dataclass `. + :deco:`dataclasses.dataclass`. Example usage with a decorator function: @@ -3034,14 +3034,14 @@ Functions and decorators The ``CustomerModel`` classes defined above will be treated by type checkers similarly to classes created with - :func:`@dataclasses.dataclass `. + :deco:`dataclasses.dataclass`. For example, type checkers will assume these classes have ``__init__`` methods that accept ``id`` and ``name``. The decorated class, metaclass, or function may accept the following bool arguments which type checkers will assume have the same effect as they would have on the - :func:`@dataclasses.dataclass` decorator: ``init``, + :deco:`dataclasses.dataclass` decorator: ``init``, ``eq``, ``order``, ``unsafe_hash``, ``frozen``, ``match_args``, ``kw_only``, and ``slots``. It must be possible for the value of these arguments (``True`` or ``False``) to be statically evaluated. @@ -3169,12 +3169,12 @@ Functions and decorators .. function:: get_overloads(func) - Return a sequence of :func:`@overload `-decorated definitions for + Return a sequence of :deco:`overload`-decorated definitions for *func*. *func* is the function object for the implementation of the overloaded function. For example, given the definition of ``process`` in - the documentation for :func:`@overload `, + the documentation for :deco:`overload`, ``get_overloads(process)`` will return a sequence of three function objects for the three defined overloads. If called on a function with no overloads, ``get_overloads()`` returns an empty sequence. @@ -3331,7 +3331,7 @@ Introspection helpers If *globalns* or *localns* is not given, appropriate namespace dictionaries are inferred from *obj*. * ``None`` is replaced with :class:`types.NoneType`. - * If :func:`@no_type_check ` has been applied to *obj*, an + * If :deco:`no_type_check` has been applied to *obj*, an empty dictionary is returned. * If *obj* is a class ``C``, the function returns a dictionary that merges annotations from ``C``'s base classes with those on ``C`` directly. This diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst index 5b17bd009b3..f9c8c4fc3a8 100644 --- a/Doc/library/warnings.rst +++ b/Doc/library/warnings.rst @@ -590,7 +590,7 @@ Available Functions The deprecation message passed to the decorator is saved in the ``__deprecated__`` attribute on the decorated object. If applied to an overload, the decorator - must be after the :func:`@overload ` decorator + must be after the :deco:`~typing.overload` decorator for the attribute to exist on the overload as returned by :func:`typing.get_overloads`. diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 7185d68c5cc..29f82fc12da 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2558,7 +2558,7 @@ instance dictionary. In contrast, non-data descriptors can be overridden by instances. Python methods (including those decorated with -:func:`@staticmethod ` and :func:`@classmethod `) are +:deco:`staticmethod` and :deco:`classmethod`) are implemented as non-data descriptors. Accordingly, instances can redefine and override methods. This allows individual instances to acquire behaviors that differ from other instances of the same class. @@ -2993,7 +2993,7 @@ class method ``__class_getitem__()``. When defined on a class, ``__class_getitem__()`` is automatically a class method. As such, there is no need for it to be decorated with - :func:`@classmethod` when it is defined. + :deco:`classmethod` when it is defined. The purpose of *__class_getitem__* diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index bcc5a3d5690..09feb185b82 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -858,8 +858,8 @@ Some smaller changes made to the core Python language are: .. XXX bytearray doesn't seem to be documented -* When using :class:`@classmethod ` and - :class:`@staticmethod ` to wrap +* When using :deco:`classmethod` and + :deco:`staticmethod` to wrap methods as class or static methods, the wrapper object now exposes the wrapped function as their :attr:`~method.__func__` attribute. diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 8db57f6f22f..d8251185fa7 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -847,8 +847,8 @@ Other Language Changes respectively. (Contributed by Joshua Bronson, Daniel Pope, and Justin Wang in :issue:`31861`.) -* Static methods (:func:`@staticmethod `) and class methods - (:func:`@classmethod `) now inherit the method attributes +* Static methods (:deco:`staticmethod`) and class methods + (:deco:`classmethod`) now inherit the method attributes (``__module__``, ``__name__``, ``__qualname__``, ``__doc__``, ``__annotations__``) and have a new ``__wrapped__`` attribute. Moreover, static methods are now callable as regular functions. diff --git a/Misc/NEWS.d/3.10.0b1.rst b/Misc/NEWS.d/3.10.0b1.rst index 406a5d7853e..5bc78b9007a 100644 --- a/Misc/NEWS.d/3.10.0b1.rst +++ b/Misc/NEWS.d/3.10.0b1.rst @@ -402,8 +402,8 @@ the heap. Should speed up dispatch in the interpreter. .. nonce: eUn4p5 .. section: Core and Builtins -Static methods (:func:`@staticmethod `) and class methods -(:func:`@classmethod `) now inherit the method attributes +Static methods (:deco:`staticmethod`) and class methods +(:deco:`classmethod`) now inherit the method attributes (``__module__``, ``__name__``, ``__qualname__``, ``__doc__``, ``__annotations__``) and have a new ``__wrapped__`` attribute. Patch by Victor Stinner. @@ -454,7 +454,7 @@ file locations. .. nonce: VSF3vg .. section: Core and Builtins -Static methods (:func:`@staticmethod `) are now callable as +Static methods (:deco:`staticmethod`) are now callable as regular functions. Patch by Victor Stinner. .. diff --git a/Misc/NEWS.d/3.11.0a1.rst b/Misc/NEWS.d/3.11.0a1.rst index 94e4868eb29..c7e57754ee3 100644 --- a/Misc/NEWS.d/3.11.0a1.rst +++ b/Misc/NEWS.d/3.11.0a1.rst @@ -2953,7 +2953,7 @@ support for Metadata 2.2. .. nonce: xTUyyX .. section: Library -Remove the :func:`@asyncio.coroutine ` :term:`decorator` +Remove the :deco:`asyncio.coroutine` :term:`decorator` enabling legacy generator-based coroutines to be compatible with async/await code; remove :class:`asyncio.coroutines.CoroWrapper` used for wrapping legacy coroutine objects in the debug mode. The decorator has been diff --git a/Misc/NEWS.d/3.11.0b1.rst b/Misc/NEWS.d/3.11.0b1.rst index c3a1942b881..7b8b983ebf9 100644 --- a/Misc/NEWS.d/3.11.0b1.rst +++ b/Misc/NEWS.d/3.11.0b1.rst @@ -664,7 +664,7 @@ for :func:`os.fcopyfile` available in macOs. .. nonce: l1p7CJ .. section: Library -For :func:`@dataclass `, add *weakref_slot*. +For :deco:`~dataclasses.dataclass`, add *weakref_slot*. The new parameter defaults to ``False``. If true, and if ``slots=True``, add a slot named ``"__weakref__"``, which will allow instances to be weakref'd. Contributed by Eric V. Smith diff --git a/Misc/NEWS.d/3.14.0a1.rst b/Misc/NEWS.d/3.14.0a1.rst index 07f2f521ece..305a0b65b98 100644 --- a/Misc/NEWS.d/3.14.0a1.rst +++ b/Misc/NEWS.d/3.14.0a1.rst @@ -1999,7 +1999,7 @@ with an escape character. .. nonce: vi2bP- .. section: Library -:func:`@warnings.deprecated ` now copies the coroutine +:deco:`warnings.deprecated` now copies the coroutine status of functions and methods so that :func:`inspect.iscoroutinefunction` returns the correct result. diff --git a/Misc/NEWS.d/next/Library/2025-08-27-17-05-36.gh-issue-138010.ZZJmPL.rst b/Misc/NEWS.d/next/Library/2025-08-27-17-05-36.gh-issue-138010.ZZJmPL.rst index 117f3ad6c67..1cc031a8bf5 100644 --- a/Misc/NEWS.d/next/Library/2025-08-27-17-05-36.gh-issue-138010.ZZJmPL.rst +++ b/Misc/NEWS.d/next/Library/2025-08-27-17-05-36.gh-issue-138010.ZZJmPL.rst @@ -1,4 +1,4 @@ -Fix an issue where defining a class with an :func:`@warnings.deprecated -`-decorated base class may not invoke the correct +Fix an issue where defining a class with a :deco:`warnings.deprecated`-decorated +base class may not invoke the correct :meth:`~object.__init_subclass__` method in cases involving multiple inheritance. Patch by Brian Schubert. From a9b0506d8db1aff8318759ed9324be56bf33eb31 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Mon, 6 Oct 2025 06:19:48 +0100 Subject: [PATCH 037/373] gh-137242: Allow Android testbed to take all Python command-line options (#138805) Modifies the Android test runner to ensure that all valid Python command line options are preserved when running the test suite. --- .github/workflows/build.yml | 2 +- Android/android.py | 75 +++++++++------- .../java/org/python/testbed/PythonSuite.kt | 2 +- .../testbed/app/src/main/c/main_activity.c | 88 +++++++++++++++---- .../java/org/python/testbed/MainActivity.kt | 40 ++++----- .../src/main/python/android_testbed_main.py | 48 ---------- Lib/test/libregrtest/main.py | 26 ++++-- 7 files changed, 152 insertions(+), 129 deletions(-) delete mode 100644 Android/testbed/app/src/main/python/android_testbed_main.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 47d38b75429..252ec831ed9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -421,7 +421,7 @@ jobs: with: persist-credentials: false - name: Build and test - run: ./Android/android.py ci ${{ matrix.arch }}-linux-android + run: ./Android/android.py ci --fast-ci ${{ matrix.arch }}-linux-android build-wasi: name: 'WASI' diff --git a/Android/android.py b/Android/android.py index b810a6bfb3c..25bb4ca70b5 100755 --- a/Android/android.py +++ b/Android/android.py @@ -2,6 +2,7 @@ import asyncio import argparse +import json import os import platform import re @@ -552,27 +553,33 @@ async def gradle_task(context): task_prefix = "connected" env["ANDROID_SERIAL"] = context.connected - if context.command: - mode = "-c" - module = context.command - else: - mode = "-m" - module = context.module or "test" + if context.ci_mode: + context.args[0:0] = [ + # See _add_ci_python_opts in libregrtest/main.py. + "-W", "error", "-bb", "-E", + + # Randomization is disabled because order-dependent failures are + # much less likely to pass on a rerun in single-process mode. + "-m", "test", + f"--{context.ci_mode}-ci", "--single-process", "--no-randomize" + ] + + if not any(arg in context.args for arg in ["-c", "-m"]): + context.args[0:0] = ["-m", "test"] args = [ gradlew, "--console", "plain", f"{task_prefix}DebugAndroidTest", ] + [ - # Build-time properties - f"-Ppython.{name}={value}" + f"-P{name}={value}" for name, value in [ - ("sitePackages", context.site_packages), ("cwd", context.cwd) - ] if value - ] + [ - # Runtime properties - f"-Pandroid.testInstrumentationRunnerArguments.python{name}={value}" - for name, value in [ - ("Mode", mode), ("Module", module), ("Args", join_command(context.args)) - ] if value + ("python.sitePackages", context.site_packages), + ("python.cwd", context.cwd), + ( + "android.testInstrumentationRunnerArguments.pythonArgs", + json.dumps(context.args), + ), + ] + if value ] if context.verbose >= 2: args.append("--info") @@ -740,15 +747,14 @@ def ci(context): else: with TemporaryDirectory(prefix=SCRIPT_NAME) as temp_dir: print("::group::Tests") + # Prove the package is self-contained by using it to run the tests. shutil.unpack_archive(package_path, temp_dir) - - # Randomization is disabled because order-dependent failures are - # much less likely to pass on a rerun in single-process mode. - launcher_args = ["--managed", "maxVersion", "-v"] - test_args = ["--fast-ci", "--single-process", "--no-randomize"] + launcher_args = [ + "--managed", "maxVersion", "-v", f"--{context.ci_mode}-ci" + ] run( - ["./android.py", "test", *launcher_args, "--", *test_args], + ["./android.py", "test", *launcher_args], cwd=temp_dir ) print("::endgroup::") @@ -831,18 +837,11 @@ def add_parser(*args, **kwargs): test.add_argument( "--cwd", metavar="DIR", type=abspath, help="Directory to copy as the app's working directory.") - - mode_group = test.add_mutually_exclusive_group() - mode_group.add_argument( - "-c", dest="command", help="Execute the given Python code.") - mode_group.add_argument( - "-m", dest="module", help="Execute the module with the given name.") - test.epilog = ( - "If neither -c nor -m are passed, the default is '-m test', which will " - "run Python's own test suite.") test.add_argument( - "args", nargs="*", help=f"Arguments to add to sys.argv. " - f"Separate them from {SCRIPT_NAME}'s own arguments with `--`.") + "args", nargs="*", help=f"Python command-line arguments. " + f"Separate them from {SCRIPT_NAME}'s own arguments with `--`. " + f"If neither -c nor -m are included, `-m test` will be prepended, " + f"which will run Python's own test suite.") # Package arguments. for subcommand in [package, ci]: @@ -850,6 +849,16 @@ def add_parser(*args, **kwargs): "-g", action="store_true", default=False, dest="debug", help="Include debug information in package") + # CI arguments + for subcommand in [test, ci]: + group = subcommand.add_mutually_exclusive_group(required=subcommand is ci) + group.add_argument( + "--fast-ci", action="store_const", dest="ci_mode", const="fast", + help="Add test arguments for GitHub Actions") + group.add_argument( + "--slow-ci", action="store_const", dest="ci_mode", const="slow", + help="Add test arguments for buildbots") + return parser.parse_args() diff --git a/Android/testbed/app/src/androidTest/java/org/python/testbed/PythonSuite.kt b/Android/testbed/app/src/androidTest/java/org/python/testbed/PythonSuite.kt index 94be52dd2dc..e57243566f9 100644 --- a/Android/testbed/app/src/androidTest/java/org/python/testbed/PythonSuite.kt +++ b/Android/testbed/app/src/androidTest/java/org/python/testbed/PythonSuite.kt @@ -20,7 +20,7 @@ class PythonSuite { val status = PythonTestRunner( InstrumentationRegistry.getInstrumentation().targetContext ).run( - InstrumentationRegistry.getArguments() + InstrumentationRegistry.getArguments().getString("pythonArgs")!!, ) assertEquals(0, status) } finally { diff --git a/Android/testbed/app/src/main/c/main_activity.c b/Android/testbed/app/src/main/c/main_activity.c index ec7f93a3e5e..7f024f0a348 100644 --- a/Android/testbed/app/src/main/c/main_activity.c +++ b/Android/testbed/app/src/main/c/main_activity.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,13 @@ static void throw_runtime_exception(JNIEnv *env, const char *message) { message); } +static void throw_errno(JNIEnv *env, const char *error_prefix) { + char error_message[1024]; + snprintf(error_message, sizeof(error_message), + "%s: %s", error_prefix, strerror(errno)); + throw_runtime_exception(env, error_message); +} + // --- Stdio redirection ------------------------------------------------------ @@ -95,10 +103,7 @@ JNIEXPORT void JNICALL Java_org_python_testbed_PythonTestRunner_redirectStdioToL for (StreamInfo *si = STREAMS; si->file; si++) { char *error_prefix; if ((error_prefix = redirect_stream(si))) { - char error_message[1024]; - snprintf(error_message, sizeof(error_message), - "%s: %s", error_prefix, strerror(errno)); - throw_runtime_exception(env, error_message); + throw_errno(env, error_prefix); return; } } @@ -107,13 +112,38 @@ JNIEXPORT void JNICALL Java_org_python_testbed_PythonTestRunner_redirectStdioToL // --- Python initialization --------------------------------------------------- -static PyStatus set_config_string( - JNIEnv *env, PyConfig *config, wchar_t **config_str, jstring value -) { - const char *value_utf8 = (*env)->GetStringUTFChars(env, value, NULL); - PyStatus status = PyConfig_SetBytesString(config, config_str, value_utf8); - (*env)->ReleaseStringUTFChars(env, value, value_utf8); - return status; +static char *init_signals() { + // Some tests use SIGUSR1, but that's blocked by default in an Android app in + // order to make it available to `sigwait` in the Signal Catcher thread. + // (https://cs.android.com/android/platform/superproject/+/android14-qpr3-release:art/runtime/signal_catcher.cc). + // That thread's functionality is only useful for debugging the JVM, so disabling + // it should not weaken the tests. + // + // There's no safe way of stopping the thread completely (#123982), but simply + // unblocking SIGUSR1 is enough to fix most tests. + // + // However, in tests that generate multiple different signals in quick + // succession, it's possible for SIGUSR1 to arrive while the main thread is busy + // running the C-level handler for a different signal. In that case, the SIGUSR1 + // may be sent to the Signal Catcher thread instead, which will generate a log + // message containing the text "reacting to signal". + // + // Such tests may need to be changed in one of the following ways: + // * Use a signal other than SIGUSR1 (e.g. test_stress_delivery_simultaneous in + // test_signal.py). + // * Send the signal to a specific thread rather than the whole process (e.g. + // test_signals in test_threadsignals.py. + sigset_t set; + if (sigemptyset(&set)) { + return "sigemptyset"; + } + if (sigaddset(&set, SIGUSR1)) { + return "sigaddset"; + } + if ((errno = pthread_sigmask(SIG_UNBLOCK, &set, NULL))) { + return "pthread_sigmask"; + } + return NULL; } static void throw_status(JNIEnv *env, PyStatus status) { @@ -121,27 +151,47 @@ static void throw_status(JNIEnv *env, PyStatus status) { } JNIEXPORT int JNICALL Java_org_python_testbed_PythonTestRunner_runPython( - JNIEnv *env, jobject obj, jstring home, jstring runModule + JNIEnv *env, jobject obj, jstring home, jarray args ) { + const char *home_utf8 = (*env)->GetStringUTFChars(env, home, NULL); + char cwd[PATH_MAX]; + snprintf(cwd, sizeof(cwd), "%s/%s", home_utf8, "cwd"); + if (chdir(cwd)) { + throw_errno(env, "chdir"); + return 1; + } + + char *error_prefix; + if ((error_prefix = init_signals())) { + throw_errno(env, error_prefix); + return 1; + } + PyConfig config; PyStatus status; - PyConfig_InitIsolatedConfig(&config); + PyConfig_InitPythonConfig(&config); - status = set_config_string(env, &config, &config.home, home); - if (PyStatus_Exception(status)) { + jsize argc = (*env)->GetArrayLength(env, args); + const char *argv[argc + 1]; + for (int i = 0; i < argc; i++) { + jobject arg = (*env)->GetObjectArrayElement(env, args, i); + argv[i] = (*env)->GetStringUTFChars(env, arg, NULL); + } + argv[argc] = NULL; + + // PyConfig_SetBytesArgv "must be called before other methods, since the + // preinitialization configuration depends on command line arguments" + if (PyStatus_Exception(status = PyConfig_SetBytesArgv(&config, argc, (char**)argv))) { throw_status(env, status); return 1; } - status = set_config_string(env, &config, &config.run_module, runModule); + status = PyConfig_SetBytesString(&config, &config.home, home_utf8); if (PyStatus_Exception(status)) { throw_status(env, status); return 1; } - // Some tests generate SIGPIPE and SIGXFSZ, which should be ignored. - config.install_signal_handlers = 1; - status = Py_InitializeFromConfig(&config); if (PyStatus_Exception(status)) { throw_status(env, status); diff --git a/Android/testbed/app/src/main/java/org/python/testbed/MainActivity.kt b/Android/testbed/app/src/main/java/org/python/testbed/MainActivity.kt index ef28948486f..5727b0fe6c3 100644 --- a/Android/testbed/app/src/main/java/org/python/testbed/MainActivity.kt +++ b/Android/testbed/app/src/main/java/org/python/testbed/MainActivity.kt @@ -5,6 +5,7 @@ import android.os.* import android.system.Os import android.widget.TextView import androidx.appcompat.app.* +import org.json.JSONArray import java.io.* @@ -15,30 +16,25 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) - val status = PythonTestRunner(this).run("-m", "test", "-W -uall") + val status = PythonTestRunner(this).run("""["-m", "test", "-W", "-uall"]""") findViewById(R.id.tvHello).text = "Exit status $status" } } class PythonTestRunner(val context: Context) { - fun run(instrumentationArgs: Bundle) = run( - instrumentationArgs.getString("pythonMode")!!, - instrumentationArgs.getString("pythonModule")!!, - instrumentationArgs.getString("pythonArgs") ?: "", - ) - /** Run Python. * - * @param mode Either "-c" or "-m". - * @param module Python statements for "-c" mode, or a module name for - * "-m" mode. - * @param args Arguments to add to sys.argv. Will be parsed by `shlex.split`. + * @param args Python command-line, encoded as JSON. * @return The Python exit status: zero on success, nonzero on failure. */ - fun run(mode: String, module: String, args: String) : Int { - Os.setenv("PYTHON_MODE", mode, true) - Os.setenv("PYTHON_MODULE", module, true) - Os.setenv("PYTHON_ARGS", args, true) + fun run(args: String) : Int { + // We leave argument 0 as an empty string, which is a placeholder for the + // executable name in embedded mode. + val argsJsonArray = JSONArray(args) + val argsStringArray = Array(argsJsonArray.length() + 1) { it -> ""} + for (i in 0..) : Int } diff --git a/Android/testbed/app/src/main/python/android_testbed_main.py b/Android/testbed/app/src/main/python/android_testbed_main.py deleted file mode 100644 index 31b8e5343a8..00000000000 --- a/Android/testbed/app/src/main/python/android_testbed_main.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -import runpy -import shlex -import signal -import sys - -# Some tests use SIGUSR1, but that's blocked by default in an Android app in -# order to make it available to `sigwait` in the Signal Catcher thread. -# (https://cs.android.com/android/platform/superproject/+/android14-qpr3-release:art/runtime/signal_catcher.cc). -# That thread's functionality is only useful for debugging the JVM, so disabling -# it should not weaken the tests. -# -# There's no safe way of stopping the thread completely (#123982), but simply -# unblocking SIGUSR1 is enough to fix most tests. -# -# However, in tests that generate multiple different signals in quick -# succession, it's possible for SIGUSR1 to arrive while the main thread is busy -# running the C-level handler for a different signal. In that case, the SIGUSR1 -# may be sent to the Signal Catcher thread instead, which will generate a log -# message containing the text "reacting to signal". -# -# Such tests may need to be changed in one of the following ways: -# * Use a signal other than SIGUSR1 (e.g. test_stress_delivery_simultaneous in -# test_signal.py). -# * Send the signal to a specific thread rather than the whole process (e.g. -# test_signals in test_threadsignals.py. -signal.pthread_sigmask(signal.SIG_UNBLOCK, [signal.SIGUSR1]) - -mode = os.environ["PYTHON_MODE"] -module = os.environ["PYTHON_MODULE"] -sys.argv[1:] = shlex.split(os.environ["PYTHON_ARGS"]) - -cwd = f"{sys.prefix}/cwd" -if not os.path.exists(cwd): - # Empty directories are lost in the asset packing/unpacking process. - os.mkdir(cwd) -os.chdir(cwd) - -if mode == "-c": - # In -c mode, sys.path starts with an empty string, which means whatever the current - # working directory is at the moment of each import. - sys.path.insert(0, "") - exec(module, {}) -elif mode == "-m": - sys.path.insert(0, os.getcwd()) - runpy.run_module(module, run_name="__main__", alter_sys=True) -else: - raise ValueError(f"unknown mode: {mode}") diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index a2d01b157ac..0fc2548789e 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -646,15 +646,23 @@ def _add_cross_compile_opts(self, regrtest_opts): return (environ, keep_environ) def _add_ci_python_opts(self, python_opts, keep_environ): - # --fast-ci and --slow-ci add options to Python: - # "-u -W default -bb -E" + # --fast-ci and --slow-ci add options to Python. + # + # Some platforms run tests in embedded mode and cannot change options + # after startup, so if this function changes, consider also updating: + # * gradle_task in Android/android.py - # Unbuffered stdout and stderr - if not sys.stdout.write_through: + # Unbuffered stdout and stderr. This isn't helpful on Android, because + # it would cause lines to be split into multiple log messages. + if not sys.stdout.write_through and sys.platform != "android": python_opts.append('-u') - # Add warnings filter 'error' - if 'default' not in sys.warnoptions: + # Add warnings filter 'error', unless the user specified a different + # filter. Ignore BytesWarning since it's controlled by '-b' below. + if not [ + opt for opt in sys.warnoptions + if not opt.endswith("::BytesWarning") + ]: python_opts.extend(('-W', 'error')) # Error on bytes/str comparison @@ -673,8 +681,12 @@ def _execute_python(self, cmd, environ): cmd_text = shlex.join(cmd) try: - print(f"+ {cmd_text}", flush=True) + # Android and iOS run tests in embedded mode. To update their + # Python options, see the comment in _add_ci_python_opts. + if not cmd[0]: + raise ValueError("No Python executable is present") + print(f"+ {cmd_text}", flush=True) if hasattr(os, 'execv') and not MS_WINDOWS: os.execv(cmd[0], cmd) # On success, execv() do no return. From 6f3dae0dc5ccd47b2b8a6e052244353d8c37e59b Mon Sep 17 00:00:00 2001 From: Furkan Onder Date: Mon, 6 Oct 2025 09:38:01 +0000 Subject: [PATCH 038/373] gh-139624: Skip problematic locales on AIX in test_date_locale2 (GH-139625) --- Lib/test/test_strptime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index d12816c9084..40e114aada6 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -570,7 +570,7 @@ def test_date_locale(self): def test_date_locale2(self): # Test %x directive loc = locale.getlocale(locale.LC_TIME)[0] - if sys.platform.startswith('sunos'): + if sys.platform.startswith(('sunos', 'aix')): if loc in ('en_US', 'de_DE', 'ar_AE'): self.skipTest(f'locale {loc!r} may not work on this platform') self.roundtrip('%x', slice(0, 3), (1900, 1, 1, 0, 0, 0, 0, 1, 0)) From 36a6c2cdfaddc4a82d14f2c84b04e05089802876 Mon Sep 17 00:00:00 2001 From: yihong Date: Mon, 6 Oct 2025 18:52:45 +0800 Subject: [PATCH 039/373] gh-139646: fix typo in `pickletools` error message (#139647) Signed-off-by: yihong0618 --- Lib/pickletools.py | 2 +- Lib/test/test_pickletools.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/pickletools.py b/Lib/pickletools.py index bcddfb722bd..254b6c7fcc9 100644 --- a/Lib/pickletools.py +++ b/Lib/pickletools.py @@ -348,7 +348,7 @@ def read_stringnl(f, decode=True, stripquotes=True, *, encoding='latin-1'): for q in (b'"', b"'"): if data.startswith(q): if not data.endswith(q): - raise ValueError("strinq quote %r not found at both " + raise ValueError("string quote %r not found at both " "ends of %r" % (q, data)) data = data[1:-1] break diff --git a/Lib/test/test_pickletools.py b/Lib/test/test_pickletools.py index a178d3353ee..cf990874621 100644 --- a/Lib/test/test_pickletools.py +++ b/Lib/test/test_pickletools.py @@ -384,13 +384,13 @@ def test_string_without_quotes(self): self.check_dis_error(b'Sabc"\n.', '', "no string quotes around b'abc\"'") self.check_dis_error(b"S'abc\n.", '', - '''strinq quote b"'" not found at both ends of b"'abc"''') + '''string quote b"'" not found at both ends of b"'abc"''') self.check_dis_error(b'S"abc\n.', '', - r"""strinq quote b'"' not found at both ends of b'"abc'""") + r"""string quote b'"' not found at both ends of b'"abc'""") self.check_dis_error(b"S'abc\"\n.", '', - r"""strinq quote b"'" not found at both ends of b'\\'abc"'""") + r"""string quote b"'" not found at both ends of b'\\'abc"'""") self.check_dis_error(b"S\"abc'\n.", '', - r"""strinq quote b'"' not found at both ends of b'"abc\\''""") + r"""string quote b'"' not found at both ends of b'"abc\\''""") def test_binstring(self): self.check_dis(b"T\x03\x00\x00\x00abc.", '''\ From b73aaffb3da0174ee8ae62767b6c363996cfe90c Mon Sep 17 00:00:00 2001 From: Cycloctane Date: Mon, 6 Oct 2025 20:40:48 +0800 Subject: [PATCH 040/373] gh-133951: Fix purelib packages not found in test_peg_generator TestCParser (GH-139607) also includes purelib in TestCParser import context --- Lib/test/test_peg_generator/test_c_parser.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_peg_generator/test_c_parser.py b/Lib/test/test_peg_generator/test_c_parser.py index aa01a9b8f7e..395f15b9a62 100644 --- a/Lib/test/test_peg_generator/test_c_parser.py +++ b/Lib/test/test_peg_generator/test_c_parser.py @@ -100,11 +100,15 @@ def setUpClass(cls): with contextlib.ExitStack() as stack: python_exe = stack.enter_context(support.setup_venv_with_pip_setuptools("venv")) - sitepackages = subprocess.check_output( + platlib_path = subprocess.check_output( [python_exe, "-c", "import sysconfig; print(sysconfig.get_path('platlib'))"], text=True, ).strip() - stack.enter_context(import_helper.DirsOnSysPath(sitepackages)) + purelib_path = subprocess.check_output( + [python_exe, "-c", "import sysconfig; print(sysconfig.get_path('purelib'))"], + text=True, + ).strip() + stack.enter_context(import_helper.DirsOnSysPath(platlib_path, purelib_path)) cls.addClassCleanup(stack.pop_all().close) @support.requires_venv_with_pip() From 69cfad0b3e1e2805914cb647cce4c7c2bfed5910 Mon Sep 17 00:00:00 2001 From: Cycloctane Date: Mon, 6 Oct 2025 21:04:59 +0800 Subject: [PATCH 041/373] gh-116488: Mention `dict.get` in the data structures tutorial (GH-139643) --- Doc/tutorial/datastructures.rst | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Doc/tutorial/datastructures.rst b/Doc/tutorial/datastructures.rst index db8a066b369..1332c53f396 100644 --- a/Doc/tutorial/datastructures.rst +++ b/Doc/tutorial/datastructures.rst @@ -512,8 +512,12 @@ dictionary; this is also the way dictionaries are written on output. The main operations on a dictionary are storing a value with some key and extracting the value given the key. It is also possible to delete a key:value pair with ``del``. If you store using a key that is already in use, the old -value associated with that key is forgotten. It is an error to extract a value -using a non-existent key. +value associated with that key is forgotten. + +Extracting a value for a non-existent key by subscripting (``d[key]``) raises a +:exc:`KeyError`. To avoid getting this error when trying to access a possibly +non-existent key, use the :meth:`~dict.get` method instead, which returns +``None`` (or a specified default value) if the key is not in the dictionary. Performing ``list(d)`` on a dictionary returns a list of all the keys used in the dictionary, in insertion order (if you want it sorted, just use @@ -528,6 +532,12 @@ Here is a small example using a dictionary:: {'jack': 4098, 'sape': 4139, 'guido': 4127} >>> tel['jack'] 4098 + >>> tel['irv'] + Traceback (most recent call last): + File "", line 1, in + KeyError: 'irv' + >>> print(tel.get('irv')) + None >>> del tel['sape'] >>> tel['irv'] = 4127 >>> tel From 708de26e31b65cd3ae768882c8da59284917a80d Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Mon, 6 Oct 2025 17:51:10 +0300 Subject: [PATCH 042/373] gh-133210: Fix `test_pydoc` without docstrings (#139654) --- Lib/test/test_pydoc/test_pydoc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py index 65ad7649b9e..5aa8d92057e 100644 --- a/Lib/test/test_pydoc/test_pydoc.py +++ b/Lib/test/test_pydoc/test_pydoc.py @@ -1902,7 +1902,7 @@ def test_text_doc_routines_in_class(self, cls=pydocfodder.B): else: self.assertIn(' | get(...) method of builtins.dict instance', lines) self.assertIn(' | dict_get = get(...) method of builtins.dict instance', lines) - self.assertIn(' | sin(...)', lines) + self.assertIn(' | sin(object, /)', lines) lines = self.getsection(result, f' | Class methods {where}:', ' | ' + '-'*70) self.assertIn(' | B_classmethod(x)', lines) @@ -1992,7 +1992,7 @@ def test_text_doc_routines_in_module(self): if not support.MISSING_C_DOCSTRINGS: self.assertIn(' sin(x, /)', lines) else: - self.assertIn(' sin(...)', lines) + self.assertIn(' sin(object, /)', lines) def test_html_doc_routines_in_module(self): doc = pydoc.HTMLDoc() @@ -2037,7 +2037,7 @@ def test_html_doc_routines_in_module(self): if not support.MISSING_C_DOCSTRINGS: self.assertIn(' sin(x, /)', lines) else: - self.assertIn(' sin(...)', lines) + self.assertIn(' sin(object, /)', lines) @unittest.skipIf( From 55a44ccf5e121e76122a0fd25a56fce85cd325ab Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Mon, 6 Oct 2025 16:55:58 +0200 Subject: [PATCH 043/373] gh-139400: Move NEWS item from section "Core and Builtins" to section "Security" (GH-139606) (#139664) --- .../2025-09-29-00-01-28.gh-issue-139400.X2T-jO.rst | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Misc/NEWS.d/next/{Core_and_Builtins => Security}/2025-09-29-00-01-28.gh-issue-139400.X2T-jO.rst (100%) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-09-29-00-01-28.gh-issue-139400.X2T-jO.rst b/Misc/NEWS.d/next/Security/2025-09-29-00-01-28.gh-issue-139400.X2T-jO.rst similarity index 100% rename from Misc/NEWS.d/next/Core_and_Builtins/2025-09-29-00-01-28.gh-issue-139400.X2T-jO.rst rename to Misc/NEWS.d/next/Security/2025-09-29-00-01-28.gh-issue-139400.X2T-jO.rst From 7c70cc5c23971ef448ea59827c6e6ae310157356 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 6 Oct 2025 19:48:50 +0300 Subject: [PATCH 044/373] gh-133210: Fix `test_inspect` without docstrings (#139651) --- Lib/test/test_inspect/test_inspect.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 555efb78dcc..e32e34c63b5 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -4261,8 +4261,14 @@ def __init__(self, a): self.assertEqual(self.signature(C, follow_wrapped=False), varargs_signature) - self.assertEqual(self.signature(C.__new__, follow_wrapped=False), - varargs_signature) + if support.MISSING_C_DOCSTRINGS: + self.assertRaisesRegex( + ValueError, "no signature found", + self.signature, C.__new__, follow_wrapped=False, + ) + else: + self.assertEqual(self.signature(C.__new__, follow_wrapped=False), + varargs_signature) def test_signature_on_class_with_wrapped_new(self): with self.subTest('FunctionType'): From 171f787a297ec4b02cfe8b3ebab8374018391f20 Mon Sep 17 00:00:00 2001 From: Furkan Onder Date: Mon, 6 Oct 2025 17:42:26 +0000 Subject: [PATCH 045/373] gh-138854: Skip test_script_shadowing_stdlib_cwd_failure on AIX (GH-138855) --- Lib/test/test_import/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index abbd5f1ed5f..b71a36ec2f7 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -1187,6 +1187,7 @@ class substr(str): @unittest.skipIf(sys.platform == 'win32', 'Cannot delete cwd on Windows') @unittest.skipIf(sys.platform == 'sunos5', 'Cannot delete cwd on Solaris/Illumos') + @unittest.skipIf(sys.platform.startswith('aix'), 'Cannot delete cwd on AIX') def test_script_shadowing_stdlib_cwd_failure(self): with os_helper.temp_dir() as tmp: subtmp = os.path.join(tmp, "subtmp") From 331158065b7426a791217157585e565157bb851c Mon Sep 17 00:00:00 2001 From: Alyssa Coghlan Date: Tue, 7 Oct 2025 05:41:08 +1000 Subject: [PATCH 046/373] Add warnings filter suggestions to PEP 765 entry in What's New (#139658) Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/whatsnew/3.14.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index c226f57e502..15fab9c6b3e 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -937,6 +937,19 @@ The compiler now emits a :exc:`SyntaxWarning` when a :keyword:`return`, leaving a :keyword:`finally` block. This change is specified in :pep:`765`. +In situations where this change is inconvenient (such as those where the +warnings are redundant due to code linting), the :ref:`warning filter +` can be used to turn off all syntax warnings by adding +``ignore::SyntaxWarning`` as a filter. This can be specified in combination +with a filter that converts other warnings to errors (for example, passing +``-Werror -Wignore::SyntaxWarning`` as CLI options, or setting +``PYTHONWARNINGS=error,ignore::SyntaxWarning``). + +Note that applying such a filter at runtime using the :mod:`warnings` module +will only suppress the warning in code that is compiled *after* the filter is +adjusted. Code that is compiled prior to the filter adjustment (for example, +when a module is imported) will still emit the syntax warning. + (Contributed by Irit Katriel in :gh:`130080`.) From 23410f0a9e2f691c8cd342cfd05cc3ca5f09c242 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Mon, 6 Oct 2025 21:17:09 -0500 Subject: [PATCH 047/373] gh-139573: Update macOS installer to use OpenSSL 3.0.18 (#139575) --- Mac/BuildScript/build-installer.py | 6 +++--- .../macOS/2025-10-04-12-29-31.gh-issue-139573.vVpHaP.rst | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/macOS/2025-10-04-12-29-31.gh-issue-139573.vVpHaP.rst diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 4da6d924848..29c552f5aa6 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -246,9 +246,9 @@ def library_recipes(): result.extend([ dict( - name="OpenSSL 3.0.16", - url="https://github.com/openssl/openssl/releases/download/openssl-3.0.16/openssl-3.0.16.tar.gz", - checksum='57e03c50feab5d31b152af2b764f10379aecd8ee92f16c985983ce4a99f7ef86', + name="OpenSSL 3.0.18", + url="https://github.com/openssl/openssl/releases/download/openssl-3.0.18/openssl-3.0.18.tar.gz", + checksum='d80c34f5cf902dccf1f1b5df5ebb86d0392e37049e5d73df1b3abae72e4ffe8b', buildrecipe=build_universal_openssl, configure=None, install=None, diff --git a/Misc/NEWS.d/next/macOS/2025-10-04-12-29-31.gh-issue-139573.vVpHaP.rst b/Misc/NEWS.d/next/macOS/2025-10-04-12-29-31.gh-issue-139573.vVpHaP.rst new file mode 100644 index 00000000000..02a3bfb41ce --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2025-10-04-12-29-31.gh-issue-139573.vVpHaP.rst @@ -0,0 +1 @@ +Updated bundled version of OpenSSL to 3.0.18. From 6d804e4efb8ed2ebeb9cef9577945c46c0038178 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 7 Oct 2025 05:53:05 +0100 Subject: [PATCH 048/373] gh-124111: Update macOS installer to use Tcl/Tk 8.6.17. (#139682) --- Mac/BuildScript/build-installer.py | 6 +++--- .../macOS/2025-10-06-23-56-36.gh-issue-124111.KOlBvs.rst | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/macOS/2025-10-06-23-56-36.gh-issue-124111.KOlBvs.rst diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 29c552f5aa6..4fd8d55f35a 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -264,10 +264,10 @@ def library_recipes(): tk_patches = ['backport_gh71383_fix.patch', 'tk868_on_10_8_10_9.patch', 'backport_gh110950_fix.patch'] else: - tcl_tk_ver='8.6.16' - tcl_checksum='91cb8fa61771c63c262efb553059b7c7ad6757afa5857af6265e4b0bdc2a14a5' + tcl_tk_ver='8.6.17' + tcl_checksum='a3903371efcce8a405c5c245d029e9f6850258a60fa3761c4d58995610949b31' - tk_checksum='be9f94d3575d4b3099d84bc3c10de8994df2d7aa405208173c709cc404a7e5fe' + tk_checksum='e4982df6f969c08bf9dd858a6891059b4a3f50dc6c87c10abadbbe2fc4838946' tk_patches = [] diff --git a/Misc/NEWS.d/next/macOS/2025-10-06-23-56-36.gh-issue-124111.KOlBvs.rst b/Misc/NEWS.d/next/macOS/2025-10-06-23-56-36.gh-issue-124111.KOlBvs.rst new file mode 100644 index 00000000000..2787e5d37fa --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2025-10-06-23-56-36.gh-issue-124111.KOlBvs.rst @@ -0,0 +1 @@ +Update macOS installer to use Tcl/Tk 8.6.17. From 134ff810cfc479c56b770f769cf8d1b680e13009 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Tue, 7 Oct 2025 09:49:59 +0100 Subject: [PATCH 049/373] GH-123299: Announce final release in What's New in Python 3.14 (#139631) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Doc/whatsnew/3.13.rst | 2 +- Doc/whatsnew/3.14.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index bc5a21e172d..1548f128b5b 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -60,7 +60,7 @@ Summary -- Release Highlights .. This section singles out the most important changes in Python 3.13. Brevity is key. -Python 3.13 is the latest stable release of the Python programming +Python 3.13 is a stable release of the Python programming language, with a mix of changes to the language, the implementation and the standard library. The biggest changes include a new `interactive interpreter diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 15fab9c6b3e..b174333760e 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -46,7 +46,7 @@ when researching a change. This article explains the new features in Python 3.14, compared to 3.13. -Python 3.14 will be released on 7 October 2025. +Python 3.14 was released on 7 October 2025. For full details, see the :ref:`changelog `. .. seealso:: @@ -60,7 +60,7 @@ Summary -- Release highlights .. This section singles out the most important changes in Python 3.14. Brevity is key. -Python 3.14 will be the latest stable release of the Python programming +Python 3.14 is the latest stable release of the Python programming language, with a mix of changes to the language, the implementation, and the standard library. The biggest changes include :ref:`template string literals @@ -3083,7 +3083,7 @@ Deprecated C APIs .. _whatsnew314-build-changes: -Build Changes +Build changes ============= * :pep:`776`: Emscripten is now an officially supported platform at From 0e2cdd313ba5c67c5e2e21d993399b890e687c63 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Tue, 7 Oct 2025 13:29:18 +0100 Subject: [PATCH 050/373] gh-139436: Remove ``dist-pdf`` from the docs archives rebuild target (#139437) --- Doc/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/Makefile b/Doc/Makefile index 84578c5c57f..f6f4c721080 100644 --- a/Doc/Makefile +++ b/Doc/Makefile @@ -184,7 +184,7 @@ venv: fi .PHONY: dist-no-html -dist-no-html: dist-text dist-pdf dist-epub dist-texinfo +dist-no-html: dist-text dist-epub dist-texinfo .PHONY: dist dist: From 7094f09f547dc1a520c666dc6ce11b7b69a1b8da Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 7 Oct 2025 14:04:37 +0100 Subject: [PATCH 051/373] GH-139291: Fix C stack limits by factoring out finding hardware stack limits (GH-139294) --- Python/ceval.c | 60 +++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 0ccaacaf3ed..1b52128c858 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -438,31 +438,26 @@ int pthread_attr_destroy(pthread_attr_t *a) #endif - -void -_Py_InitializeRecursionLimits(PyThreadState *tstate) +static void +hardware_stack_limits(uintptr_t *top, uintptr_t *base) { - _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; #ifdef WIN32 ULONG_PTR low, high; GetCurrentThreadStackLimits(&low, &high); - _tstate->c_stack_top = (uintptr_t)high; + *top = (uintptr_t)high; ULONG guarantee = 0; SetThreadStackGuarantee(&guarantee); - _tstate->c_stack_hard_limit = ((uintptr_t)low) + guarantee + _PyOS_STACK_MARGIN_BYTES; - _tstate->c_stack_soft_limit = _tstate->c_stack_hard_limit + _PyOS_STACK_MARGIN_BYTES; + *base = (uintptr_t)low + guarantee; #elif defined(__APPLE__) pthread_t this_thread = pthread_self(); void *stack_addr = pthread_get_stackaddr_np(this_thread); // top of the stack size_t stack_size = pthread_get_stacksize_np(this_thread); - _tstate->c_stack_top = (uintptr_t)stack_addr; - _tstate->c_stack_hard_limit = _tstate->c_stack_top - stack_size; - _tstate->c_stack_soft_limit = _tstate->c_stack_hard_limit + _PyOS_STACK_MARGIN_BYTES; + *top = (uintptr_t)stack_addr; + *base = ((uintptr_t)stack_addr) - stack_size; #else - uintptr_t here_addr = _Py_get_machine_stack_pointer(); -/// XXX musl supports HAVE_PTHRED_GETATTR_NP, but the resulting stack size -/// (on alpine at least) is much smaller than expected and imposes undue limits -/// compared to the old stack size estimation. (We assume musl is not glibc.) + /// XXX musl supports HAVE_PTHRED_GETATTR_NP, but the resulting stack size + /// (on alpine at least) is much smaller than expected and imposes undue limits + /// compared to the old stack size estimation. (We assume musl is not glibc.) # if defined(HAVE_PTHREAD_GETATTR_NP) && !defined(_AIX) && \ !defined(__NetBSD__) && (defined(__GLIBC__) || !defined(__linux__)) size_t stack_size, guard_size; @@ -475,26 +470,35 @@ _Py_InitializeRecursionLimits(PyThreadState *tstate) err |= pthread_attr_destroy(&attr); } if (err == 0) { - uintptr_t base = ((uintptr_t)stack_addr) + guard_size; - _tstate->c_stack_top = base + stack_size; -#ifdef _Py_THREAD_SANITIZER - // Thread sanitizer crashes if we use a bit more than half the stack. - _tstate->c_stack_soft_limit = base + (stack_size / 2); -#else - _tstate->c_stack_soft_limit = base + _PyOS_STACK_MARGIN_BYTES * 2; -#endif - _tstate->c_stack_hard_limit = base + _PyOS_STACK_MARGIN_BYTES; - assert(_tstate->c_stack_soft_limit < here_addr); - assert(here_addr < _tstate->c_stack_top); + *base = ((uintptr_t)stack_addr) + guard_size; + *top = (uintptr_t)stack_addr + stack_size; return; } # endif - _tstate->c_stack_top = _Py_SIZE_ROUND_UP(here_addr, 4096); - _tstate->c_stack_soft_limit = _tstate->c_stack_top - Py_C_STACK_SIZE; - _tstate->c_stack_hard_limit = _tstate->c_stack_top - (Py_C_STACK_SIZE + _PyOS_STACK_MARGIN_BYTES); + uintptr_t here_addr = _Py_get_machine_stack_pointer(); + uintptr_t top_addr = _Py_SIZE_ROUND_UP(here_addr, 4096); + *top = top_addr; + *base = top_addr - Py_C_STACK_SIZE; #endif } +void +_Py_InitializeRecursionLimits(PyThreadState *tstate) +{ + uintptr_t top; + uintptr_t base; + hardware_stack_limits(&top, &base); +#ifdef _Py_THREAD_SANITIZER + // Thread sanitizer crashes if we use more than half the stack. + uintptr_t stacksize = top - base; + base += stacksize/2; +#endif + _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; + _tstate->c_stack_top = top; + _tstate->c_stack_hard_limit = base + _PyOS_STACK_MARGIN_BYTES; + _tstate->c_stack_soft_limit = base + _PyOS_STACK_MARGIN_BYTES * 2; +} + /* The function _Py_EnterRecursiveCallTstate() only calls _Py_CheckRecursiveCall() if the recursion_depth reaches recursion_limit. */ int From 96c59a6e427fab32d0bca89b77febca8cba8aada Mon Sep 17 00:00:00 2001 From: danigm Date: Tue, 7 Oct 2025 16:54:31 +0200 Subject: [PATCH 052/373] gh-138497: Support LLVM_VERSION configuration via env (#138498) Co-authored-by: Savannah Ostrowski --- ...-09-04-12-16-31.gh-issue-138497.Y_5YXh.rst | 4 ++ Tools/jit/README.md | 2 +- Tools/jit/_llvm.py | 49 ++++++++++++------- Tools/jit/_targets.py | 19 +++++-- Tools/jit/build.py | 3 ++ configure | 2 +- configure.ac | 2 +- 7 files changed, 56 insertions(+), 25 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2025-09-04-12-16-31.gh-issue-138497.Y_5YXh.rst diff --git a/Misc/NEWS.d/next/Build/2025-09-04-12-16-31.gh-issue-138497.Y_5YXh.rst b/Misc/NEWS.d/next/Build/2025-09-04-12-16-31.gh-issue-138497.Y_5YXh.rst new file mode 100644 index 00000000000..7eb07709968 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2025-09-04-12-16-31.gh-issue-138497.Y_5YXh.rst @@ -0,0 +1,4 @@ +The LLVM version used by the JIT at build time can now be modified using +the ``LLVM_VERSION`` environment variable. Use this at your own risk, as +there is only one officially supported LLVM version. For more information, +please check ``Tools/jit/README.md``. diff --git a/Tools/jit/README.md b/Tools/jit/README.md index ffc762d3828..35c7ffd7a28 100644 --- a/Tools/jit/README.md +++ b/Tools/jit/README.md @@ -9,7 +9,7 @@ ## Installing LLVM The JIT compiler does not require end users to install any third-party dependencies, but part of it must be *built* using LLVM[^why-llvm]. You are *not* required to build the rest of CPython using LLVM, or even the same version of LLVM (in fact, this is uncommon). -LLVM version 19 is required. Both `clang` and `llvm-readobj` need to be installed and discoverable (version suffixes, like `clang-19`, are okay). It's highly recommended that you also have `llvm-objdump` available, since this allows the build script to dump human-readable assembly for the generated code. +LLVM version 19 is the officially supported version. You can modify if needed using the `LLVM_VERSION` env var during configure. Both `clang` and `llvm-readobj` need to be installed and discoverable (version suffixes, like `clang-19`, are okay). It's highly recommended that you also have `llvm-objdump` available, since this allows the build script to dump human-readable assembly for the generated code. It's easy to install all of the required tools: diff --git a/Tools/jit/_llvm.py b/Tools/jit/_llvm.py index f09a8404871..bc3b50ffe61 100644 --- a/Tools/jit/_llvm.py +++ b/Tools/jit/_llvm.py @@ -10,8 +10,8 @@ import _targets -_LLVM_VERSION = 19 -_LLVM_VERSION_PATTERN = re.compile(rf"version\s+{_LLVM_VERSION}\.\d+\.\d+\S*\s+") + +_LLVM_VERSION = "19" _EXTERNALS_LLVM_TAG = "llvm-19.1.7.0" _P = typing.ParamSpec("_P") @@ -56,53 +56,66 @@ async def _run(tool: str, args: typing.Iterable[str], echo: bool = False) -> str @_async_cache -async def _check_tool_version(name: str, *, echo: bool = False) -> bool: +async def _check_tool_version( + name: str, llvm_version: str, *, echo: bool = False +) -> bool: output = await _run(name, ["--version"], echo=echo) - return bool(output and _LLVM_VERSION_PATTERN.search(output)) + _llvm_version_pattern = re.compile(rf"version\s+{llvm_version}\.\d+\.\d+\S*\s+") + return bool(output and _llvm_version_pattern.search(output)) @_async_cache -async def _get_brew_llvm_prefix(*, echo: bool = False) -> str | None: - output = await _run("brew", ["--prefix", f"llvm@{_LLVM_VERSION}"], echo=echo) +async def _get_brew_llvm_prefix(llvm_version: str, *, echo: bool = False) -> str | None: + output = await _run("brew", ["--prefix", f"llvm@{llvm_version}"], echo=echo) return output and output.removesuffix("\n") @_async_cache -async def _find_tool(tool: str, *, echo: bool = False) -> str | None: +async def _find_tool(tool: str, llvm_version: str, *, echo: bool = False) -> str | None: # Unversioned executables: path = tool - if await _check_tool_version(path, echo=echo): + if await _check_tool_version(path, llvm_version, echo=echo): return path # Versioned executables: - path = f"{tool}-{_LLVM_VERSION}" - if await _check_tool_version(path, echo=echo): + path = f"{tool}-{llvm_version}" + if await _check_tool_version(path, llvm_version, echo=echo): return path # PCbuild externals: externals = os.environ.get("EXTERNALS_DIR", _targets.EXTERNALS) path = os.path.join(externals, _EXTERNALS_LLVM_TAG, "bin", tool) - if await _check_tool_version(path, echo=echo): + if await _check_tool_version(path, llvm_version, echo=echo): return path # Homebrew-installed executables: - prefix = await _get_brew_llvm_prefix(echo=echo) + prefix = await _get_brew_llvm_prefix(llvm_version, echo=echo) if prefix is not None: path = os.path.join(prefix, "bin", tool) - if await _check_tool_version(path, echo=echo): + if await _check_tool_version(path, llvm_version, echo=echo): return path # Nothing found: return None async def maybe_run( - tool: str, args: typing.Iterable[str], echo: bool = False + tool: str, + args: typing.Iterable[str], + echo: bool = False, + llvm_version: str = _LLVM_VERSION, ) -> str | None: """Run an LLVM tool if it can be found. Otherwise, return None.""" - path = await _find_tool(tool, echo=echo) + + path = await _find_tool(tool, llvm_version, echo=echo) return path and await _run(path, args, echo=echo) -async def run(tool: str, args: typing.Iterable[str], echo: bool = False) -> str: +async def run( + tool: str, + args: typing.Iterable[str], + echo: bool = False, + llvm_version: str = _LLVM_VERSION, +) -> str: """Run an LLVM tool if it can be found. Otherwise, raise RuntimeError.""" - output = await maybe_run(tool, args, echo=echo) + + output = await maybe_run(tool, args, echo=echo, llvm_version=llvm_version) if output is None: - raise RuntimeError(f"Can't find {tool}-{_LLVM_VERSION}!") + raise RuntimeError(f"Can't find {tool}-{llvm_version}!") return output diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index 2f3969e7d05..9fc3522d23d 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -50,6 +50,7 @@ class _Target(typing.Generic[_S, _R]): debug: bool = False verbose: bool = False cflags: str = "" + llvm_version: str = _llvm._LLVM_VERSION known_symbols: dict[str, int] = dataclasses.field(default_factory=dict) pyconfig_dir: pathlib.Path = pathlib.Path.cwd().resolve() @@ -81,7 +82,9 @@ def _compute_digest(self) -> str: async def _parse(self, path: pathlib.Path) -> _stencils.StencilGroup: group = _stencils.StencilGroup() args = ["--disassemble", "--reloc", f"{path}"] - output = await _llvm.maybe_run("llvm-objdump", args, echo=self.verbose) + output = await _llvm.maybe_run( + "llvm-objdump", args, echo=self.verbose, llvm_version=self.llvm_version + ) if output is not None: # Make sure that full paths don't leak out (for reproducibility): long, short = str(path), str(path.name) @@ -99,7 +102,9 @@ async def _parse(self, path: pathlib.Path) -> _stencils.StencilGroup: "--sections", f"{path}", ] - output = await _llvm.run("llvm-readobj", args, echo=self.verbose) + output = await _llvm.run( + "llvm-readobj", args, echo=self.verbose, llvm_version=self.llvm_version + ) # --elf-output-style=JSON is only *slightly* broken on Mach-O... output = output.replace("PrivateExtern\n", "\n") output = output.replace("Extern\n", "\n") @@ -175,12 +180,16 @@ async def _compile( # Allow user-provided CFLAGS to override any defaults *shlex.split(self.cflags), ] - await _llvm.run("clang", args_s, echo=self.verbose) + await _llvm.run( + "clang", args_s, echo=self.verbose, llvm_version=self.llvm_version + ) self.optimizer( s, label_prefix=self.label_prefix, symbol_prefix=self.symbol_prefix ).run() args_o = [f"--target={self.triple}", "-c", "-o", f"{o}", f"{s}"] - await _llvm.run("clang", args_o, echo=self.verbose) + await _llvm.run( + "clang", args_o, echo=self.verbose, llvm_version=self.llvm_version + ) return await self._parse(o) async def _build_stencils(self) -> dict[str, _stencils.StencilGroup]: @@ -224,6 +233,8 @@ def build( if not self.stable: warning = f"JIT support for {self.triple} is still experimental!" request = "Please report any issues you encounter.".center(len(warning)) + if self.llvm_version != _llvm._LLVM_VERSION: + request = f"Warning! Building with an LLVM version other than {_llvm._LLVM_VERSION} is not supported." outline = "=" * len(warning) print("\n".join(["", outline, warning, request, outline, ""])) digest = f"// {self._compute_digest()}\n" diff --git a/Tools/jit/build.py b/Tools/jit/build.py index a0733005929..127d93b317f 100644 --- a/Tools/jit/build.py +++ b/Tools/jit/build.py @@ -42,6 +42,7 @@ parser.add_argument( "--cflags", help="additional flags to pass to the compiler", default="" ) + parser.add_argument("--llvm-version", help="LLVM version to use") args = parser.parse_args() for target in args.target: target.debug = args.debug @@ -49,6 +50,8 @@ target.verbose = args.verbose target.cflags = args.cflags target.pyconfig_dir = args.pyconfig_dir + if args.llvm_version: + target.llvm_version = args.llvm_version target.build( comment=comment, force=args.force, diff --git a/configure b/configure index 0d1f6a29e9b..d80340e3015 100755 --- a/configure +++ b/configure @@ -10875,7 +10875,7 @@ then : else case e in #( e) as_fn_append CFLAGS_NODIST " $jit_flags" - REGEN_JIT_COMMAND="\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py ${ARCH_TRIPLES:-$host} --output-dir . --pyconfig-dir . --cflags=\"$CFLAGS_JIT\"" + REGEN_JIT_COMMAND="\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py ${ARCH_TRIPLES:-$host} --output-dir . --pyconfig-dir . --cflags=\"$CFLAGS_JIT\" --llvm-version=\"$LLVM_VERSION\"" if test "x$Py_DEBUG" = xtrue then : as_fn_append REGEN_JIT_COMMAND " --debug" diff --git a/configure.ac b/configure.ac index 7b5da6e0d15..1e0c0f71b7c 100644 --- a/configure.ac +++ b/configure.ac @@ -2786,7 +2786,7 @@ AS_VAR_IF([jit_flags], [], [AS_VAR_APPEND([CFLAGS_NODIST], [" $jit_flags"]) AS_VAR_SET([REGEN_JIT_COMMAND], - ["\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py ${ARCH_TRIPLES:-$host} --output-dir . --pyconfig-dir . --cflags=\"$CFLAGS_JIT\""]) + ["\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py ${ARCH_TRIPLES:-$host} --output-dir . --pyconfig-dir . --cflags=\"$CFLAGS_JIT\" --llvm-version=\"$LLVM_VERSION\""]) AS_VAR_IF([Py_DEBUG], [true], [AS_VAR_APPEND([REGEN_JIT_COMMAND], [" --debug"])], From 539461d9ec8e5322ead638f7be733fd196aa6c79 Mon Sep 17 00:00:00 2001 From: Tomasz Pytel Date: Tue, 7 Oct 2025 12:28:15 -0400 Subject: [PATCH 053/373] gh-139516: Fix lambda colon start format spec in f-string in tokenizer (#139657) --- Lib/test/test_fstring.py | 7 +++++++ Lib/test/test_tokenize.py | 17 +++++++++++++++++ ...25-10-06-13-15-26.gh-issue-139516.d9Pkur.rst | 1 + Parser/lexer/lexer.c | 2 +- Parser/lexer/state.h | 2 ++ 5 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-13-15-26.gh-issue-139516.d9Pkur.rst diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 41cefe0e286..05d0cbd2445 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -1859,6 +1859,13 @@ def __format__(self, format): # Test multiple format specs in same raw f-string self.assertEqual(rf"{UnchangedFormat():\xFF} {UnchangedFormat():\n}", '\\xFF \\n') + def test_gh139516(self): + with temp_cwd(): + script = 'script.py' + with open(script, 'wb') as f: + f.write('''def f(a): pass\nf"{f(a=lambda: 'à'\n)}"'''.encode()) + assert_python_ok(script) + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index d90a7659c42..8fdd03f347b 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -1216,6 +1216,23 @@ def test_multiline_non_ascii_fstring_with_expr(self): FSTRING_END "\'\'\'" (3, 1) (3, 4) """) + # gh-139516, the '\n' is explicit to ensure no trailing whitespace which would invalidate the test + self.check_tokenize('''f"{f(a=lambda: 'à'\n)}"''', """\ + FSTRING_START \'f"\' (1, 0) (1, 2) + OP '{' (1, 2) (1, 3) + NAME 'f' (1, 3) (1, 4) + OP '(' (1, 4) (1, 5) + NAME 'a' (1, 5) (1, 6) + OP '=' (1, 6) (1, 7) + NAME 'lambda' (1, 7) (1, 13) + OP ':' (1, 13) (1, 14) + STRING "\'à\'" (1, 15) (1, 18) + NL '\\n' (1, 18) (1, 19) + OP ')' (2, 0) (2, 1) + OP '}' (2, 1) (2, 2) + FSTRING_END \'"\' (2, 2) (2, 3) + """) + class GenerateTokensTest(TokenizeTest): def check_tokenize(self, s, expected): # Format the tokens in s in a table format. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-13-15-26.gh-issue-139516.d9Pkur.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-13-15-26.gh-issue-139516.d9Pkur.rst new file mode 100644 index 00000000000..a7091123060 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-13-15-26.gh-issue-139516.d9Pkur.rst @@ -0,0 +1 @@ +Fix lambda colon erroneously start format spec in f-string in tokenizer. diff --git a/Parser/lexer/lexer.c b/Parser/lexer/lexer.c index 81363cf8e81..a69994e9b3d 100644 --- a/Parser/lexer/lexer.c +++ b/Parser/lexer/lexer.c @@ -1376,7 +1376,7 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid non-printable character U+%04X", c)); } - if( c == '=' && INSIDE_FSTRING_EXPR(current_tok)) { + if( c == '=' && INSIDE_FSTRING_EXPR_AT_TOP(current_tok)) { current_tok->in_debug = 1; } diff --git a/Parser/lexer/state.h b/Parser/lexer/state.h index 5e8cac7249b..877127125a7 100644 --- a/Parser/lexer/state.h +++ b/Parser/lexer/state.h @@ -9,6 +9,8 @@ #define INSIDE_FSTRING(tok) (tok->tok_mode_stack_index > 0) #define INSIDE_FSTRING_EXPR(tok) (tok->curly_bracket_expr_start_depth >= 0) +#define INSIDE_FSTRING_EXPR_AT_TOP(tok) \ + (tok->curly_bracket_depth - tok->curly_bracket_expr_start_depth == 1) enum decoding_state { STATE_INIT, From 162997bb70e067668c039700141770687bc8f267 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 7 Oct 2025 20:15:26 +0300 Subject: [PATCH 054/373] gh-139700: Check consistency of the zip64 end of central directory record (GH-139702) Support records with "zip64 extensible data" if there are no bytes prepended to the ZIP file. --- Lib/test/test_zipfile/test_core.py | 82 ++++++++++++++++++- Lib/zipfile/__init__.py | 51 +++++++----- ...-10-07-19-31-34.gh-issue-139700.vNHU1O.rst | 3 + 3 files changed, 113 insertions(+), 23 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2025-10-07-19-31-34.gh-issue-139700.vNHU1O.rst diff --git a/Lib/test/test_zipfile/test_core.py b/Lib/test/test_zipfile/test_core.py index c033059a515..6acfefc74d6 100644 --- a/Lib/test/test_zipfile/test_core.py +++ b/Lib/test/test_zipfile/test_core.py @@ -898,6 +898,8 @@ def make_zip64_file( self, file_size_64_set=False, file_size_extra=False, compress_size_64_set=False, compress_size_extra=False, header_offset_64_set=False, header_offset_extra=False, + extensible_data=b'', + end_of_central_dir_size=None, offset_to_end_of_central_dir=None, ): """Generate bytes sequence for a zip with (incomplete) zip64 data. @@ -951,6 +953,12 @@ def make_zip64_file( central_dir_size = struct.pack(' 2: inferred = concat + offset_cd @@ -289,16 +286,15 @@ def _EndRecData64(fpin, offset, endrec): """ Read the ZIP64 end-of-archive records and use that to update endrec """ - try: - fpin.seek(offset - sizeEndCentDir64Locator, 2) - except OSError: - # If the seek fails, the file is not large enough to contain a ZIP64 + offset -= sizeEndCentDir64Locator + if offset < 0: + # The file is not large enough to contain a ZIP64 # end-of-archive record, so just return the end record we were given. return endrec - + fpin.seek(offset) data = fpin.read(sizeEndCentDir64Locator) if len(data) != sizeEndCentDir64Locator: - return endrec + raise OSError("Unknown I/O error") sig, diskno, reloff, disks = struct.unpack(structEndArchive64Locator, data) if sig != stringEndArchive64Locator: return endrec @@ -306,16 +302,33 @@ def _EndRecData64(fpin, offset, endrec): if diskno != 0 or disks > 1: raise BadZipFile("zipfiles that span multiple disks are not supported") - # Assume no 'zip64 extensible data' - fpin.seek(offset - sizeEndCentDir64Locator - sizeEndCentDir64, 2) + offset -= sizeEndCentDir64 + if reloff > offset: + raise BadZipFile("Corrupt zip64 end of central directory locator") + # First, check the assumption that there is no prepended data. + fpin.seek(reloff) + extrasz = offset - reloff data = fpin.read(sizeEndCentDir64) if len(data) != sizeEndCentDir64: - return endrec + raise OSError("Unknown I/O error") + if not data.startswith(stringEndArchive64) and reloff != offset: + # Since we already have seen the Zip64 EOCD Locator, it's + # possible we got here because there is prepended data. + # Assume no 'zip64 extensible data' + fpin.seek(offset) + extrasz = 0 + data = fpin.read(sizeEndCentDir64) + if len(data) != sizeEndCentDir64: + raise OSError("Unknown I/O error") + if not data.startswith(stringEndArchive64): + raise BadZipFile("Zip64 end of central directory record not found") + sig, sz, create_version, read_version, disk_num, disk_dir, \ dircount, dircount2, dirsize, diroffset = \ struct.unpack(structEndArchive64, data) - if sig != stringEndArchive64: - return endrec + if (diroffset + dirsize != reloff or + sz + 12 != sizeEndCentDir64 + extrasz): + raise BadZipFile("Corrupt zip64 end of central directory record") # Update the original endrec using data from the ZIP64 record endrec[_ECD_SIGNATURE] = sig @@ -325,6 +338,7 @@ def _EndRecData64(fpin, offset, endrec): endrec[_ECD_ENTRIES_TOTAL] = dircount2 endrec[_ECD_SIZE] = dirsize endrec[_ECD_OFFSET] = diroffset + endrec[_ECD_LOCATION] = offset - extrasz return endrec @@ -358,7 +372,7 @@ def _EndRecData(fpin): endrec.append(filesize - sizeEndCentDir) # Try to read the "Zip64 end of central directory" structure - return _EndRecData64(fpin, -sizeEndCentDir, endrec) + return _EndRecData64(fpin, filesize - sizeEndCentDir, endrec) # Either this is not a ZIP file, or it is a ZIP file with an archive # comment. Search the end of the file for the "end of central directory" @@ -382,8 +396,7 @@ def _EndRecData(fpin): endrec.append(maxCommentStart + start) # Try to read the "Zip64 end of central directory" structure - return _EndRecData64(fpin, maxCommentStart + start - filesize, - endrec) + return _EndRecData64(fpin, maxCommentStart + start, endrec) # Unable to find a valid end of central directory structure return None @@ -2142,7 +2155,7 @@ def _write_end_record(self): " would require ZIP64 extensions") zip64endrec = struct.pack( structEndArchive64, stringEndArchive64, - 44, 45, 45, 0, 0, centDirCount, centDirCount, + sizeEndCentDir64 - 12, 45, 45, 0, 0, centDirCount, centDirCount, centDirSize, centDirOffset) self.fp.write(zip64endrec) diff --git a/Misc/NEWS.d/next/Security/2025-10-07-19-31-34.gh-issue-139700.vNHU1O.rst b/Misc/NEWS.d/next/Security/2025-10-07-19-31-34.gh-issue-139700.vNHU1O.rst new file mode 100644 index 00000000000..a8e7a1f1878 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2025-10-07-19-31-34.gh-issue-139700.vNHU1O.rst @@ -0,0 +1,3 @@ +Check consistency of the zip64 end of central directory record. Support +records with "zip64 extensible data" if there are no bytes prepended to the +ZIP file. From d396a32b3d1a4b3a35bdfc8b44521d3021a108c7 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Tue, 7 Oct 2025 18:39:45 +0100 Subject: [PATCH 055/373] gh-139698: Fix typo in What's New 3.14 (#139699) --- Doc/whatsnew/3.14.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index b174333760e..e1a25c84813 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -449,8 +449,8 @@ depending on platform and architecture. The baseline is Python 3.14 built with Clang 19, without this new interpreter. This interpreter currently only works with Clang 19 and newer -on x86-64 and AArch64 architectures. However, a future release -of GCC is expected will support this as well. +on x86-64 and AArch64 architectures. +However, a future release of GCC is expected to support this as well. This feature is opt-in for now. Enabling profile-guided optimization is highly recommendeded when using the new interpreter as it is the only configuration From 25edfa7cf1c0ddeaae2dd76ca6c18807b339257a Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Tue, 7 Oct 2025 20:53:27 +0200 Subject: [PATCH 056/373] Doc: Improve clarity for subinterpreters in What's New in 3.14 (#139221) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/whatsnew/3.14.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index e1a25c84813..677365c2f59 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -217,7 +217,7 @@ has significant benefits: * they support a new (to Python), human-friendly concurrency model * true multi-core parallelism -For some use cases, concurrency in software enables efficiency and +For some use cases, concurrency in software improves efficiency and can simplify design, at a high level. At the same time, implementing and maintaining all but the simplest concurrency is often a struggle for the human brain. @@ -225,9 +225,10 @@ That especially applies to plain threads (for example, :mod:`threading`), where all memory is shared between all threads. With multiple isolated interpreters, you can take advantage of a class -of concurrency models, like CSP or the actor model, that have found +of concurrency models, like Communicating Sequential Processes (CSP) +or the actor model, that have found success in other programming languages, like Smalltalk, Erlang, -Haskell, and Go. Think of multiple interpreters like threads +Haskell, and Go. Think of multiple interpreters as threads but with opt-in sharing. Regarding multi-core parallelism: as of Python 3.12, interpreters @@ -249,8 +250,8 @@ having the isolation of processes with the efficiency of threads. While the feature has been around for decades, multiple interpreters have not been used widely, due to low awareness and the lack of a standard library module. Consequently, they currently have several -notable limitations, which will improve significantly now that the -feature is finally going mainstream. +notable limitations, which are expected to improve significantly now +that the feature is going mainstream. Current limitations: From f962e1eacf2141e6b14c96b58da62382a93b070b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 7 Oct 2025 23:49:08 +0200 Subject: [PATCH 057/373] gh-79315: Add Include/cpython/marshal.h header (#139725) --- Include/cpython/marshal.h | 17 +++++++++++++++++ Include/marshal.h | 23 +++++------------------ Makefile.pre.in | 1 + PCbuild/pythoncore.vcxproj | 1 + 4 files changed, 24 insertions(+), 18 deletions(-) create mode 100644 Include/cpython/marshal.h diff --git a/Include/cpython/marshal.h b/Include/cpython/marshal.h new file mode 100644 index 00000000000..6c1f7f96b6a --- /dev/null +++ b/Include/cpython/marshal.h @@ -0,0 +1,17 @@ +#ifndef _Py_CPYTHON_MARSHAL_H +# error "this header file must not be included directly" +#endif + +PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromString(const char *, + Py_ssize_t); +PyAPI_FUNC(PyObject *) PyMarshal_WriteObjectToString(PyObject *, int); + +#define Py_MARSHAL_VERSION 5 + +PyAPI_FUNC(long) PyMarshal_ReadLongFromFile(FILE *); +PyAPI_FUNC(int) PyMarshal_ReadShortFromFile(FILE *); +PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromFile(FILE *); +PyAPI_FUNC(PyObject *) PyMarshal_ReadLastObjectFromFile(FILE *); + +PyAPI_FUNC(void) PyMarshal_WriteLongToFile(long, FILE *, int); +PyAPI_FUNC(void) PyMarshal_WriteObjectToFile(PyObject *, FILE *, int); diff --git a/Include/marshal.h b/Include/marshal.h index f773587bdd0..2ccb112b40c 100644 --- a/Include/marshal.h +++ b/Include/marshal.h @@ -1,31 +1,18 @@ - /* Interface for marshal.c */ #ifndef Py_MARSHAL_H #define Py_MARSHAL_H -#ifndef Py_LIMITED_API - #ifdef __cplusplus extern "C" { #endif -PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromString(const char *, - Py_ssize_t); -PyAPI_FUNC(PyObject *) PyMarshal_WriteObjectToString(PyObject *, int); - -#define Py_MARSHAL_VERSION 5 - -PyAPI_FUNC(long) PyMarshal_ReadLongFromFile(FILE *); -PyAPI_FUNC(int) PyMarshal_ReadShortFromFile(FILE *); -PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromFile(FILE *); -PyAPI_FUNC(PyObject *) PyMarshal_ReadLastObjectFromFile(FILE *); - -PyAPI_FUNC(void) PyMarshal_WriteLongToFile(long, FILE *, int); -PyAPI_FUNC(void) PyMarshal_WriteObjectToFile(PyObject *, FILE *, int); +#ifndef Py_LIMITED_API +# define _Py_CPYTHON_MARSHAL_H +# include "cpython/marshal.h" +# undef _Py_CPYTHON_MARSHAL_H +#endif #ifdef __cplusplus } #endif - -#endif /* Py_LIMITED_API */ #endif /* !Py_MARSHAL_H */ diff --git a/Makefile.pre.in b/Makefile.pre.in index 244e25c348f..ff9a84e4c27 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1271,6 +1271,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/cpython/pylock.h \ $(srcdir)/Include/cpython/longintrepr.h \ $(srcdir)/Include/cpython/longobject.h \ + $(srcdir)/Include/cpython/marshal.h \ $(srcdir)/Include/cpython/memoryobject.h \ $(srcdir)/Include/cpython/methodobject.h \ $(srcdir)/Include/cpython/modsupport.h \ diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index d7544d3a9fb..d043d23eed8 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -168,6 +168,7 @@ + From 1cf22600f10322d77ab2c2bb41acabf2360f84e9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 8 Oct 2025 00:48:18 +0200 Subject: [PATCH 058/373] gh-79315: Add Include/cpython/sliceobject.h header (#139729) --- Include/cpython/sliceobject.h | 20 ++++++++++++++++++++ Include/sliceobject.h | 25 ++++++------------------- Makefile.pre.in | 1 + PCbuild/pythoncore.vcxproj | 1 + PCbuild/pythoncore.vcxproj.filters | 3 +++ 5 files changed, 31 insertions(+), 19 deletions(-) create mode 100644 Include/cpython/sliceobject.h diff --git a/Include/cpython/sliceobject.h b/Include/cpython/sliceobject.h new file mode 100644 index 00000000000..4c3ea1faceb --- /dev/null +++ b/Include/cpython/sliceobject.h @@ -0,0 +1,20 @@ +#ifndef Py_CPYTHON_SLICEOBJECT_H +# error "this header file must not be included directly" +#endif + +/* Slice object interface */ + +/* +A slice object containing start, stop, and step data members (the +names are from range). After much talk with Guido, it was decided to +let these be any arbitrary python type. Py_None stands for omitted values. +*/ +typedef struct { + PyObject_HEAD + PyObject *start, *stop, *step; /* not NULL */ +} PySliceObject; + +PyAPI_FUNC(PyObject *) _PySlice_FromIndices(Py_ssize_t start, Py_ssize_t stop); +PyAPI_FUNC(int) _PySlice_GetLongIndices(PySliceObject *self, PyObject *length, + PyObject **start_ptr, PyObject **stop_ptr, + PyObject **step_ptr); diff --git a/Include/sliceobject.h b/Include/sliceobject.h index 35e2ea254ca..00c70a6e911 100644 --- a/Include/sliceobject.h +++ b/Include/sliceobject.h @@ -16,19 +16,6 @@ PyAPI_DATA(PyObject) _Py_EllipsisObject; /* Don't use this directly */ /* Slice object interface */ -/* - -A slice object containing start, stop, and step data members (the -names are from range). After much talk with Guido, it was decided to -let these be any arbitrary python type. Py_None stands for omitted values. -*/ -#ifndef Py_LIMITED_API -typedef struct { - PyObject_HEAD - PyObject *start, *stop, *step; /* not NULL */ -} PySliceObject; -#endif - PyAPI_DATA(PyTypeObject) PySlice_Type; PyAPI_DATA(PyTypeObject) PyEllipsis_Type; @@ -36,12 +23,6 @@ PyAPI_DATA(PyTypeObject) PyEllipsis_Type; PyAPI_FUNC(PyObject *) PySlice_New(PyObject* start, PyObject* stop, PyObject* step); -#ifndef Py_LIMITED_API -PyAPI_FUNC(PyObject *) _PySlice_FromIndices(Py_ssize_t start, Py_ssize_t stop); -PyAPI_FUNC(int) _PySlice_GetLongIndices(PySliceObject *self, PyObject *length, - PyObject **start_ptr, PyObject **stop_ptr, - PyObject **step_ptr); -#endif PyAPI_FUNC(int) PySlice_GetIndices(PyObject *r, Py_ssize_t length, Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step); Py_DEPRECATED(3.7) @@ -63,6 +44,12 @@ PyAPI_FUNC(Py_ssize_t) PySlice_AdjustIndices(Py_ssize_t length, Py_ssize_t step); #endif +#ifndef Py_LIMITED_API +# define Py_CPYTHON_SLICEOBJECT_H +# include "cpython/sliceobject.h" +# undef Py_CPYTHON_SLICEOBJECT_H +#endif + #ifdef __cplusplus } #endif diff --git a/Makefile.pre.in b/Makefile.pre.in index ff9a84e4c27..a356ac0ea94 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1297,6 +1297,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/cpython/pythonrun.h \ $(srcdir)/Include/cpython/pythread.h \ $(srcdir)/Include/cpython/setobject.h \ + $(srcdir)/Include/cpython/sliceobject.h \ $(srcdir)/Include/cpython/traceback.h \ $(srcdir)/Include/cpython/tracemalloc.h \ $(srcdir)/Include/cpython/tupleobject.h \ diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index d043d23eed8..25a7313eb26 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -191,6 +191,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 1868b222f18..edbd88d930a 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -492,6 +492,9 @@ Include\cpython + + Include\cpython + Include\cpython From 5a77f02d72e0735877fe4a5d615559d1541bc232 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 8 Oct 2025 00:49:24 +0200 Subject: [PATCH 059/373] gh-79315: Remove Include/pylock.h and Include/monitoring.h (#139731) Keep Include/cpython/pylock.h and Include/cpython/monitoring.h. --- Include/Python.h | 4 ++-- Include/cpython/monitoring.h | 16 ++++++++++++++-- Include/cpython/pylock.h | 15 +++++++++++++-- Include/monitoring.h | 18 ------------------ Include/pylock.h | 16 ---------------- Makefile.pre.in | 2 -- Modules/_testcapi/monitoring.c | 2 -- PCbuild/pythoncore.vcxproj | 2 +- PCbuild/pythoncore.vcxproj.filters | 3 --- 9 files changed, 30 insertions(+), 48 deletions(-) delete mode 100644 Include/monitoring.h delete mode 100644 Include/pylock.h diff --git a/Include/Python.h b/Include/Python.h index 261b4d316bd..78083bbf31d 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -78,7 +78,7 @@ __pragma(warning(disable: 4201)) #include "pybuffer.h" #include "pystats.h" #include "pyatomic.h" -#include "pylock.h" +#include "cpython/pylock.h" #include "critical_section.h" #include "object.h" #include "refcount.h" @@ -105,7 +105,7 @@ __pragma(warning(disable: 4201)) #include "setobject.h" #include "methodobject.h" #include "moduleobject.h" -#include "monitoring.h" +#include "cpython/monitoring.h" #include "cpython/funcobject.h" #include "cpython/classobject.h" #include "fileobject.h" diff --git a/Include/cpython/monitoring.h b/Include/cpython/monitoring.h index ce92942404c..5094c8c23ae 100644 --- a/Include/cpython/monitoring.h +++ b/Include/cpython/monitoring.h @@ -1,7 +1,13 @@ -#ifndef Py_CPYTHON_MONITORING_H -# error "this header file must not be included directly" +#ifndef Py_MONITORING_H +#define Py_MONITORING_H +#ifndef Py_LIMITED_API +#ifdef __cplusplus +extern "C" { #endif +// There is currently no limited API for monitoring + + /* Local events. * These require bytecode instrumentation */ @@ -267,3 +273,9 @@ PyMonitoring_FireStopIterationEvent(PyMonitoringState *state, PyObject *codelike } #undef _PYMONITORING_IF_ACTIVE + +#ifdef __cplusplus +} +#endif +#endif // !Py_LIMITED_API +#endif // !Py_MONITORING_H diff --git a/Include/cpython/pylock.h b/Include/cpython/pylock.h index 63886fca28e..460ac2c9f80 100644 --- a/Include/cpython/pylock.h +++ b/Include/cpython/pylock.h @@ -1,7 +1,11 @@ -#ifndef Py_CPYTHON_LOCK_H -# error "this header file must not be included directly" +#ifndef Py_LOCK_H +#define Py_LOCK_H +#ifndef Py_LIMITED_API +#ifdef __cplusplus +extern "C" { #endif + #define _Py_UNLOCKED 0 #define _Py_LOCKED 1 @@ -72,3 +76,10 @@ _PyMutex_IsLocked(PyMutex *m) return (_Py_atomic_load_uint8(&m->_bits) & _Py_LOCKED) != 0; } #define PyMutex_IsLocked _PyMutex_IsLocked + + +#ifdef __cplusplus +} +#endif +#endif // !Py_LIMITED_API +#endif // !Py_LOCK_H diff --git a/Include/monitoring.h b/Include/monitoring.h deleted file mode 100644 index 985f7f230e4..00000000000 --- a/Include/monitoring.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef Py_MONITORING_H -#define Py_MONITORING_H -#ifdef __cplusplus -extern "C" { -#endif - -// There is currently no limited API for monitoring - -#ifndef Py_LIMITED_API -# define Py_CPYTHON_MONITORING_H -# include "cpython/monitoring.h" -# undef Py_CPYTHON_MONITORING_H -#endif - -#ifdef __cplusplus -} -#endif -#endif /* !Py_MONITORING_H */ diff --git a/Include/pylock.h b/Include/pylock.h deleted file mode 100644 index 1939ef269d3..00000000000 --- a/Include/pylock.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef Py_LOCK_H -#define Py_LOCK_H -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef Py_LIMITED_API -# define Py_CPYTHON_LOCK_H -# include "cpython/pylock.h" -# undef Py_CPYTHON_LOCK_H -#endif - -#ifdef __cplusplus -} -#endif -#endif /* !Py_LOCK_H */ diff --git a/Makefile.pre.in b/Makefile.pre.in index a356ac0ea94..061305a9ed1 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1191,14 +1191,12 @@ PYTHON_HEADERS= \ $(srcdir)/Include/intrcheck.h \ $(srcdir)/Include/iterobject.h \ $(srcdir)/Include/listobject.h \ - $(srcdir)/Include/pylock.h \ $(srcdir)/Include/longobject.h \ $(srcdir)/Include/marshal.h \ $(srcdir)/Include/memoryobject.h \ $(srcdir)/Include/methodobject.h \ $(srcdir)/Include/modsupport.h \ $(srcdir)/Include/moduleobject.h \ - $(srcdir)/Include/monitoring.h \ $(srcdir)/Include/object.h \ $(srcdir)/Include/objimpl.h \ $(srcdir)/Include/opcode.h \ diff --git a/Modules/_testcapi/monitoring.c b/Modules/_testcapi/monitoring.c index e041943492d..3f99836c1eb 100644 --- a/Modules/_testcapi/monitoring.c +++ b/Modules/_testcapi/monitoring.c @@ -1,8 +1,6 @@ #include "parts.h" #include "util.h" -#include "monitoring.h" - #define Py_BUILD_CORE #include "internal/pycore_instruments.h" diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 25a7313eb26..248b63f2577 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -172,6 +172,7 @@ + @@ -334,7 +335,6 @@ - diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index edbd88d930a..da07a139b7c 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -120,9 +120,6 @@ Include - - Include - Include From 85ec35d2ab8b764aefd6810efd59ff54c92554e9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 8 Oct 2025 01:19:50 +0200 Subject: [PATCH 060/373] gh-79315: Add Include/cpython/structseq.h header (#139730) --- Include/cpython/structseq.h | 12 ++++++++++++ Include/structseq.h | 12 +++--------- Makefile.pre.in | 1 + PCbuild/pythoncore.vcxproj | 1 + PCbuild/pythoncore.vcxproj.filters | 3 +++ 5 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 Include/cpython/structseq.h diff --git a/Include/cpython/structseq.h b/Include/cpython/structseq.h new file mode 100644 index 00000000000..328fbe86143 --- /dev/null +++ b/Include/cpython/structseq.h @@ -0,0 +1,12 @@ +#ifndef Py_CPYTHON_STRUCTSEQ_H +# error "this header file must not be included directly" +#endif + +PyAPI_FUNC(void) PyStructSequence_InitType(PyTypeObject *type, + PyStructSequence_Desc *desc); +PyAPI_FUNC(int) PyStructSequence_InitType2(PyTypeObject *type, + PyStructSequence_Desc *desc); + +typedef PyTupleObject PyStructSequence; +#define PyStructSequence_SET_ITEM PyStructSequence_SetItem +#define PyStructSequence_GET_ITEM PyStructSequence_GetItem diff --git a/Include/structseq.h b/Include/structseq.h index 29e24fee54e..e52d6188030 100644 --- a/Include/structseq.h +++ b/Include/structseq.h @@ -21,12 +21,6 @@ typedef struct PyStructSequence_Desc { PyAPI_DATA(const char * const) PyStructSequence_UnnamedField; -#ifndef Py_LIMITED_API -PyAPI_FUNC(void) PyStructSequence_InitType(PyTypeObject *type, - PyStructSequence_Desc *desc); -PyAPI_FUNC(int) PyStructSequence_InitType2(PyTypeObject *type, - PyStructSequence_Desc *desc); -#endif PyAPI_FUNC(PyTypeObject*) PyStructSequence_NewType(PyStructSequence_Desc *desc); PyAPI_FUNC(PyObject *) PyStructSequence_New(PyTypeObject* type); @@ -35,9 +29,9 @@ PyAPI_FUNC(void) PyStructSequence_SetItem(PyObject*, Py_ssize_t, PyObject*); PyAPI_FUNC(PyObject*) PyStructSequence_GetItem(PyObject*, Py_ssize_t); #ifndef Py_LIMITED_API -typedef PyTupleObject PyStructSequence; -#define PyStructSequence_SET_ITEM PyStructSequence_SetItem -#define PyStructSequence_GET_ITEM PyStructSequence_GetItem +# define Py_CPYTHON_STRUCTSEQ_H +# include "cpython/structseq.h" +# undef Py_CPYTHON_STRUCTSEQ_H #endif #ifdef __cplusplus diff --git a/Makefile.pre.in b/Makefile.pre.in index 061305a9ed1..987d55a9bdb 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1296,6 +1296,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/cpython/pythread.h \ $(srcdir)/Include/cpython/setobject.h \ $(srcdir)/Include/cpython/sliceobject.h \ + $(srcdir)/Include/cpython/structseq.h \ $(srcdir)/Include/cpython/traceback.h \ $(srcdir)/Include/cpython/tracemalloc.h \ $(srcdir)/Include/cpython/tupleobject.h \ diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 248b63f2577..71f508a7e8b 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -193,6 +193,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index da07a139b7c..547e9ee1abf 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -486,6 +486,9 @@ Include\cpython + + Include\cpython + Include\cpython From c53c55b6ed9550dbffc5aad08aad7b920d095972 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Tue, 7 Oct 2025 19:29:43 -0500 Subject: [PATCH 061/373] Remove long-obsolete PCbuild/field3.py script (GH-139739) As far as I can tell, it has not actually been used since 2003. --- PCbuild/field3.py | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 PCbuild/field3.py diff --git a/PCbuild/field3.py b/PCbuild/field3.py deleted file mode 100644 index edcbe36ae08..00000000000 --- a/PCbuild/field3.py +++ /dev/null @@ -1,35 +0,0 @@ -# An absurd workaround for the lack of arithmetic in MS's resource compiler. -# After building Python, run this, then paste the output into the appropriate -# part of PC\python_nt.rc. -# Example output: -# -# * For 2.3a0, -# * PY_MICRO_VERSION = 0 -# * PY_RELEASE_LEVEL = 'alpha' = 0xA -# * PY_RELEASE_SERIAL = 1 -# * -# * and 0*1000 + 10*10 + 1 = 101. -# */ -# #define FIELD3 101 - -import sys - -major, minor, micro, level, serial = sys.version_info -levelnum = {'alpha': 0xA, - 'beta': 0xB, - 'candidate': 0xC, - 'final': 0xF, - }[level] -string = sys.version.split()[0] # like '2.3a0' - -print(" * For %s," % string) -print(" * PY_MICRO_VERSION = %d" % micro) -print(" * PY_RELEASE_LEVEL = %r = %s" % (level, hex(levelnum))) -print(" * PY_RELEASE_SERIAL = %d" % serial) -print(" *") - -field3 = micro * 1000 + levelnum * 10 + serial - -print(" * and %d*1000 + %d*10 + %d = %d" % (micro, levelnum, serial, field3)) -print(" */") -print("#define FIELD3", field3) From a15aeec29efa5b3d5d5568278c13bb3fc45f52ef Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Tue, 7 Oct 2025 19:25:06 -0700 Subject: [PATCH 062/373] GH-139590: Run `ruff format` on pre-commit for Tools/wasm (#139591) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- .pre-commit-config.yaml | 4 + Tools/wasm/.ruff.toml | 28 ++ Tools/wasm/emscripten/__main__.py | 70 ++-- .../wasm/emscripten/prepare_external_wasm.py | 5 +- Tools/wasm/emscripten/wasm_assets.py | 4 +- Tools/wasm/emscripten/web_example/server.py | 10 +- Tools/wasm/wasi.py | 10 +- Tools/wasm/wasi/__main__.py | 322 +++++++++++------- 8 files changed, 305 insertions(+), 148 deletions(-) create mode 100644 Tools/wasm/.ruff.toml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 014dab1d2b5..0e00ffb3bd2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,6 +34,10 @@ repos: name: Run Ruff (format) on Tools/build/check_warnings.py args: [--check, --config=Tools/build/.ruff.toml] files: ^Tools/build/check_warnings.py + - id: ruff-format + name: Run Ruff (format) on Tools/wasm/ + args: [--check, --config=Tools/wasm/.ruff.toml] + files: ^Tools/wasm/ - repo: https://github.com/psf/black-pre-commit-mirror rev: 25.9.0 diff --git a/Tools/wasm/.ruff.toml b/Tools/wasm/.ruff.toml new file mode 100644 index 00000000000..aabcf8dc4f5 --- /dev/null +++ b/Tools/wasm/.ruff.toml @@ -0,0 +1,28 @@ +extend = "../../.ruff.toml" # Inherit the project-wide settings + +[format] +preview = true +docstring-code-format = true + +[lint] +select = [ + "C4", # flake8-comprehensions + "E", # pycodestyle + "F", # pyflakes + "I", # isort + "ISC", # flake8-implicit-str-concat + "LOG", # flake8-logging + "PGH", # pygrep-hooks + "PT", # flake8-pytest-style + "PYI", # flake8-pyi + "RUF100", # Ban unused `# noqa` comments + "UP", # pyupgrade + "W", # pycodestyle + "YTT", # flake8-2020 +] +ignore = [ + "E501", # Line too long + "F541", # f-string without any placeholders + "PYI024", # Use `typing.NamedTuple` instead of `collections.namedtuple` + "PYI025", # Use `from collections.abc import Set as AbstractSet` +] diff --git a/Tools/wasm/emscripten/__main__.py b/Tools/wasm/emscripten/__main__.py index 202dd298199..fdf3142c0a3 100644 --- a/Tools/wasm/emscripten/__main__.py +++ b/Tools/wasm/emscripten/__main__.py @@ -33,7 +33,9 @@ PREFIX_DIR = CROSS_BUILD_DIR / HOST_TRIPLE / "prefix" LOCAL_SETUP = CHECKOUT / "Modules" / "Setup.local" -LOCAL_SETUP_MARKER = "# Generated by Tools/wasm/emscripten.py\n".encode("utf-8") +LOCAL_SETUP_MARKER = "# Generated by Tools/wasm/emscripten.py\n".encode( + "utf-8" +) def updated_env(updates={}): @@ -45,7 +47,9 @@ def updated_env(updates={}): # https://reproducible-builds.org/docs/source-date-epoch/ git_epoch_cmd = ["git", "log", "-1", "--pretty=%ct"] try: - epoch = subprocess.check_output(git_epoch_cmd, encoding="utf-8").strip() + epoch = subprocess.check_output( + git_epoch_cmd, encoding="utf-8" + ).strip() env_defaults["SOURCE_DATE_EPOCH"] = epoch except subprocess.CalledProcessError: pass # Might be building from a tarball. @@ -79,7 +83,11 @@ def wrapper(context): terminal_width = 80 print("⎯" * terminal_width) print("📁", working_dir) - if clean_ok and getattr(context, "clean", False) and working_dir.exists(): + if ( + clean_ok + and getattr(context, "clean", False) + and working_dir.exists() + ): print("🚮 Deleting directory (--clean)...") shutil.rmtree(working_dir) @@ -128,7 +136,9 @@ def build_python_path(): if not binary.is_file(): binary = binary.with_suffix(".exe") if not binary.is_file(): - raise FileNotFoundError("Unable to find `python(.exe)` in " f"{NATIVE_BUILD_DIR}") + raise FileNotFoundError( + f"Unable to find `python(.exe)` in {NATIVE_BUILD_DIR}" + ) return binary @@ -158,7 +168,8 @@ def make_build_python(context, working_dir): cmd = [ binary, "-c", - "import sys; " "print(f'{sys.version_info.major}.{sys.version_info.minor}')", + "import sys; " + "print(f'{sys.version_info.major}.{sys.version_info.minor}')", ] version = subprocess.check_output(cmd, encoding="utf-8").strip() @@ -173,7 +184,9 @@ def check_shasum(file: str, expected_shasum: str): def download_and_unpack(working_dir: Path, url: str, expected_shasum: str): - with tempfile.NamedTemporaryFile(suffix=".tar.gz", delete_on_close=False) as tmp_file: + with tempfile.NamedTemporaryFile( + suffix=".tar.gz", delete_on_close=False + ) as tmp_file: with urlopen(url) as response: shutil.copyfileobj(response, tmp_file) tmp_file.close() @@ -186,7 +199,11 @@ def make_emscripten_libffi(context, working_dir): ver = "3.4.6" libffi_dir = working_dir / f"libffi-{ver}" shutil.rmtree(libffi_dir, ignore_errors=True) - download_and_unpack(working_dir, f"https://github.com/libffi/libffi/releases/download/v{ver}/libffi-{ver}.tar.gz", "b0dea9df23c863a7a50e825440f3ebffabd65df1497108e5d437747843895a4e") + download_and_unpack( + working_dir, + f"https://github.com/libffi/libffi/releases/download/v{ver}/libffi-{ver}.tar.gz", + "b0dea9df23c863a7a50e825440f3ebffabd65df1497108e5d437747843895a4e", + ) call( [EMSCRIPTEN_DIR / "make_libffi.sh"], env=updated_env({"PREFIX": PREFIX_DIR}), @@ -200,7 +217,11 @@ def make_mpdec(context, working_dir): ver = "4.0.1" mpdec_dir = working_dir / f"mpdecimal-{ver}" shutil.rmtree(mpdec_dir, ignore_errors=True) - download_and_unpack(working_dir, f"https://www.bytereef.org/software/mpdecimal/releases/mpdecimal-{ver}.tar.gz", "96d33abb4bb0070c7be0fed4246cd38416188325f820468214471938545b1ac8") + download_and_unpack( + working_dir, + f"https://www.bytereef.org/software/mpdecimal/releases/mpdecimal-{ver}.tar.gz", + "96d33abb4bb0070c7be0fed4246cd38416188325f820468214471938545b1ac8", + ) call( [ "emconfigure", @@ -214,10 +235,7 @@ def make_mpdec(context, working_dir): quiet=context.quiet, ) call( - [ - "make", - "install" - ], + ["make", "install"], cwd=mpdec_dir, quiet=context.quiet, ) @@ -226,17 +244,15 @@ def make_mpdec(context, working_dir): @subdir(HOST_DIR, clean_ok=True) def configure_emscripten_python(context, working_dir): """Configure the emscripten/host build.""" - config_site = os.fsdecode( - EMSCRIPTEN_DIR / "config.site-wasm32-emscripten" - ) + config_site = os.fsdecode(EMSCRIPTEN_DIR / "config.site-wasm32-emscripten") emscripten_build_dir = working_dir.relative_to(CHECKOUT) python_build_dir = NATIVE_BUILD_DIR / "build" lib_dirs = list(python_build_dir.glob("lib.*")) - assert ( - len(lib_dirs) == 1 - ), f"Expected a single lib.* directory in {python_build_dir}" + assert len(lib_dirs) == 1, ( + f"Expected a single lib.* directory in {python_build_dir}" + ) lib_dir = os.fsdecode(lib_dirs[0]) pydebug = lib_dir.endswith("-pydebug") python_version = lib_dir.removesuffix("-pydebug").rpartition("-")[-1] @@ -290,7 +306,9 @@ def configure_emscripten_python(context, working_dir): quiet=context.quiet, ) - shutil.copy(EMSCRIPTEN_DIR / "node_entry.mjs", working_dir / "node_entry.mjs") + shutil.copy( + EMSCRIPTEN_DIR / "node_entry.mjs", working_dir / "node_entry.mjs" + ) node_entry = working_dir / "node_entry.mjs" exec_script = working_dir / "python.sh" @@ -383,13 +401,15 @@ def main(): subcommands = parser.add_subparsers(dest="subcommand") build = subcommands.add_parser("build", help="Build everything") configure_build = subcommands.add_parser( - "configure-build-python", help="Run `configure` for the " "build Python" + "configure-build-python", help="Run `configure` for the build Python" ) make_mpdec_cmd = subcommands.add_parser( - "make-mpdec", help="Clone mpdec repo, configure and build it for emscripten" + "make-mpdec", + help="Clone mpdec repo, configure and build it for emscripten", ) make_libffi_cmd = subcommands.add_parser( - "make-libffi", help="Clone libffi repo, configure and build it for emscripten" + "make-libffi", + help="Clone libffi repo, configure and build it for emscripten", ) make_build = subcommands.add_parser( "make-build-python", help="Run `make` for the build Python" @@ -457,7 +477,11 @@ def main(): if not context.subcommand: # No command provided, display help and exit - print("Expected one of", ", ".join(sorted(dispatch.keys())), file=sys.stderr) + print( + "Expected one of", + ", ".join(sorted(dispatch.keys())), + file=sys.stderr, + ) parser.print_help(sys.stderr) sys.exit(1) dispatch[context.subcommand](context) diff --git a/Tools/wasm/emscripten/prepare_external_wasm.py b/Tools/wasm/emscripten/prepare_external_wasm.py index 960e5aefd24..1b0a9de4b1f 100644 --- a/Tools/wasm/emscripten/prepare_external_wasm.py +++ b/Tools/wasm/emscripten/prepare_external_wasm.py @@ -19,6 +19,7 @@ }}); """ + def prepare_wasm(input_file, output_file, function_name): # Read the compiled WASM as binary and convert to hex wasm_bytes = Path(input_file).read_bytes() @@ -31,9 +32,7 @@ def prepare_wasm(input_file, output_file, function_name): ) Path(output_file).write_text(js_content) - print( - f"Successfully compiled {input_file} and generated {output_file}" - ) + print(f"Successfully compiled {input_file} and generated {output_file}") return 0 diff --git a/Tools/wasm/emscripten/wasm_assets.py b/Tools/wasm/emscripten/wasm_assets.py index 78da913ff60..90f318f319a 100755 --- a/Tools/wasm/emscripten/wasm_assets.py +++ b/Tools/wasm/emscripten/wasm_assets.py @@ -27,7 +27,9 @@ WASM_STDLIB_ZIP = ( WASM_LIB / f"python{sys.version_info.major}{sys.version_info.minor}.zip" ) -WASM_STDLIB = WASM_LIB / f"python{sys.version_info.major}.{sys.version_info.minor}" +WASM_STDLIB = ( + WASM_LIB / f"python{sys.version_info.major}.{sys.version_info.minor}" +) WASM_DYNLOAD = WASM_STDLIB / "lib-dynload" diff --git a/Tools/wasm/emscripten/web_example/server.py b/Tools/wasm/emscripten/web_example/server.py index 768e6f84e07..f2e6ed56c6b 100755 --- a/Tools/wasm/emscripten/web_example/server.py +++ b/Tools/wasm/emscripten/web_example/server.py @@ -6,10 +6,16 @@ description="Start a local webserver with a Python terminal." ) parser.add_argument( - "--port", type=int, default=8000, help="port for the http server to listen on" + "--port", + type=int, + default=8000, + help="port for the http server to listen on", ) parser.add_argument( - "--bind", type=str, default="127.0.0.1", help="Bind address (empty for all)" + "--bind", + type=str, + default="127.0.0.1", + help="Bind address (empty for all)", ) diff --git a/Tools/wasm/wasi.py b/Tools/wasm/wasi.py index b49b27cbbbe..af55e03d10f 100644 --- a/Tools/wasm/wasi.py +++ b/Tools/wasm/wasi.py @@ -1,10 +1,12 @@ -if __name__ == "__main__": +if __name__ == "__main__": import pathlib import runpy import sys - print("⚠️ WARNING: This script is deprecated and slated for removal in Python 3.20; " - "execute the `wasi/` directory instead (i.e. `python Tools/wasm/wasi`)\n", - file=sys.stderr) + print( + "⚠️ WARNING: This script is deprecated and slated for removal in Python 3.20; " + "execute the `wasi/` directory instead (i.e. `python Tools/wasm/wasi`)\n", + file=sys.stderr, + ) runpy.run_path(pathlib.Path(__file__).parent / "wasi", run_name="__main__") diff --git a/Tools/wasm/wasi/__main__.py b/Tools/wasm/wasi/__main__.py index 973d78caa08..a0658cb351a 100644 --- a/Tools/wasm/wasi/__main__.py +++ b/Tools/wasm/wasi/__main__.py @@ -4,6 +4,7 @@ import contextlib import functools import os + try: from os import process_cpu_count as cpu_count except ImportError: @@ -17,15 +18,19 @@ CHECKOUT = pathlib.Path(__file__).parent.parent.parent.parent -assert (CHECKOUT / "configure").is_file(), "Please update the location of the file" +assert (CHECKOUT / "configure").is_file(), ( + "Please update the location of the file" +) CROSS_BUILD_DIR = CHECKOUT / "cross-build" # Build platform can also be found via `config.guess`. BUILD_DIR = CROSS_BUILD_DIR / sysconfig.get_config_var("BUILD_GNU_TYPE") LOCAL_SETUP = CHECKOUT / "Modules" / "Setup.local" -LOCAL_SETUP_MARKER = ("# Generated by Tools/wasm/wasi .\n" - "# Required to statically build extension modules.").encode("utf-8") +LOCAL_SETUP_MARKER = ( + "# Generated by Tools/wasm/wasi .\n" + "# Required to statically build extension modules." +).encode("utf-8") WASI_SDK_VERSION = 24 @@ -42,7 +47,9 @@ def updated_env(updates={}): # https://reproducible-builds.org/docs/source-date-epoch/ git_epoch_cmd = ["git", "log", "-1", "--pretty=%ct"] try: - epoch = subprocess.check_output(git_epoch_cmd, encoding="utf-8").strip() + epoch = subprocess.check_output( + git_epoch_cmd, encoding="utf-8" + ).strip() env_defaults["SOURCE_DATE_EPOCH"] = epoch except subprocess.CalledProcessError: pass # Might be building from a tarball. @@ -63,6 +70,7 @@ def updated_env(updates={}): def subdir(working_dir, *, clean_ok=False): """Decorator to change to a working directory.""" + def decorator(func): @functools.wraps(func) def wrapper(context): @@ -71,16 +79,20 @@ def wrapper(context): if callable(working_dir): working_dir = working_dir(context) try: - tput_output = subprocess.check_output(["tput", "cols"], - encoding="utf-8") + tput_output = subprocess.check_output( + ["tput", "cols"], encoding="utf-8" + ) except subprocess.CalledProcessError: terminal_width = 80 else: terminal_width = int(tput_output.strip()) print("⎯" * terminal_width) print("📁", working_dir) - if (clean_ok and getattr(context, "clean", False) and - working_dir.exists()): + if ( + clean_ok + and getattr(context, "clean", False) + and working_dir.exists() + ): print("🚮 Deleting directory (--clean)...") shutil.rmtree(working_dir) @@ -110,11 +122,14 @@ def call(command, *, context=None, quiet=False, logdir=None, **kwargs): stdout = None stderr = None else: - stdout = tempfile.NamedTemporaryFile("w", encoding="utf-8", - delete=False, - dir=logdir, - prefix="cpython-wasi-", - suffix=".log") + stdout = tempfile.NamedTemporaryFile( + "w", + encoding="utf-8", + delete=False, + dir=logdir, + prefix="cpython-wasi-", + suffix=".log", + ) stderr = subprocess.STDOUT print(f"📝 Logging output to {stdout.name} (--quiet)...") @@ -127,8 +142,9 @@ def build_python_path(): if not binary.is_file(): binary = binary.with_suffix(".exe") if not binary.is_file(): - raise FileNotFoundError("Unable to find `python(.exe)` in " - f"{BUILD_DIR}") + raise FileNotFoundError( + f"Unable to find `python(.exe)` in {BUILD_DIR}" + ) return binary @@ -136,9 +152,11 @@ def build_python_path(): def build_python_is_pydebug(): """Find out if the build Python is a pydebug build.""" test = "import sys, test.support; sys.exit(test.support.Py_DEBUG)" - result = subprocess.run([build_python_path(), "-c", test], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + result = subprocess.run( + [build_python_path(), "-c", test], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) return bool(result.returncode) @@ -154,7 +172,7 @@ def configure_build_python(context, working_dir): print(f"📝 Creating {LOCAL_SETUP} ...") LOCAL_SETUP.write_bytes(LOCAL_SETUP_MARKER) - configure = [os.path.relpath(CHECKOUT / 'configure', working_dir)] + configure = [os.path.relpath(CHECKOUT / "configure", working_dir)] if context.args: configure.extend(context.args) @@ -164,13 +182,15 @@ def configure_build_python(context, working_dir): @subdir(BUILD_DIR) def make_build_python(context, working_dir): """Make/build the build Python.""" - call(["make", "--jobs", str(cpu_count()), "all"], - context=context) + call(["make", "--jobs", str(cpu_count()), "all"], context=context) binary = build_python_path() - cmd = [binary, "-c", - "import sys; " - "print(f'{sys.version_info.major}.{sys.version_info.minor}')"] + cmd = [ + binary, + "-c", + "import sys; " + "print(f'{sys.version_info.major}.{sys.version_info.minor}')", + ] version = subprocess.check_output(cmd, encoding="utf-8").strip() print(f"🎉 {binary} {version}") @@ -188,8 +208,11 @@ def find_wasi_sdk(): # Starting with WASI SDK 23, the tarballs went from containing a directory named # ``wasi-sdk-{WASI_SDK_VERSION}.0`` to e.g. # ``wasi-sdk-{WASI_SDK_VERSION}.0-x86_64-linux``. - potential_sdks = [path for path in opt_path.glob(f"wasi-sdk-{WASI_SDK_VERSION}.0*") - if path.is_dir()] + potential_sdks = [ + path + for path in opt_path.glob(f"wasi-sdk-{WASI_SDK_VERSION}.0*") + if path.is_dir() + ] if len(potential_sdks) == 1: return potential_sdks[0] elif (default_path := opt_path / "wasi-sdk").is_dir(): @@ -200,8 +223,13 @@ def wasi_sdk_env(context): """Calculate environment variables for building with wasi-sdk.""" wasi_sdk_path = context.wasi_sdk_path sysroot = wasi_sdk_path / "share" / "wasi-sysroot" - env = {"CC": "clang", "CPP": "clang-cpp", "CXX": "clang++", - "AR": "llvm-ar", "RANLIB": "ranlib"} + env = { + "CC": "clang", + "CPP": "clang-cpp", + "CXX": "clang++", + "AR": "llvm-ar", + "RANLIB": "ranlib", + } for env_var, binary_name in list(env.items()): env[env_var] = os.fsdecode(wasi_sdk_path / "bin" / binary_name) @@ -212,16 +240,20 @@ def wasi_sdk_env(context): env["PKG_CONFIG_PATH"] = "" env["PKG_CONFIG_LIBDIR"] = os.pathsep.join( - map(os.fsdecode, - [sysroot / "lib" / "pkgconfig", - sysroot / "share" / "pkgconfig"])) + map( + os.fsdecode, + [sysroot / "lib" / "pkgconfig", sysroot / "share" / "pkgconfig"], + ) + ) env["PKG_CONFIG_SYSROOT_DIR"] = os.fsdecode(sysroot) env["WASI_SDK_PATH"] = os.fsdecode(wasi_sdk_path) env["WASI_SYSROOT"] = os.fsdecode(sysroot) - env["PATH"] = os.pathsep.join([os.fsdecode(wasi_sdk_path / "bin"), - os.environ["PATH"]]) + env["PATH"] = os.pathsep.join([ + os.fsdecode(wasi_sdk_path / "bin"), + os.environ["PATH"], + ]) return env @@ -230,54 +262,70 @@ def wasi_sdk_env(context): def configure_wasi_python(context, working_dir): """Configure the WASI/host build.""" if not context.wasi_sdk_path or not context.wasi_sdk_path.exists(): - raise ValueError("WASI-SDK not found; " - "download from " - "https://github.com/WebAssembly/wasi-sdk and/or " - "specify via $WASI_SDK_PATH or --wasi-sdk") + raise ValueError( + "WASI-SDK not found; " + "download from " + "https://github.com/WebAssembly/wasi-sdk and/or " + "specify via $WASI_SDK_PATH or --wasi-sdk" + ) - config_site = os.fsdecode(CHECKOUT / "Tools" / "wasm" / "wasi" / "config.site-wasm32-wasi") + config_site = os.fsdecode( + CHECKOUT / "Tools" / "wasm" / "wasi" / "config.site-wasm32-wasi" + ) wasi_build_dir = working_dir.relative_to(CHECKOUT) python_build_dir = BUILD_DIR / "build" lib_dirs = list(python_build_dir.glob("lib.*")) - assert len(lib_dirs) == 1, f"Expected a single lib.* directory in {python_build_dir}" + assert len(lib_dirs) == 1, ( + f"Expected a single lib.* directory in {python_build_dir}" + ) lib_dir = os.fsdecode(lib_dirs[0]) python_version = lib_dir.rpartition("-")[-1] - sysconfig_data_dir = f"{wasi_build_dir}/build/lib.wasi-wasm32-{python_version}" + sysconfig_data_dir = ( + f"{wasi_build_dir}/build/lib.wasi-wasm32-{python_version}" + ) # Use PYTHONPATH to include sysconfig data which must be anchored to the # WASI guest's `/` directory. - args = {"GUEST_DIR": "/", - "HOST_DIR": CHECKOUT, - "ENV_VAR_NAME": "PYTHONPATH", - "ENV_VAR_VALUE": f"/{sysconfig_data_dir}", - "PYTHON_WASM": working_dir / "python.wasm"} + args = { + "GUEST_DIR": "/", + "HOST_DIR": CHECKOUT, + "ENV_VAR_NAME": "PYTHONPATH", + "ENV_VAR_VALUE": f"/{sysconfig_data_dir}", + "PYTHON_WASM": working_dir / "python.wasm", + } # Check dynamically for wasmtime in case it was specified manually via # `--host-runner`. if WASMTIME_HOST_RUNNER_VAR in context.host_runner: if wasmtime := shutil.which("wasmtime"): args[WASMTIME_VAR_NAME] = wasmtime else: - raise FileNotFoundError("wasmtime not found; download from " - "https://github.com/bytecodealliance/wasmtime") + raise FileNotFoundError( + "wasmtime not found; download from " + "https://github.com/bytecodealliance/wasmtime" + ) host_runner = context.host_runner.format_map(args) env_additions = {"CONFIG_SITE": config_site, "HOSTRUNNER": host_runner} build_python = os.fsdecode(build_python_path()) # The path to `configure` MUST be relative, else `python.wasm` is unable # to find the stdlib due to Python not recognizing that it's being # executed from within a checkout. - configure = [os.path.relpath(CHECKOUT / 'configure', working_dir), - f"--host={context.host_triple}", - f"--build={BUILD_DIR.name}", - f"--with-build-python={build_python}"] + configure = [ + os.path.relpath(CHECKOUT / "configure", working_dir), + f"--host={context.host_triple}", + f"--build={BUILD_DIR.name}", + f"--with-build-python={build_python}", + ] if build_python_is_pydebug(): configure.append("--with-pydebug") if context.args: configure.extend(context.args) - call(configure, - env=updated_env(env_additions | wasi_sdk_env(context)), - context=context) + call( + configure, + env=updated_env(env_additions | wasi_sdk_env(context)), + context=context, + ) python_wasm = working_dir / "python.wasm" exec_script = working_dir / "python.sh" @@ -291,9 +339,11 @@ def configure_wasi_python(context, working_dir): @subdir(lambda context: CROSS_BUILD_DIR / context.host_triple) def make_wasi_python(context, working_dir): """Run `make` for the WASI/host build.""" - call(["make", "--jobs", str(cpu_count()), "all"], - env=updated_env(), - context=context) + call( + ["make", "--jobs", str(cpu_count()), "all"], + env=updated_env(), + context=context, + ) exec_script = working_dir / "python.sh" call([exec_script, "--version"], quiet=False) @@ -305,11 +355,16 @@ def make_wasi_python(context, working_dir): def build_all(context): """Build everything.""" - steps = [configure_build_python, make_build_python, configure_wasi_python, - make_wasi_python] + steps = [ + configure_build_python, + make_build_python, + configure_wasi_python, + make_wasi_python, + ] for step in steps: step(context) + def clean_contents(context): """Delete all files created by this script.""" if CROSS_BUILD_DIR.exists(): @@ -324,76 +379,113 @@ def clean_contents(context): def main(): default_host_triple = "wasm32-wasip1" default_wasi_sdk = find_wasi_sdk() - default_host_runner = (f"{WASMTIME_HOST_RUNNER_VAR} run " - # Make sure the stack size will work for a pydebug - # build. - # Use 16 MiB stack. - "--wasm max-wasm-stack=16777216 " - # Enable thread support; causes use of preview1. - #"--wasm threads=y --wasi threads=y " - # Map the checkout to / to load the stdlib from /Lib. - "--dir {HOST_DIR}::{GUEST_DIR} " - # Set PYTHONPATH to the sysconfig data. - "--env {ENV_VAR_NAME}={ENV_VAR_VALUE}") + default_host_runner = ( + f"{WASMTIME_HOST_RUNNER_VAR} run " + # Make sure the stack size will work for a pydebug + # build. + # Use 16 MiB stack. + "--wasm max-wasm-stack=16777216 " + # Enable thread support; causes use of preview1. + # "--wasm threads=y --wasi threads=y " + # Map the checkout to / to load the stdlib from /Lib. + "--dir {HOST_DIR}::{GUEST_DIR} " + # Set PYTHONPATH to the sysconfig data. + "--env {ENV_VAR_NAME}={ENV_VAR_VALUE}" + ) default_logdir = pathlib.Path(tempfile.gettempdir()) parser = argparse.ArgumentParser() subcommands = parser.add_subparsers(dest="subcommand") build = subcommands.add_parser("build", help="Build everything") - configure_build = subcommands.add_parser("configure-build-python", - help="Run `configure` for the " - "build Python") - make_build = subcommands.add_parser("make-build-python", - help="Run `make` for the build Python") - configure_host = subcommands.add_parser("configure-host", - help="Run `configure` for the " - "host/WASI (pydebug builds " - "are inferred from the build " - "Python)") - make_host = subcommands.add_parser("make-host", - help="Run `make` for the host/WASI") - subcommands.add_parser("clean", help="Delete files and directories " - "created by this script") - for subcommand in build, configure_build, make_build, configure_host, make_host: - subcommand.add_argument("--quiet", action="store_true", default=False, - dest="quiet", - help="Redirect output from subprocesses to a log file") - subcommand.add_argument("--logdir", type=pathlib.Path, default=default_logdir, - help="Directory to store log files; " - f"defaults to {default_logdir}") + configure_build = subcommands.add_parser( + "configure-build-python", help="Run `configure` for the build Python" + ) + make_build = subcommands.add_parser( + "make-build-python", help="Run `make` for the build Python" + ) + configure_host = subcommands.add_parser( + "configure-host", + help="Run `configure` for the " + "host/WASI (pydebug builds " + "are inferred from the build " + "Python)", + ) + make_host = subcommands.add_parser( + "make-host", help="Run `make` for the host/WASI" + ) + subcommands.add_parser( + "clean", help="Delete files and directories created by this script" + ) + for subcommand in ( + build, + configure_build, + make_build, + configure_host, + make_host, + ): + subcommand.add_argument( + "--quiet", + action="store_true", + default=False, + dest="quiet", + help="Redirect output from subprocesses to a log file", + ) + subcommand.add_argument( + "--logdir", + type=pathlib.Path, + default=default_logdir, + help=f"Directory to store log files; defaults to {default_logdir}", + ) for subcommand in configure_build, configure_host: - subcommand.add_argument("--clean", action="store_true", default=False, - dest="clean", - help="Delete any relevant directories before building") + subcommand.add_argument( + "--clean", + action="store_true", + default=False, + dest="clean", + help="Delete any relevant directories before building", + ) for subcommand in build, configure_build, configure_host: - subcommand.add_argument("args", nargs="*", - help="Extra arguments to pass to `configure`") + subcommand.add_argument( + "args", nargs="*", help="Extra arguments to pass to `configure`" + ) for subcommand in build, configure_host: - subcommand.add_argument("--wasi-sdk", type=pathlib.Path, - dest="wasi_sdk_path", - default=default_wasi_sdk, - help=f"Path to the WASI SDK; defaults to {default_wasi_sdk}") - subcommand.add_argument("--host-runner", action="store", - default=default_host_runner, dest="host_runner", - help="Command template for running the WASI host; defaults to " - f"`{default_host_runner}`") + subcommand.add_argument( + "--wasi-sdk", + type=pathlib.Path, + dest="wasi_sdk_path", + default=default_wasi_sdk, + help=f"Path to the WASI SDK; defaults to {default_wasi_sdk}", + ) + subcommand.add_argument( + "--host-runner", + action="store", + default=default_host_runner, + dest="host_runner", + help="Command template for running the WASI host; defaults to " + f"`{default_host_runner}`", + ) for subcommand in build, configure_host, make_host: - subcommand.add_argument("--host-triple", action="store", - default=default_host_triple, - help="The target triple for the WASI host build; " - f"defaults to {default_host_triple}") + subcommand.add_argument( + "--host-triple", + action="store", + default=default_host_triple, + help="The target triple for the WASI host build; " + f"defaults to {default_host_triple}", + ) context = parser.parse_args() context.init_dir = pathlib.Path().absolute() - dispatch = {"configure-build-python": configure_build_python, - "make-build-python": make_build_python, - "configure-host": configure_wasi_python, - "make-host": make_wasi_python, - "build": build_all, - "clean": clean_contents} + dispatch = { + "configure-build-python": configure_build_python, + "make-build-python": make_build_python, + "configure-host": configure_wasi_python, + "make-host": make_wasi_python, + "build": build_all, + "clean": clean_contents, + } dispatch[context.subcommand](context) -if __name__ == "__main__": +if __name__ == "__main__": main() From c4e7d245d61ac4547ecf3362b28f64fc00aa88c0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 8 Oct 2025 12:10:58 +0200 Subject: [PATCH 063/373] gh-138342: Move _PyObject_VisitType() to the internal C API (#139734) --- Include/cpython/object.h | 4 ---- Include/internal/pycore_object.h | 4 ++++ Modules/_dbmmodule.c | 1 + Modules/_decimal/_decimal.c | 2 +- Modules/_gdbmmodule.c | 5 +++-- Modules/_multiprocessing/semaphore.c | 1 + Modules/_sqlite/prepare_protocol.c | 7 +++++++ Modules/_sqlite/statement.c | 7 +++++++ Modules/blake2module.c | 5 +++-- Modules/md5module.c | 3 ++- Modules/sha1module.c | 1 + Modules/sha2module.c | 3 ++- Modules/sha3module.c | 1 + Modules/socketmodule.c | 1 + Modules/unicodedata.c | 1 + 15 files changed, 35 insertions(+), 11 deletions(-) diff --git a/Include/cpython/object.h b/Include/cpython/object.h index 4e6f86f29d8..e2f87524c21 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -492,7 +492,3 @@ PyAPI_FUNC(int) PyUnstable_TryIncRef(PyObject *); PyAPI_FUNC(void) PyUnstable_EnableTryIncRef(PyObject *); PyAPI_FUNC(int) PyUnstable_Object_IsUniquelyReferenced(PyObject *); - -/* Utility for the tp_traverse slot of mutable heap types that have no other - * references. */ -PyAPI_FUNC(int) _PyObject_VisitType(PyObject *op, visitproc visit, void *arg); diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 40f8ca68c00..77560e5da66 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -1047,6 +1047,10 @@ static inline Py_ALWAYS_INLINE void _Py_INCREF_MORTAL(PyObject *op) } #endif +/* Utility for the tp_traverse slot of mutable heap types that have no other + * references. */ +PyAPI_FUNC(int) _PyObject_VisitType(PyObject *op, visitproc visit, void *arg); + #ifdef __cplusplus } #endif diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index 3fdcf22ffd5..06712015418 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -8,6 +8,7 @@ #endif #include "Python.h" +#include "pycore_object.h" // _PyObject_VisitType() #include #include diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 0696c150cd4..04b6695f8af 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -30,9 +30,9 @@ #endif #include +#include "pycore_object.h" // _PyObject_VisitType() #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_typeobject.h" -#include "complexobject.h" #include diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index a4bc0a0f675..87b84976f49 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -8,11 +8,12 @@ #endif #include "Python.h" -#include "pycore_pyerrors.h" // _PyErr_SetLocaleString() +#include "pycore_object.h" // _PyObject_VisitType() +#include "pycore_pyerrors.h" // _PyErr_SetLocaleString() #include "gdbm.h" #include -#include // free() +#include // free() #include #include diff --git a/Modules/_multiprocessing/semaphore.c b/Modules/_multiprocessing/semaphore.c index d5a1f27e9ff..85cc0ac70a6 100644 --- a/Modules/_multiprocessing/semaphore.c +++ b/Modules/_multiprocessing/semaphore.c @@ -8,6 +8,7 @@ */ #include "multiprocessing.h" +#include "pycore_object.h" // _PyObject_VisitType() #ifdef HAVE_SYS_TIME_H # include // gettimeofday() diff --git a/Modules/_sqlite/prepare_protocol.c b/Modules/_sqlite/prepare_protocol.c index d7ac09e3947..0e2812e1e4f 100644 --- a/Modules/_sqlite/prepare_protocol.c +++ b/Modules/_sqlite/prepare_protocol.c @@ -21,8 +21,15 @@ * 3. This notice may not be removed or altered from any source distribution. */ +#ifndef Py_BUILD_CORE_BUILTIN +# define Py_BUILD_CORE_MODULE 1 +#endif + #include "prepare_protocol.h" +#include "pycore_object.h" // _PyObject_VisitType() + + static int pysqlite_prepare_protocol_init(PyObject *self, PyObject *args, PyObject *kwargs) { diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c index 77181104eda..c551ca59b33 100644 --- a/Modules/_sqlite/statement.c +++ b/Modules/_sqlite/statement.c @@ -21,10 +21,17 @@ * 3. This notice may not be removed or altered from any source distribution. */ +#ifndef Py_BUILD_CORE_BUILTIN +# define Py_BUILD_CORE_MODULE 1 +#endif + #include "connection.h" #include "statement.h" #include "util.h" +#include "pycore_object.h" // _PyObject_VisitType() + + #define _pysqlite_Statement_CAST(op) ((pysqlite_Statement *)(op)) /* prototypes */ diff --git a/Modules/blake2module.c b/Modules/blake2module.c index 4921e8f945e..89b0ebd516f 100644 --- a/Modules/blake2module.c +++ b/Modules/blake2module.c @@ -16,9 +16,10 @@ #include "Python.h" #include "hashlib.h" -#include "pycore_strhex.h" // _Py_strhex() -#include "pycore_typeobject.h" #include "pycore_moduleobject.h" +#include "pycore_object.h" // _PyObject_VisitType() +#include "pycore_strhex.h" // _Py_strhex() +#include "pycore_typeobject.h" // QUICK CPU AUTODETECTION // diff --git a/Modules/md5module.c b/Modules/md5module.c index 6b6457427b6..56e9faf4c62 100644 --- a/Modules/md5module.c +++ b/Modules/md5module.c @@ -22,7 +22,8 @@ #endif #include "Python.h" -#include "pycore_strhex.h" // _Py_strhex() +#include "pycore_object.h" // _PyObject_VisitType() +#include "pycore_strhex.h" // _Py_strhex() #include "hashlib.h" diff --git a/Modules/sha1module.c b/Modules/sha1module.c index d64eb91458c..89e66240d1d 100644 --- a/Modules/sha1module.c +++ b/Modules/sha1module.c @@ -21,6 +21,7 @@ #include "Python.h" #include "hashlib.h" +#include "pycore_object.h" // _PyObject_VisitType() #include "pycore_strhex.h" // _Py_strhex() #include "pycore_typeobject.h" // _PyType_GetModuleState() diff --git a/Modules/sha2module.c b/Modules/sha2module.c index 66259fe47a0..9453b0be512 100644 --- a/Modules/sha2module.c +++ b/Modules/sha2module.c @@ -22,8 +22,9 @@ #include "Python.h" #include "pycore_moduleobject.h" // _PyModule_GetState() -#include "pycore_typeobject.h" // _PyType_GetModuleState() +#include "pycore_object.h" // _PyObject_VisitType() #include "pycore_strhex.h" // _Py_strhex() +#include "pycore_typeobject.h" // _PyType_GetModuleState() #include "hashlib.h" diff --git a/Modules/sha3module.c b/Modules/sha3module.c index 14c543b8641..38c9bc0405b 100644 --- a/Modules/sha3module.c +++ b/Modules/sha3module.c @@ -21,6 +21,7 @@ #endif #include "Python.h" +#include "pycore_object.h" // _PyObject_VisitType() #include "pycore_strhex.h" // _Py_strhex() #include "pycore_typeobject.h" // _PyType_GetModuleState() #include "hashlib.h" diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index ec8b53273bc..fa153efd8b7 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -109,6 +109,7 @@ Local naming conventions: #include "pycore_capsule.h" // _PyCapsule_SetTraverse() #include "pycore_fileutils.h" // _Py_set_inheritable() #include "pycore_moduleobject.h" // _PyModule_GetState +#include "pycore_object.h" // _PyObject_VisitType() #include "pycore_time.h" // _PyTime_AsMilliseconds() #include "pycore_pystate.h" // _Py_AssertHoldsTstate() diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c index 3c9bc35fa35..a3699beff7d 100644 --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -17,6 +17,7 @@ #endif #include "Python.h" +#include "pycore_object.h" // _PyObject_VisitType() #include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI #include From 5cea8435943b4f9d22c89c80d86c2ba3b392c6f9 Mon Sep 17 00:00:00 2001 From: Rogdham <3994389+Rogdham@users.noreply.github.com> Date: Wed, 8 Oct 2025 14:29:37 +0200 Subject: [PATCH 064/373] gh-137589: Zipfile tests: close file objects (GH-138080) Zipfile tests: close file objects --- Lib/test/test_zipfile/_path/test_path.py | 11 +++++--- Lib/test/test_zipfile/test_core.py | 34 +++++++++++++----------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/Lib/test/test_zipfile/_path/test_path.py b/Lib/test/test_zipfile/_path/test_path.py index 958a586b0dc..e7931b6f394 100644 --- a/Lib/test/test_zipfile/_path/test_path.py +++ b/Lib/test/test_zipfile/_path/test_path.py @@ -274,7 +274,8 @@ def test_pathlike_construction(self, alpharep): """ zipfile_ondisk = self.zipfile_ondisk(alpharep) pathlike = FakePath(str(zipfile_ondisk)) - zipfile.Path(pathlike) + root = zipfile.Path(pathlike) + root.root.close() @pass_alpharep def test_traverse_pathlike(self, alpharep): @@ -373,6 +374,7 @@ def test_root_on_disk(self, alpharep): root = zipfile.Path(self.zipfile_ondisk(alpharep)) assert root.name == 'alpharep.zip' == root.filename.name assert root.stem == 'alpharep' == root.filename.stem + root.root.close() @pass_alpharep def test_suffix(self, alpharep): @@ -574,11 +576,13 @@ def test_inheritance(self, alpharep): ) def test_pickle(self, alpharep, path_type, subpath): zipfile_ondisk = path_type(str(self.zipfile_ondisk(alpharep))) - - saved_1 = pickle.dumps(zipfile.Path(zipfile_ondisk, at=subpath)) + root = zipfile.Path(zipfile_ondisk, at=subpath) + saved_1 = pickle.dumps(root) + root.root.close() restored_1 = pickle.loads(saved_1) first, *rest = restored_1.iterdir() assert first.read_text(encoding='utf-8').startswith('content of ') + restored_1.root.close() @pass_alpharep def test_extract_orig_with_implied_dirs(self, alpharep): @@ -590,6 +594,7 @@ def test_extract_orig_with_implied_dirs(self, alpharep): # wrap the zipfile for its side effect zipfile.Path(zf) zf.extractall(source_path.parent) + zf.close() @pass_alpharep def test_getinfo_missing(self, alpharep): diff --git a/Lib/test/test_zipfile/test_core.py b/Lib/test/test_zipfile/test_core.py index 6acfefc74d6..1edb5dde998 100644 --- a/Lib/test/test_zipfile/test_core.py +++ b/Lib/test/test_zipfile/test_core.py @@ -312,26 +312,26 @@ def test_low_compression(self): self.assertEqual(openobj.read(1), b'2') def test_writestr_compression(self): - zipfp = zipfile.ZipFile(TESTFN2, "w") - zipfp.writestr("b.txt", "hello world", compress_type=self.compression) - info = zipfp.getinfo('b.txt') - self.assertEqual(info.compress_type, self.compression) + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + zipfp.writestr("b.txt", "hello world", compress_type=self.compression) + info = zipfp.getinfo('b.txt') + self.assertEqual(info.compress_type, self.compression) def test_writestr_compresslevel(self): - zipfp = zipfile.ZipFile(TESTFN2, "w", compresslevel=1) - zipfp.writestr("a.txt", "hello world", compress_type=self.compression) - zipfp.writestr("b.txt", "hello world", compress_type=self.compression, - compresslevel=2) + with zipfile.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp: + zipfp.writestr("a.txt", "hello world", compress_type=self.compression) + zipfp.writestr("b.txt", "hello world", compress_type=self.compression, + compresslevel=2) - # Compression level follows the constructor. - a_info = zipfp.getinfo('a.txt') - self.assertEqual(a_info.compress_type, self.compression) - self.assertEqual(a_info.compress_level, 1) + # Compression level follows the constructor. + a_info = zipfp.getinfo('a.txt') + self.assertEqual(a_info.compress_type, self.compression) + self.assertEqual(a_info.compress_level, 1) - # Compression level is overridden. - b_info = zipfp.getinfo('b.txt') - self.assertEqual(b_info.compress_type, self.compression) - self.assertEqual(b_info._compresslevel, 2) + # Compression level is overridden. + b_info = zipfp.getinfo('b.txt') + self.assertEqual(b_info.compress_type, self.compression) + self.assertEqual(b_info._compresslevel, 2) def test_read_return_size(self): # Issue #9837: ZipExtFile.read() shouldn't return more bytes @@ -2330,6 +2330,7 @@ def test_empty_zipfile(self): zipf = zipfile.ZipFile(TESTFN, mode="r") except zipfile.BadZipFile: self.fail("Unable to create empty ZIP file in 'w' mode") + zipf.close() zipf = zipfile.ZipFile(TESTFN, mode="a") zipf.close() @@ -2337,6 +2338,7 @@ def test_empty_zipfile(self): zipf = zipfile.ZipFile(TESTFN, mode="r") except: self.fail("Unable to create empty ZIP file in 'a' mode") + zipf.close() def test_open_empty_file(self): # Issue 1710703: Check that opening a file with less than 22 bytes From 3d3f126e86c653f9727c42081d9ce2e4a4d15de3 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 8 Oct 2025 14:56:00 +0200 Subject: [PATCH 065/373] gh-139353: Rename formatter_unicode.c to unicode_formatter.c (#139723) * Move Python/formatter_unicode.c to Objects/unicode_formatter.c. * Move Objects/stringlib/localeutil.h content into unicode_formatter.c. Remove localeutil.h. * Move _PyUnicode_InsertThousandsGrouping() to unicode_formatter.c and mark the function as static. * Rename unicode_fill() to _PyUnicode_Fill() and export it in pycore_unicodeobject.h. * Move MAX_UNICODE to pycore_unicodeobject.h as _Py_MAX_UNICODE. --- Include/internal/pycore_unicodeobject.h | 53 ++-- Makefile.pre.in | 3 +- Objects/stringlib/localeutil.h | 97 -------- .../unicode_formatter.c | 235 ++++++++++++++++++ Objects/unicodeobject.c | 186 +------------- PCbuild/_freeze_module.vcxproj | 2 +- PCbuild/_freeze_module.vcxproj.filters | 6 +- PCbuild/pythoncore.vcxproj | 2 +- PCbuild/pythoncore.vcxproj.filters | 6 +- Python/fileutils.c | 8 +- 10 files changed, 293 insertions(+), 305 deletions(-) delete mode 100644 Objects/stringlib/localeutil.h rename Python/formatter_unicode.c => Objects/unicode_formatter.c (88%) diff --git a/Include/internal/pycore_unicodeobject.h b/Include/internal/pycore_unicodeobject.h index c85c01da89a..f1c9bcd4788 100644 --- a/Include/internal/pycore_unicodeobject.h +++ b/Include/internal/pycore_unicodeobject.h @@ -11,6 +11,44 @@ extern "C" { #include "pycore_fileutils.h" // _Py_error_handler #include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI +// Maximum code point of Unicode 6.0: 0x10ffff (1,114,111). +#define _Py_MAX_UNICODE 0x10ffff + + +static inline void +_PyUnicode_Fill(int kind, void *data, Py_UCS4 value, + Py_ssize_t start, Py_ssize_t length) +{ + assert(0 <= start); + switch (kind) { + case PyUnicode_1BYTE_KIND: { + assert(value <= 0xff); + Py_UCS1 ch = (unsigned char)value; + Py_UCS1 *to = (Py_UCS1 *)data + start; + memset(to, ch, length); + break; + } + case PyUnicode_2BYTE_KIND: { + assert(value <= 0xffff); + Py_UCS2 ch = (Py_UCS2)value; + Py_UCS2 *to = (Py_UCS2 *)data + start; + const Py_UCS2 *end = to + length; + for (; to < end; ++to) *to = ch; + break; + } + case PyUnicode_4BYTE_KIND: { + assert(value <= _Py_MAX_UNICODE); + Py_UCS4 ch = value; + Py_UCS4 * to = (Py_UCS4 *)data + start; + const Py_UCS4 *end = to + length; + for (; to < end; ++to) *to = ch; + break; + } + default: Py_UNREACHABLE(); + } +} + + /* --- Characters Type APIs ----------------------------------------------- */ extern int _PyUnicode_IsXidStart(Py_UCS4 ch); @@ -240,21 +278,6 @@ extern PyObject* _PyUnicode_XStrip( ); -/* Using explicit passed-in values, insert the thousands grouping - into the string pointed to by buffer. For the argument descriptions, - see Objects/stringlib/localeutil.h */ -extern Py_ssize_t _PyUnicode_InsertThousandsGrouping( - _PyUnicodeWriter *writer, - Py_ssize_t n_buffer, - PyObject *digits, - Py_ssize_t d_pos, - Py_ssize_t n_digits, - Py_ssize_t min_width, - const char *grouping, - PyObject *thousands_sep, - Py_UCS4 *maxchar, - int forward); - /* Dedent a string. Behaviour is expected to be an exact match of `textwrap.dedent`. Return a new reference on success, NULL with exception set on error. diff --git a/Makefile.pre.in b/Makefile.pre.in index 987d55a9bdb..a5223246845 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -501,7 +501,6 @@ PYTHON_OBJS= \ Python/pystrtod.o \ Python/pystrhex.o \ Python/dtoa.o \ - Python/formatter_unicode.o \ Python/fileutils.o \ Python/suggestions.o \ Python/perf_trampoline.o \ @@ -558,6 +557,7 @@ OBJECT_OBJS= \ Objects/tupleobject.o \ Objects/typeobject.o \ Objects/typevarobject.o \ + Objects/unicode_formatter.o \ Objects/unicodeobject.o \ Objects/unicodectype.o \ Objects/unionobject.o \ @@ -2091,7 +2091,6 @@ UNICODE_DEPS = \ $(srcdir)/Objects/stringlib/fastsearch.h \ $(srcdir)/Objects/stringlib/find.h \ $(srcdir)/Objects/stringlib/find_max_char.h \ - $(srcdir)/Objects/stringlib/localeutil.h \ $(srcdir)/Objects/stringlib/partition.h \ $(srcdir)/Objects/stringlib/replace.h \ $(srcdir)/Objects/stringlib/repr.h \ diff --git a/Objects/stringlib/localeutil.h b/Objects/stringlib/localeutil.h deleted file mode 100644 index a4ab701de00..00000000000 --- a/Objects/stringlib/localeutil.h +++ /dev/null @@ -1,97 +0,0 @@ -/* _PyUnicode_InsertThousandsGrouping() helper functions */ - -typedef struct { - const char *grouping; - char previous; - Py_ssize_t i; /* Where we're currently pointing in grouping. */ -} GroupGenerator; - - -static void -GroupGenerator_init(GroupGenerator *self, const char *grouping) -{ - self->grouping = grouping; - self->i = 0; - self->previous = 0; -} - - -/* Returns the next grouping, or 0 to signify end. */ -static Py_ssize_t -GroupGenerator_next(GroupGenerator *self) -{ - /* Note that we don't really do much error checking here. If a - grouping string contains just CHAR_MAX, for example, then just - terminate the generator. That shouldn't happen, but at least we - fail gracefully. */ - switch (self->grouping[self->i]) { - case 0: - return self->previous; - case CHAR_MAX: - /* Stop the generator. */ - return 0; - default: { - char ch = self->grouping[self->i]; - self->previous = ch; - self->i++; - return (Py_ssize_t)ch; - } - } -} - - -/* Fill in some digits, leading zeros, and thousands separator. All - are optional, depending on when we're called. */ -static void -InsertThousandsGrouping_fill(_PyUnicodeWriter *writer, Py_ssize_t *buffer_pos, - PyObject *digits, Py_ssize_t *digits_pos, - Py_ssize_t n_chars, Py_ssize_t n_zeros, - PyObject *thousands_sep, Py_ssize_t thousands_sep_len, - Py_UCS4 *maxchar, int forward) -{ - if (!writer) { - /* if maxchar > 127, maxchar is already set */ - if (*maxchar == 127 && thousands_sep) { - Py_UCS4 maxchar2 = PyUnicode_MAX_CHAR_VALUE(thousands_sep); - *maxchar = Py_MAX(*maxchar, maxchar2); - } - return; - } - - if (thousands_sep) { - if (!forward) { - *buffer_pos -= thousands_sep_len; - } - /* Copy the thousands_sep chars into the buffer. */ - _PyUnicode_FastCopyCharacters(writer->buffer, *buffer_pos, - thousands_sep, 0, - thousands_sep_len); - if (forward) { - *buffer_pos += thousands_sep_len; - } - } - - if (!forward) { - *buffer_pos -= n_chars; - *digits_pos -= n_chars; - } - _PyUnicode_FastCopyCharacters(writer->buffer, *buffer_pos, - digits, *digits_pos, - n_chars); - if (forward) { - *buffer_pos += n_chars; - *digits_pos += n_chars; - } - - if (n_zeros) { - if (!forward) { - *buffer_pos -= n_zeros; - } - int kind = PyUnicode_KIND(writer->buffer); - void *data = PyUnicode_DATA(writer->buffer); - unicode_fill(kind, data, '0', *buffer_pos, n_zeros); - if (forward) { - *buffer_pos += n_zeros; - } - } -} diff --git a/Python/formatter_unicode.c b/Objects/unicode_formatter.c similarity index 88% rename from Python/formatter_unicode.c rename to Objects/unicode_formatter.c index 30807f428c7..b8604d13559 100644 --- a/Python/formatter_unicode.c +++ b/Objects/unicode_formatter.c @@ -8,6 +8,241 @@ #include "pycore_unicodeobject.h" // PyUnicode_MAX_CHAR_VALUE() #include + +/* _PyUnicode_InsertThousandsGrouping() helper functions */ + +typedef struct { + const char *grouping; + char previous; + Py_ssize_t i; /* Where we're currently pointing in grouping. */ +} GroupGenerator; + + +static void +GroupGenerator_init(GroupGenerator *self, const char *grouping) +{ + self->grouping = grouping; + self->i = 0; + self->previous = 0; +} + + +/* Returns the next grouping, or 0 to signify end. */ +static Py_ssize_t +GroupGenerator_next(GroupGenerator *self) +{ + /* Note that we don't really do much error checking here. If a + grouping string contains just CHAR_MAX, for example, then just + terminate the generator. That shouldn't happen, but at least we + fail gracefully. */ + switch (self->grouping[self->i]) { + case 0: + return self->previous; + case CHAR_MAX: + /* Stop the generator. */ + return 0; + default: { + char ch = self->grouping[self->i]; + self->previous = ch; + self->i++; + return (Py_ssize_t)ch; + } + } +} + + +/* Fill in some digits, leading zeros, and thousands separator. All + are optional, depending on when we're called. */ +static void +InsertThousandsGrouping_fill(_PyUnicodeWriter *writer, Py_ssize_t *buffer_pos, + PyObject *digits, Py_ssize_t *digits_pos, + Py_ssize_t n_chars, Py_ssize_t n_zeros, + PyObject *thousands_sep, Py_ssize_t thousands_sep_len, + Py_UCS4 *maxchar, int forward) +{ + if (!writer) { + /* if maxchar > 127, maxchar is already set */ + if (*maxchar == 127 && thousands_sep) { + Py_UCS4 maxchar2 = PyUnicode_MAX_CHAR_VALUE(thousands_sep); + *maxchar = Py_MAX(*maxchar, maxchar2); + } + return; + } + + if (thousands_sep) { + if (!forward) { + *buffer_pos -= thousands_sep_len; + } + /* Copy the thousands_sep chars into the buffer. */ + _PyUnicode_FastCopyCharacters(writer->buffer, *buffer_pos, + thousands_sep, 0, + thousands_sep_len); + if (forward) { + *buffer_pos += thousands_sep_len; + } + } + + if (!forward) { + *buffer_pos -= n_chars; + *digits_pos -= n_chars; + } + _PyUnicode_FastCopyCharacters(writer->buffer, *buffer_pos, + digits, *digits_pos, + n_chars); + if (forward) { + *buffer_pos += n_chars; + *digits_pos += n_chars; + } + + if (n_zeros) { + if (!forward) { + *buffer_pos -= n_zeros; + } + int kind = PyUnicode_KIND(writer->buffer); + void *data = PyUnicode_DATA(writer->buffer); + _PyUnicode_Fill(kind, data, '0', *buffer_pos, n_zeros); + if (forward) { + *buffer_pos += n_zeros; + } + } +} + + +/** + * InsertThousandsGrouping: + * @writer: Unicode writer. + * @n_buffer: Number of characters in @buffer. + * @digits: Digits we're reading from. If count is non-NULL, this is unused. + * @d_pos: Start of digits string. + * @n_digits: The number of digits in the string, in which we want + * to put the grouping chars. + * @min_width: The minimum width of the digits in the output string. + * Output will be zero-padded on the left to fill. + * @grouping: see definition in localeconv(). + * @thousands_sep: see definition in localeconv(). + * + * There are 2 modes: counting and filling. If @writer is NULL, + * we are in counting mode, else filling mode. + * If counting, the required buffer size is returned. + * If filling, we know the buffer will be large enough, so we don't + * need to pass in the buffer size. + * Inserts thousand grouping characters (as defined by grouping and + * thousands_sep) into @writer. + * + * Return value: -1 on error, number of characters otherwise. + **/ +static Py_ssize_t +_PyUnicode_InsertThousandsGrouping( + _PyUnicodeWriter *writer, + Py_ssize_t n_buffer, + PyObject *digits, + Py_ssize_t d_pos, + Py_ssize_t n_digits, + Py_ssize_t min_width, + const char *grouping, + PyObject *thousands_sep, + Py_UCS4 *maxchar, + int forward) +{ + min_width = Py_MAX(0, min_width); + if (writer) { + assert(digits != NULL); + assert(maxchar == NULL); + } + else { + assert(digits == NULL); + assert(maxchar != NULL); + } + assert(0 <= d_pos); + assert(0 <= n_digits); + assert(grouping != NULL); + + Py_ssize_t count = 0; + Py_ssize_t n_zeros; + int loop_broken = 0; + int use_separator = 0; /* First time through, don't append the + separator. They only go between + groups. */ + Py_ssize_t buffer_pos; + Py_ssize_t digits_pos; + Py_ssize_t len; + Py_ssize_t n_chars; + Py_ssize_t remaining = n_digits; /* Number of chars remaining to + be looked at */ + /* A generator that returns all of the grouping widths, until it + returns 0. */ + GroupGenerator groupgen; + GroupGenerator_init(&groupgen, grouping); + const Py_ssize_t thousands_sep_len = PyUnicode_GET_LENGTH(thousands_sep); + + /* if digits are not grouped, thousands separator + should be an empty string */ + assert(!(grouping[0] == CHAR_MAX && thousands_sep_len != 0)); + + digits_pos = d_pos + (forward ? 0 : n_digits); + if (writer) { + buffer_pos = writer->pos + (forward ? 0 : n_buffer); + assert(buffer_pos <= PyUnicode_GET_LENGTH(writer->buffer)); + assert(digits_pos <= PyUnicode_GET_LENGTH(digits)); + } + else { + buffer_pos = forward ? 0 : n_buffer; + } + + if (!writer) { + *maxchar = 127; + } + + while ((len = GroupGenerator_next(&groupgen)) > 0) { + len = Py_MIN(len, Py_MAX(Py_MAX(remaining, min_width), 1)); + n_zeros = Py_MAX(0, len - remaining); + n_chars = Py_MAX(0, Py_MIN(remaining, len)); + + /* Use n_zero zero's and n_chars chars */ + + /* Count only, don't do anything. */ + count += (use_separator ? thousands_sep_len : 0) + n_zeros + n_chars; + + /* Copy into the writer. */ + InsertThousandsGrouping_fill(writer, &buffer_pos, + digits, &digits_pos, + n_chars, n_zeros, + use_separator ? thousands_sep : NULL, + thousands_sep_len, maxchar, forward); + + /* Use a separator next time. */ + use_separator = 1; + + remaining -= n_chars; + min_width -= len; + + if (remaining <= 0 && min_width <= 0) { + loop_broken = 1; + break; + } + min_width -= thousands_sep_len; + } + if (!loop_broken) { + /* We left the loop without using a break statement. */ + + len = Py_MAX(Py_MAX(remaining, min_width), 1); + n_zeros = Py_MAX(0, len - remaining); + n_chars = Py_MAX(0, Py_MIN(remaining, len)); + + /* Use n_zero zero's and n_chars chars */ + count += (use_separator ? thousands_sep_len : 0) + n_zeros + n_chars; + + /* Copy into the writer. */ + InsertThousandsGrouping_fill(writer, &buffer_pos, + digits, &digits_pos, + n_chars, n_zeros, + use_separator ? thousands_sep : NULL, + thousands_sep_len, maxchar, forward); + } + return count; +} + + /* Raises an exception about an unknown presentation type for this * type. */ diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 11ba147a744..c71f9d3f71d 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -104,9 +104,7 @@ NOTE: In the interpreter's initialization phase, some globals are currently */ -// Maximum code point of Unicode 6.0: 0x10ffff (1,114,111). -// The value must be the same in fileutils.c. -#define MAX_UNICODE 0x10ffff +#define MAX_UNICODE _Py_MAX_UNICODE #ifdef Py_DEBUG # define _PyUnicode_CHECK(op) _PyUnicode_CheckConsistency(op, 0) @@ -420,39 +418,6 @@ static void clear_global_interned_strings(void) return unicode_get_empty(); \ } while (0) -static inline void -unicode_fill(int kind, void *data, Py_UCS4 value, - Py_ssize_t start, Py_ssize_t length) -{ - assert(0 <= start); - switch (kind) { - case PyUnicode_1BYTE_KIND: { - assert(value <= 0xff); - Py_UCS1 ch = (unsigned char)value; - Py_UCS1 *to = (Py_UCS1 *)data + start; - memset(to, ch, length); - break; - } - case PyUnicode_2BYTE_KIND: { - assert(value <= 0xffff); - Py_UCS2 ch = (Py_UCS2)value; - Py_UCS2 *to = (Py_UCS2 *)data + start; - const Py_UCS2 *end = to + length; - for (; to < end; ++to) *to = ch; - break; - } - case PyUnicode_4BYTE_KIND: { - assert(value <= MAX_UNICODE); - Py_UCS4 ch = value; - Py_UCS4 * to = (Py_UCS4 *)data + start; - const Py_UCS4 *end = to + length; - for (; to < end; ++to) *to = ch; - break; - } - default: Py_UNREACHABLE(); - } -} - /* Fast detection of the most frequent whitespace characters */ const unsigned char _Py_ascii_whitespace[] = { @@ -9735,142 +9700,6 @@ any_find_slice(PyObject* s1, PyObject* s2, return result; } -/* _PyUnicode_InsertThousandsGrouping() helper functions */ -#include "stringlib/localeutil.h" - -/** - * InsertThousandsGrouping: - * @writer: Unicode writer. - * @n_buffer: Number of characters in @buffer. - * @digits: Digits we're reading from. If count is non-NULL, this is unused. - * @d_pos: Start of digits string. - * @n_digits: The number of digits in the string, in which we want - * to put the grouping chars. - * @min_width: The minimum width of the digits in the output string. - * Output will be zero-padded on the left to fill. - * @grouping: see definition in localeconv(). - * @thousands_sep: see definition in localeconv(). - * - * There are 2 modes: counting and filling. If @writer is NULL, - * we are in counting mode, else filling mode. - * If counting, the required buffer size is returned. - * If filling, we know the buffer will be large enough, so we don't - * need to pass in the buffer size. - * Inserts thousand grouping characters (as defined by grouping and - * thousands_sep) into @writer. - * - * Return value: -1 on error, number of characters otherwise. - **/ -Py_ssize_t -_PyUnicode_InsertThousandsGrouping( - _PyUnicodeWriter *writer, - Py_ssize_t n_buffer, - PyObject *digits, - Py_ssize_t d_pos, - Py_ssize_t n_digits, - Py_ssize_t min_width, - const char *grouping, - PyObject *thousands_sep, - Py_UCS4 *maxchar, - int forward) -{ - min_width = Py_MAX(0, min_width); - if (writer) { - assert(digits != NULL); - assert(maxchar == NULL); - } - else { - assert(digits == NULL); - assert(maxchar != NULL); - } - assert(0 <= d_pos); - assert(0 <= n_digits); - assert(grouping != NULL); - - Py_ssize_t count = 0; - Py_ssize_t n_zeros; - int loop_broken = 0; - int use_separator = 0; /* First time through, don't append the - separator. They only go between - groups. */ - Py_ssize_t buffer_pos; - Py_ssize_t digits_pos; - Py_ssize_t len; - Py_ssize_t n_chars; - Py_ssize_t remaining = n_digits; /* Number of chars remaining to - be looked at */ - /* A generator that returns all of the grouping widths, until it - returns 0. */ - GroupGenerator groupgen; - GroupGenerator_init(&groupgen, grouping); - const Py_ssize_t thousands_sep_len = PyUnicode_GET_LENGTH(thousands_sep); - - /* if digits are not grouped, thousands separator - should be an empty string */ - assert(!(grouping[0] == CHAR_MAX && thousands_sep_len != 0)); - - digits_pos = d_pos + (forward ? 0 : n_digits); - if (writer) { - buffer_pos = writer->pos + (forward ? 0 : n_buffer); - assert(buffer_pos <= PyUnicode_GET_LENGTH(writer->buffer)); - assert(digits_pos <= PyUnicode_GET_LENGTH(digits)); - } - else { - buffer_pos = forward ? 0 : n_buffer; - } - - if (!writer) { - *maxchar = 127; - } - - while ((len = GroupGenerator_next(&groupgen)) > 0) { - len = Py_MIN(len, Py_MAX(Py_MAX(remaining, min_width), 1)); - n_zeros = Py_MAX(0, len - remaining); - n_chars = Py_MAX(0, Py_MIN(remaining, len)); - - /* Use n_zero zero's and n_chars chars */ - - /* Count only, don't do anything. */ - count += (use_separator ? thousands_sep_len : 0) + n_zeros + n_chars; - - /* Copy into the writer. */ - InsertThousandsGrouping_fill(writer, &buffer_pos, - digits, &digits_pos, - n_chars, n_zeros, - use_separator ? thousands_sep : NULL, - thousands_sep_len, maxchar, forward); - - /* Use a separator next time. */ - use_separator = 1; - - remaining -= n_chars; - min_width -= len; - - if (remaining <= 0 && min_width <= 0) { - loop_broken = 1; - break; - } - min_width -= thousands_sep_len; - } - if (!loop_broken) { - /* We left the loop without using a break statement. */ - - len = Py_MAX(Py_MAX(remaining, min_width), 1); - n_zeros = Py_MAX(0, len - remaining); - n_chars = Py_MAX(0, Py_MIN(remaining, len)); - - /* Use n_zero zero's and n_chars chars */ - count += (use_separator ? thousands_sep_len : 0) + n_zeros + n_chars; - - /* Copy into the writer. */ - InsertThousandsGrouping_fill(writer, &buffer_pos, - digits, &digits_pos, - n_chars, n_zeros, - use_separator ? thousands_sep : NULL, - thousands_sep_len, maxchar, forward); - } - return count; -} Py_ssize_t PyUnicode_Count(PyObject *str, @@ -10427,7 +10256,7 @@ _PyUnicode_FastFill(PyObject *unicode, Py_ssize_t start, Py_ssize_t length, assert(fill_char <= PyUnicode_MAX_CHAR_VALUE(unicode)); assert(start >= 0); assert(start + length <= PyUnicode_GET_LENGTH(unicode)); - unicode_fill(kind, data, fill_char, start, length); + _PyUnicode_Fill(kind, data, fill_char, start, length); } Py_ssize_t @@ -10496,9 +10325,10 @@ pad(PyObject *self, kind = PyUnicode_KIND(u); data = PyUnicode_DATA(u); if (left) - unicode_fill(kind, data, fill, 0, left); + _PyUnicode_Fill(kind, data, fill, 0, left); if (right) - unicode_fill(kind, data, fill, left + _PyUnicode_LENGTH(self), right); + _PyUnicode_Fill(kind, data, fill, + left + _PyUnicode_LENGTH(self), right); _PyUnicode_FastCopyCharacters(u, left, self, 0, _PyUnicode_LENGTH(self)); assert(_PyUnicode_CheckConsistency(u, 1)); return u; @@ -11910,7 +11740,7 @@ unicode_expandtabs_impl(PyObject *self, int tabsize) if (tabsize > 0) { incr = tabsize - (line_pos % tabsize); line_pos += incr; - unicode_fill(kind, dest_data, ' ', j, incr); + _PyUnicode_Fill(kind, dest_data, ' ', j, incr); j += incr; } } @@ -15405,7 +15235,7 @@ unicode_format_arg_output(struct unicode_formatter_t *ctx, /* Pad left with the fill character if needed */ if (arg->width > len && !(arg->flags & F_LJUST)) { sublen = arg->width - len; - unicode_fill(writer->kind, writer->data, fill, writer->pos, sublen); + _PyUnicode_Fill(writer->kind, writer->data, fill, writer->pos, sublen); writer->pos += sublen; arg->width = len; } @@ -15437,7 +15267,7 @@ unicode_format_arg_output(struct unicode_formatter_t *ctx, /* Pad right with the fill character if needed */ if (arg->width > len) { sublen = arg->width - len; - unicode_fill(writer->kind, writer->data, ' ', writer->pos, sublen); + _PyUnicode_Fill(writer->kind, writer->data, ' ', writer->pos, sublen); writer->pos += sublen; } return 0; diff --git a/PCbuild/_freeze_module.vcxproj b/PCbuild/_freeze_module.vcxproj index 5ceddf759b8..c4a11fa9b24 100644 --- a/PCbuild/_freeze_module.vcxproj +++ b/PCbuild/_freeze_module.vcxproj @@ -165,6 +165,7 @@ + @@ -209,7 +210,6 @@ - diff --git a/PCbuild/_freeze_module.vcxproj.filters b/PCbuild/_freeze_module.vcxproj.filters index 332d466b1f7..7bbbec2c988 100644 --- a/PCbuild/_freeze_module.vcxproj.filters +++ b/PCbuild/_freeze_module.vcxproj.filters @@ -160,9 +160,6 @@ Source Files - - Source Files - Source Files @@ -487,6 +484,9 @@ Source Files + + Source Files + Source Files diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 71f508a7e8b..e2e1e415827 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -559,6 +559,7 @@ + @@ -605,7 +606,6 @@ - $(GeneratedFrozenModulesDir)Python;%(AdditionalIncludeDirectories) diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 547e9ee1abf..7e7ed9c2ae6 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -1274,6 +1274,9 @@ Objects + + Objects + Objects @@ -1382,9 +1385,6 @@ Python - - Python - Python diff --git a/Python/fileutils.c b/Python/fileutils.c index 2a3f12d4e87..b808229716f 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -2,6 +2,7 @@ #include "pycore_fileutils.h" // fileutils definitions #include "pycore_runtime.h" // _PyRuntime #include "pycore_pystate.h" // _Py_AssertHoldsTstate() +#include "pycore_unicodeobject.h" // _Py_MAX_UNICODE #include "osdefs.h" // SEP #include // mbstowcs() @@ -50,9 +51,6 @@ extern int winerror_to_errno(int); int _Py_open_cloexec_works = -1; #endif -// The value must be the same in unicodeobject.c. -#define MAX_UNICODE 0x10ffff - // mbstowcs() and mbrtowc() errors static const size_t DECODE_ERROR = ((size_t)-1); #ifdef HAVE_MBRTOWC @@ -123,7 +121,7 @@ is_valid_wide_char(wchar_t ch) { #ifdef HAVE_NON_UNICODE_WCHAR_T_REPRESENTATION /* Oracle Solaris doesn't use Unicode code points as wchar_t encoding - for non-Unicode locales, which makes values higher than MAX_UNICODE + for non-Unicode locales, which makes values higher than _Py_MAX_UNICODE possibly valid. */ return 1; #endif @@ -132,7 +130,7 @@ is_valid_wide_char(wchar_t ch) return 0; } #if SIZEOF_WCHAR_T > 2 - if (ch > MAX_UNICODE) { + if (ch > _Py_MAX_UNICODE) { // bpo-35883: Reject characters outside [U+0000; U+10ffff] range. // The glibc mbstowcs() UTF-8 decoder does not respect the RFC 3629, // it creates characters outside the [U+0000; U+10ffff] range: From 8b9606a2c577b0a0f7f425959a27a8554a9b474f Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Wed, 8 Oct 2025 06:28:29 -0700 Subject: [PATCH 066/373] gh-139452: Clarify redirect_stdout, stderr behavior (gh-139490) --- Doc/library/contextlib.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/contextlib.rst b/Doc/library/contextlib.rst index 176be4ff333..d0fa645093a 100644 --- a/Doc/library/contextlib.rst +++ b/Doc/library/contextlib.rst @@ -327,10 +327,10 @@ Functions and classes provided: .. function:: redirect_stdout(new_target) Context manager for temporarily redirecting :data:`sys.stdout` to - another file or file-like object. + another :term:`file object`. This tool adds flexibility to existing functions or classes whose output - is hardwired to stdout. + is hardwired to :data:`sys.stdout`. For example, the output of :func:`help` normally is sent to *sys.stdout*. You can capture that output in a string by redirecting the output to an @@ -366,8 +366,8 @@ Functions and classes provided: .. function:: redirect_stderr(new_target) - Similar to :func:`~contextlib.redirect_stdout` but redirecting - :data:`sys.stderr` to another file or file-like object. + Similar to :func:`~contextlib.redirect_stdout` but redirecting the global + :data:`sys.stderr` to another :term:`file object`. This context manager is :ref:`reentrant `. From 49fb46f555881c9f2d20ca87c8187c8718217c77 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Wed, 8 Oct 2025 19:49:54 +0530 Subject: [PATCH 067/373] gh-139774: use relaxed atomics for datetime hashes (#139775) --- Modules/_datetimemodule.c | 47 ++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 12d316985fc..46c4f57984b 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -15,6 +15,7 @@ #include "pycore_time.h" // _PyTime_ObjectToTime_t() #include "pycore_unicodeobject.h" // _PyUnicode_Copy() #include "pycore_initconfig.h" // _PyStatus_OK() +#include "pycore_pyatomic_ft_wrappers.h" #include "datetime.h" @@ -2540,14 +2541,16 @@ static Py_hash_t delta_hash(PyObject *op) { PyDateTime_Delta *self = PyDelta_CAST(op); - if (self->hashcode == -1) { + Py_hash_t hash = FT_ATOMIC_LOAD_SSIZE_RELAXED(self->hashcode); + if (hash == -1) { PyObject *temp = delta_getstate(self); if (temp != NULL) { - self->hashcode = PyObject_Hash(temp); + hash = PyObject_Hash(temp); + FT_ATOMIC_STORE_SSIZE_RELAXED(self->hashcode, hash); Py_DECREF(temp); } } - return self->hashcode; + return hash; } static PyObject * @@ -3921,12 +3924,14 @@ static Py_hash_t date_hash(PyObject *op) { PyDateTime_Date *self = PyDate_CAST(op); - if (self->hashcode == -1) { - self->hashcode = generic_hash( + Py_hash_t hash = FT_ATOMIC_LOAD_SSIZE_RELAXED(self->hashcode); + if (hash == -1) { + hash = generic_hash( (unsigned char *)self->data, _PyDateTime_DATE_DATASIZE); + FT_ATOMIC_STORE_SSIZE_RELAXED(self->hashcode, hash); } - return self->hashcode; + return hash; } static PyObject * @@ -5043,7 +5048,8 @@ static Py_hash_t time_hash(PyObject *op) { PyDateTime_Time *self = PyTime_CAST(op); - if (self->hashcode == -1) { + Py_hash_t hash = FT_ATOMIC_LOAD_SSIZE_RELAXED(self->hashcode); + if (hash == -1) { PyObject *offset, *self0; if (TIME_GET_FOLD(self)) { self0 = new_time_ex2(TIME_GET_HOUR(self), @@ -5065,10 +5071,11 @@ time_hash(PyObject *op) return -1; /* Reduce this to a hash of another object. */ - if (offset == Py_None) - self->hashcode = generic_hash( + if (offset == Py_None) { + hash = generic_hash( (unsigned char *)self->data, _PyDateTime_TIME_DATASIZE); - else { + FT_ATOMIC_STORE_SSIZE_RELAXED(self->hashcode, hash); + } else { PyObject *temp1, *temp2; int seconds, microseconds; assert(HASTZINFO(self)); @@ -5087,12 +5094,13 @@ time_hash(PyObject *op) Py_DECREF(offset); return -1; } - self->hashcode = PyObject_Hash(temp2); + hash = PyObject_Hash(temp2); + FT_ATOMIC_STORE_SSIZE_RELAXED(self->hashcode, hash); Py_DECREF(temp2); } Py_DECREF(offset); } - return self->hashcode; + return hash; } /*[clinic input] @@ -6627,7 +6635,8 @@ static Py_hash_t datetime_hash(PyObject *op) { PyDateTime_DateTime *self = PyDateTime_CAST(op); - if (self->hashcode == -1) { + Py_hash_t hash = FT_ATOMIC_LOAD_SSIZE_RELAXED(self->hashcode); + if (hash == -1) { PyObject *offset, *self0; if (DATE_GET_FOLD(self)) { self0 = new_datetime_ex2(GET_YEAR(self), @@ -6652,10 +6661,11 @@ datetime_hash(PyObject *op) return -1; /* Reduce this to a hash of another object. */ - if (offset == Py_None) - self->hashcode = generic_hash( + if (offset == Py_None) { + hash = generic_hash( (unsigned char *)self->data, _PyDateTime_DATETIME_DATASIZE); - else { + FT_ATOMIC_STORE_SSIZE_RELAXED(self->hashcode, hash); + } else { PyObject *temp1, *temp2; int days, seconds; @@ -6679,12 +6689,13 @@ datetime_hash(PyObject *op) Py_DECREF(offset); return -1; } - self->hashcode = PyObject_Hash(temp2); + hash = PyObject_Hash(temp2); + FT_ATOMIC_STORE_SSIZE_RELAXED(self->hashcode, hash); Py_DECREF(temp2); } Py_DECREF(offset); } - return self->hashcode; + return hash; } /*[clinic input] From 59a6f9d8c52af40ccd31e5dca848f71808c24b06 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 8 Oct 2025 16:34:19 +0200 Subject: [PATCH 068/373] gh-135676: Add a summary of source characters (GH-138194) Co-authored-by: Carol Willing Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Co-authored-by: Blaise Pabon Co-authored-by: Micha Albert Co-authored-by: KeithTheEE --- Doc/reference/lexical_analysis.rst | 76 ++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst index 05ae410c168..0b0dba1a996 100644 --- a/Doc/reference/lexical_analysis.rst +++ b/Doc/reference/lexical_analysis.rst @@ -10,12 +10,76 @@ Lexical analysis A Python program is read by a *parser*. Input to the parser is a stream of :term:`tokens `, generated by the *lexical analyzer* (also known as the *tokenizer*). -This chapter describes how the lexical analyzer breaks a file into tokens. +This chapter describes how the lexical analyzer produces these tokens. -Python reads program text as Unicode code points; the encoding of a source file -can be given by an encoding declaration and defaults to UTF-8, see :pep:`3120` -for details. If the source file cannot be decoded, a :exc:`SyntaxError` is -raised. +The lexical analyzer determines the program text's :ref:`encoding ` +(UTF-8 by default), and decodes the text into +:ref:`source characters `. +If the text cannot be decoded, a :exc:`SyntaxError` is raised. + +Next, the lexical analyzer uses the source characters to generate a stream of tokens. +The type of a generated token generally depends on the next source character to +be processed. Similarly, other special behavior of the analyzer depends on +the first source character that hasn't yet been processed. +The following table gives a quick summary of these source characters, +with links to sections that contain more information. + +.. list-table:: + :header-rows: 1 + + * - Character + - Next token (or other relevant documentation) + + * - * space + * tab + * formfeed + - * :ref:`Whitespace ` + + * - * CR, LF + - * :ref:`New line ` + * :ref:`Indentation ` + + * - * backslash (``\``) + - * :ref:`Explicit line joining ` + * (Also significant in :ref:`string escape sequences `) + + * - * hash (``#``) + - * :ref:`Comment ` + + * - * quote (``'``, ``"``) + - * :ref:`String literal ` + + * - * ASCII letter (``a``-``z``, ``A``-``Z``) + * non-ASCII character + - * :ref:`Name ` + * Prefixed :ref:`string or bytes literal ` + + * - * underscore (``_``) + - * :ref:`Name ` + * (Can also be part of :ref:`numeric literals `) + + * - * number (``0``-``9``) + - * :ref:`Numeric literal ` + + * - * dot (``.``) + - * :ref:`Numeric literal ` + * :ref:`Operator ` + + * - * question mark (``?``) + * dollar (``$``) + * + .. (the following uses zero-width space characters to render + .. a literal backquote) + + backquote (``​`​``) + * control character + - * Error (outside string literals and comments) + + * - * other printing character + - * :ref:`Operator or delimiter ` + + * - * end of file + - * :ref:`End marker ` .. _line-structure: @@ -120,6 +184,8 @@ If an encoding is declared, the encoding name must be recognized by Python encoding is used for all lexical analysis, including string literals, comments and identifiers. +.. _lexical-source-character: + All lexical analysis, including string literals, comments and identifiers, works on Unicode text decoded using the source encoding. Any Unicode code point, except the NUL control character, can appear in From 570d17259f824302d20567a3a2f32c67ebdaefcd Mon Sep 17 00:00:00 2001 From: Wulian233 <1055917385@qq.com> Date: Wed, 8 Oct 2025 23:00:54 +0800 Subject: [PATCH 069/373] gh-139769: Update `PCBuild/find_python.bat` to allow discovery of Python 3.14 (GH-139770) Enable 3.14 py.exe can be use on PCBuild --- PCbuild/find_python.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PCbuild/find_python.bat b/PCbuild/find_python.bat index d65d080ca71..841d83968c6 100644 --- a/PCbuild/find_python.bat +++ b/PCbuild/find_python.bat @@ -47,7 +47,7 @@ @rem If py.exe finds a recent enough version, use that one @rem It is fine to add new versions to this list when they have released, @rem but we do not use prerelease builds here. -@for %%p in (3.13 3.12 3.11 3.10) do @py -%%p -EV >nul 2>&1 && (set PYTHON=py -%%p) && (set _Py_Python_Source=found %%p with py.exe) && goto :found +@for %%p in (3.14 3.13 3.12 3.11 3.10) do @py -%%p -EV >nul 2>&1 && (set PYTHON=py -%%p) && (set _Py_Python_Source=found %%p with py.exe) && goto :found @if NOT exist "%_Py_EXTERNALS_DIR%" mkdir "%_Py_EXTERNALS_DIR%" @set _Py_NUGET=%NUGET% From b04a57deef66ce08233be57d1ab5873388df2cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 8 Oct 2025 17:22:44 +0200 Subject: [PATCH 070/373] gh-139748: fix leaks in AC error paths when using unicode FS-based converters (#139765) --- Lib/test/test_clinic.py | 2 + Lib/test/test_compile.py | 15 +++++++ Lib/test/test_symtable.py | 7 ++++ ...-10-08-13-52-00.gh-issue-139748.jq0yFJ.rst | 2 + Modules/_ssl.c | 5 +-- Modules/clinic/_ssl.c.h | 7 +++- Modules/clinic/socketmodule.c.h | 7 +++- Modules/clinic/symtablemodule.c.h | 7 +++- Modules/posixmodule.c | 39 +++++++------------ Modules/socketmodule.c | 5 +-- Modules/symtablemodule.c | 6 +-- Python/bltinmodule.c | 5 +-- Python/clinic/bltinmodule.c.h | 7 +++- Tools/clinic/libclinic/converters.py | 20 ++++++++++ 14 files changed, 88 insertions(+), 46 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-08-13-52-00.gh-issue-139748.jq0yFJ.rst diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index d54dd546ea3..e0dbb062eb0 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -2980,6 +2980,8 @@ def test_cli_converters(self): "uint64", "uint8", "unicode", + "unicode_fs_decoded", + "unicode_fs_encoded", "unsigned_char", "unsigned_int", "unsigned_long", diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 277a2a18754..1660dabe681 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -651,6 +651,21 @@ def test_compile_filename(self): compile('pass', filename, 'exec') self.assertRaises(TypeError, compile, 'pass', list(b'file.py'), 'exec') + def test_compile_filename_refleak(self): + # Regression tests for reference leak in PyUnicode_FSDecoder. + # See https://github.com/python/cpython/issues/139748. + mortal_str = 'this is a mortal string' + # check error path when 'mode' AC conversion failed + self.assertRaises(TypeError, compile, b'', mortal_str, mode=1234) + # check error path when 'optimize' AC conversion failed + self.assertRaises(OverflowError, compile, b'', mortal_str, + 'exec', optimize=1 << 1000) + # check error path when 'dont_inherit' AC conversion failed + class EvilBool: + def __bool__(self): raise ValueError + self.assertRaises(ValueError, compile, b'', mortal_str, + 'exec', dont_inherit=EvilBool()) + @support.cpython_only def test_same_filename_used(self): s = """def f(): pass\ndef g(): pass""" diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py index 26c75fcbc2b..943e63fc13c 100644 --- a/Lib/test/test_symtable.py +++ b/Lib/test/test_symtable.py @@ -579,6 +579,13 @@ def test_nested_genexpr(self): self.assertEqual(sorted(st.get_identifiers()), [".0", "y"]) self.assertEqual(st.get_children(), []) + def test__symtable_refleak(self): + # Regression test for reference leak in PyUnicode_FSDecoder. + # See https://github.com/python/cpython/issues/139748. + mortal_str = 'this is a mortal string' + # check error path when 'compile_type' AC conversion failed + self.assertRaises(TypeError, symtable.symtable, '', mortal_str, 1) + class ComprehensionTests(unittest.TestCase): def get_identifiers_recursive(self, st, res): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-08-13-52-00.gh-issue-139748.jq0yFJ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-08-13-52-00.gh-issue-139748.jq0yFJ.rst new file mode 100644 index 00000000000..3b190f3f6df --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-08-13-52-00.gh-issue-139748.jq0yFJ.rst @@ -0,0 +1,2 @@ +Fix reference leaks in error branches of functions accepting path strings or +bytes such as :func:`compile` and :func:`os.system`. Patch by Bénédikt Tran. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index e1bdc4033ba..1fa44ef1de4 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -1858,14 +1858,14 @@ _certificate_to_der(_sslmodulestate *state, X509 *certificate) /*[clinic input] _ssl._test_decode_cert - path: object(converter="PyUnicode_FSConverter") + path: unicode_fs_encoded / [clinic start generated code]*/ static PyObject * _ssl__test_decode_cert_impl(PyObject *module, PyObject *path) -/*[clinic end generated code: output=96becb9abb23c091 input=cdeaaf02d4346628]*/ +/*[clinic end generated code: output=96becb9abb23c091 input=cb4988d5e651a4f8]*/ { PyObject *retval = NULL; X509 *x=NULL; @@ -1895,7 +1895,6 @@ _ssl__test_decode_cert_impl(PyObject *module, PyObject *path) X509_free(x); fail0: - Py_DECREF(path); if (cert != NULL) BIO_free(cert); return retval; } diff --git a/Modules/clinic/_ssl.c.h b/Modules/clinic/_ssl.c.h index 0e8e4a7c8a7..d1fb024903e 100644 --- a/Modules/clinic/_ssl.c.h +++ b/Modules/clinic/_ssl.c.h @@ -48,7 +48,7 @@ static PyObject * _ssl__test_decode_cert(PyObject *module, PyObject *arg) { PyObject *return_value = NULL; - PyObject *path; + PyObject *path = NULL; if (!PyUnicode_FSConverter(arg, &path)) { goto exit; @@ -56,6 +56,9 @@ _ssl__test_decode_cert(PyObject *module, PyObject *arg) return_value = _ssl__test_decode_cert_impl(module, path); exit: + /* Cleanup for path */ + Py_XDECREF(path); + return return_value; } @@ -3322,4 +3325,4 @@ exit: #ifndef _SSL_ENUM_CRLS_METHODDEF #define _SSL_ENUM_CRLS_METHODDEF #endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */ -/*[clinic end generated code: output=5a630a1e83927d47 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3b6c9cbfc4660ecb input=a9049054013a1b77]*/ diff --git a/Modules/clinic/socketmodule.c.h b/Modules/clinic/socketmodule.c.h index 0cedab597db..e0cc1c50dcb 100644 --- a/Modules/clinic/socketmodule.c.h +++ b/Modules/clinic/socketmodule.c.h @@ -479,7 +479,7 @@ static PyObject * _socket_if_nametoindex(PyObject *module, PyObject *arg) { PyObject *return_value = NULL; - PyObject *oname; + PyObject *oname = NULL; if (!PyUnicode_FSConverter(arg, &oname)) { goto exit; @@ -487,6 +487,9 @@ _socket_if_nametoindex(PyObject *module, PyObject *arg) return_value = _socket_if_nametoindex_impl(module, oname); exit: + /* Cleanup for oname */ + Py_XDECREF(oname); + return return_value; } @@ -538,4 +541,4 @@ exit: #ifndef _SOCKET_IF_INDEXTONAME_METHODDEF #define _SOCKET_IF_INDEXTONAME_METHODDEF #endif /* !defined(_SOCKET_IF_INDEXTONAME_METHODDEF) */ -/*[clinic end generated code: output=0376c46b76ae2bce input=a9049054013a1b77]*/ +/*[clinic end generated code: output=36051ebf6ad1e6f8 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/symtablemodule.c.h b/Modules/clinic/symtablemodule.c.h index 2ecd3afc00d..bd55d77c540 100644 --- a/Modules/clinic/symtablemodule.c.h +++ b/Modules/clinic/symtablemodule.c.h @@ -22,7 +22,7 @@ _symtable_symtable(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; PyObject *source; - PyObject *filename; + PyObject *filename = NULL; const char *startstr; if (!_PyArg_CheckPositional("symtable", nargs, 3, 3)) { @@ -48,6 +48,9 @@ _symtable_symtable(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return_value = _symtable_symtable_impl(module, source, filename, startstr); exit: + /* Cleanup for filename */ + Py_XDECREF(filename); + return return_value; } -/*[clinic end generated code: output=931964a76a72f850 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=7a8545d9a1efe837 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index f50167c223e..4189d300856 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -3137,17 +3137,6 @@ class dev_t_return_converter(unsigned_long_return_converter): conversion_fn = '_PyLong_FromDev' unsigned_cast = '(dev_t)' -class FSConverter_converter(CConverter): - type = 'PyObject *' - converter = 'PyUnicode_FSConverter' - def converter_init(self): - if self.default is not unspecified: - fail("FSConverter_converter does not support default values") - self.c_default = 'NULL' - - def cleanup(self): - return "Py_XDECREF(" + self.name + ");\n" - class pid_t_converter(CConverter): type = 'pid_t' format_unit = '" _Py_PARSE_PID "' @@ -3211,7 +3200,7 @@ class confname_converter(CConverter): """, argname=argname, converter=self.converter, table=self.table) [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=8189d5ae78244626]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=d2759f2332cd39b3]*/ /*[clinic input] @@ -6135,14 +6124,14 @@ os_system_impl(PyObject *module, const wchar_t *command) /*[clinic input] os.system -> long - command: FSConverter + command: unicode_fs_encoded Execute the command in a subshell. [clinic start generated code]*/ static long os_system_impl(PyObject *module, PyObject *command) -/*[clinic end generated code: output=290fc437dd4f33a0 input=86a58554ba6094af]*/ +/*[clinic end generated code: output=290fc437dd4f33a0 input=47c6f24b6dc92881]*/ { long result; const char *bytes = PyBytes_AsString(command); @@ -9328,7 +9317,7 @@ os_getgroups_impl(PyObject *module) /*[clinic input] os.initgroups - username as oname: FSConverter + username as oname: unicode_fs_encoded gid: int / @@ -9341,12 +9330,12 @@ group id. static PyObject * os_initgroups_impl(PyObject *module, PyObject *oname, int gid) -/*[clinic end generated code: output=7f074d30a425fd3a input=df3d54331b0af204]*/ +/*[clinic end generated code: output=7f074d30a425fd3a input=984e60c7fed88cb4]*/ #else /*[clinic input] os.initgroups - username as oname: FSConverter + username as oname: unicode_fs_encoded gid: gid_t / @@ -9359,7 +9348,7 @@ group id. static PyObject * os_initgroups_impl(PyObject *module, PyObject *oname, gid_t gid) -/*[clinic end generated code: output=59341244521a9e3f input=0cb91bdc59a4c564]*/ +/*[clinic end generated code: output=59341244521a9e3f input=17d8fbe2dea42ca4]*/ #endif { const char *username = PyBytes_AS_STRING(oname); @@ -13115,8 +13104,8 @@ os_putenv_impl(PyObject *module, PyObject *name, PyObject *value) /*[clinic input] os.putenv - name: FSConverter - value: FSConverter + name: unicode_fs_encoded + value: unicode_fs_encoded / Change or add an environment variable. @@ -13124,7 +13113,7 @@ Change or add an environment variable. static PyObject * os_putenv_impl(PyObject *module, PyObject *name, PyObject *value) -/*[clinic end generated code: output=d29a567d6b2327d2 input=a97bc6152f688d31]*/ +/*[clinic end generated code: output=d29a567d6b2327d2 input=84fcd30f873c8c45]*/ { const char *name_string = PyBytes_AS_STRING(name); const char *value_string = PyBytes_AS_STRING(value); @@ -13167,7 +13156,7 @@ os_unsetenv_impl(PyObject *module, PyObject *name) #else /*[clinic input] os.unsetenv - name: FSConverter + name: unicode_fs_encoded / Delete an environment variable. @@ -13175,7 +13164,7 @@ Delete an environment variable. static PyObject * os_unsetenv_impl(PyObject *module, PyObject *name) -/*[clinic end generated code: output=54c4137ab1834f02 input=2bb5288a599c7107]*/ +/*[clinic end generated code: output=54c4137ab1834f02 input=78ff12e505ade80a]*/ { if (PySys_Audit("os.unsetenv", "(O)", name) < 0) { return NULL; @@ -15229,14 +15218,14 @@ os_urandom_impl(PyObject *module, Py_ssize_t size) /*[clinic input] os.memfd_create - name: FSConverter + name: unicode_fs_encoded flags: unsigned_int(bitwise=True, c_default="MFD_CLOEXEC") = MFD_CLOEXEC [clinic start generated code]*/ static PyObject * os_memfd_create_impl(PyObject *module, PyObject *name, unsigned int flags) -/*[clinic end generated code: output=6681ede983bdb9a6 input=a42cfc199bcd56e9]*/ +/*[clinic end generated code: output=6681ede983bdb9a6 input=cd0eb092cfac474b]*/ { int fd; const char *bytes = PyBytes_AS_STRING(name); diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index fa153efd8b7..dd4b6892977 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -7278,7 +7278,7 @@ Returns a list of network interface information (index, name) tuples."); /*[clinic input] _socket.if_nametoindex - oname: object(converter="PyUnicode_FSConverter") + oname: unicode_fs_encoded / Returns the interface index corresponding to the interface name if_name. @@ -7286,7 +7286,7 @@ Returns the interface index corresponding to the interface name if_name. static PyObject * _socket_if_nametoindex_impl(PyObject *module, PyObject *oname) -/*[clinic end generated code: output=289a411614f30244 input=01e0f1205307fb77]*/ +/*[clinic end generated code: output=289a411614f30244 input=6125dc20683560cf]*/ { #ifdef MS_WINDOWS NET_IFINDEX index; @@ -7295,7 +7295,6 @@ _socket_if_nametoindex_impl(PyObject *module, PyObject *oname) #endif index = if_nametoindex(PyBytes_AS_STRING(oname)); - Py_DECREF(oname); if (index == 0) { /* if_nametoindex() doesn't set errno */ PyErr_SetString(PyExc_OSError, "no interface with this name"); diff --git a/Modules/symtablemodule.c b/Modules/symtablemodule.c index d0d5223e5ac..d353f406831 100644 --- a/Modules/symtablemodule.c +++ b/Modules/symtablemodule.c @@ -13,7 +13,7 @@ module _symtable _symtable.symtable source: object - filename: object(converter='PyUnicode_FSDecoder') + filename: unicode_fs_decoded startstr: str / @@ -23,7 +23,7 @@ Return symbol and scope dictionaries used internally by compiler. static PyObject * _symtable_symtable_impl(PyObject *module, PyObject *source, PyObject *filename, const char *startstr) -/*[clinic end generated code: output=59eb0d5fc7285ac4 input=9dd8a50c0c36a4d7]*/ +/*[clinic end generated code: output=59eb0d5fc7285ac4 input=436ffff90d02e4f6]*/ { struct symtable *st; PyObject *t; @@ -47,12 +47,10 @@ _symtable_symtable_impl(PyObject *module, PyObject *source, else { PyErr_SetString(PyExc_ValueError, "symtable() arg 3 must be 'exec' or 'eval' or 'single'"); - Py_DECREF(filename); Py_XDECREF(source_copy); return NULL; } st = _Py_SymtableStringObjectFlags(str, filename, start, &cf); - Py_DECREF(filename); Py_XDECREF(source_copy); if (st == NULL) { return NULL; diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index b7e08ae54da..2551c2c961b 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -745,7 +745,7 @@ builtin_chr(PyObject *module, PyObject *i) compile as builtin_compile source: object - filename: object(converter="PyUnicode_FSDecoder") + filename: unicode_fs_decoded mode: str flags: int = 0 dont_inherit: bool = False @@ -771,7 +771,7 @@ static PyObject * builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename, const char *mode, int flags, int dont_inherit, int optimize, int feature_version) -/*[clinic end generated code: output=b0c09c84f116d3d7 input=cc78e20e7c7682ba]*/ +/*[clinic end generated code: output=b0c09c84f116d3d7 input=8f0069edbdac381b]*/ { PyObject *source_copy; const char *str; @@ -889,7 +889,6 @@ builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename, error: result = NULL; finally: - Py_DECREF(filename); return result; } diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index 60e20a46af5..adb82f45c25 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -296,7 +296,7 @@ builtin_compile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj PyObject *argsbuf[7]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 3; PyObject *source; - PyObject *filename; + PyObject *filename = NULL; const char *mode; int flags = 0; int dont_inherit = 0; @@ -367,6 +367,9 @@ skip_optional_kwonly: return_value = builtin_compile_impl(module, source, filename, mode, flags, dont_inherit, optimize, feature_version); exit: + /* Cleanup for filename */ + Py_XDECREF(filename); + return return_value; } @@ -1274,4 +1277,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=c0b72519622c849e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=7eada753dc2e046f input=a9049054013a1b77]*/ diff --git a/Tools/clinic/libclinic/converters.py b/Tools/clinic/libclinic/converters.py index 3154299e31b..201125b9165 100644 --- a/Tools/clinic/libclinic/converters.py +++ b/Tools/clinic/libclinic/converters.py @@ -916,6 +916,26 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st return super().parse_arg(argname, displayname, limited_capi=limited_capi) +class _unicode_fs_converter_base(CConverter): + type = 'PyObject *' + + def converter_init(self) -> None: + if self.default is not unspecified: + fail(f"{self.__class__.__name__} does not support default values") + self.c_default = 'NULL' + + def cleanup(self) -> str: + return f"Py_XDECREF({self.parser_name});" + + +class unicode_fs_encoded_converter(_unicode_fs_converter_base): + converter = 'PyUnicode_FSConverter' + + +class unicode_fs_decoded_converter(_unicode_fs_converter_base): + converter = 'PyUnicode_FSDecoder' + + @add_legacy_c_converter('u') @add_legacy_c_converter('u#', zeroes=True) @add_legacy_c_converter('Z', accept={str, NoneType}) From 99fd52563220f7dd09303fa7a2b232d8618da6ce Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Wed, 8 Oct 2025 16:26:22 +0100 Subject: [PATCH 071/373] gh-101100: Fix all Sphinx warnings in `Doc/library/subprocess.rst` (#139576) --- Doc/library/subprocess.rst | 4 ++-- Doc/tools/.nitignore | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index 028a7861f36..1aade881745 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -649,7 +649,7 @@ functions. If specified, *env* must provide any variables required for the program to execute. On Windows, in order to run a `side-by-side assembly`_ the - specified *env* **must** include a valid :envvar:`SystemRoot`. + specified *env* **must** include a valid ``%SystemRoot%``. .. _side-by-side assembly: https://en.wikipedia.org/wiki/Side-by-Side_Assembly @@ -1473,7 +1473,7 @@ handling consistency are valid for these functions. Return ``(exitcode, output)`` of executing *cmd* in a shell. - Execute the string *cmd* in a shell with :meth:`Popen.check_output` and + Execute the string *cmd* in a shell with :func:`check_output` and return a 2-tuple ``(exitcode, output)``. *encoding* and *errors* are used to decode output; see the notes on :ref:`frequently-used-arguments` for more details. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 0ee92dce437..4ee09c6bbaa 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -35,7 +35,6 @@ Doc/library/smtplib.rst Doc/library/socket.rst Doc/library/ssl.rst Doc/library/stdtypes.rst -Doc/library/subprocess.rst Doc/library/termios.rst Doc/library/test.rst Doc/library/tkinter.rst From 72e370c910bd941806a36b01f2f57f4c53a78eed Mon Sep 17 00:00:00 2001 From: ed Date: Wed, 8 Oct 2025 15:36:53 +0000 Subject: [PATCH 072/373] gh-139736: Fix argparse indentation overshoot (#139738) Co-authored-by: Savannah Ostrowski --- Lib/argparse.py | 2 +- Lib/test/test_argparse.py | 12 ++++++------ .../2025-10-08-00-06-30.gh-issue-139736.baPeBd.rst | 2 ++ 3 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-08-00-06-30.gh-issue-139736.baPeBd.rst diff --git a/Lib/argparse.py b/Lib/argparse.py index 863e951528b..d71e551401c 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -280,7 +280,7 @@ def add_argument(self, action): if action.help is not SUPPRESS: # find all invocations - get_invocation = self._format_action_invocation + get_invocation = lambda x: self._decolor(self._format_action_invocation(x)) invocation_lengths = [len(get_invocation(action)) + self._current_indent] for subaction in self._iter_indented_subactions(action): invocation_lengths.append(len(get_invocation(subaction)) + self._current_indent) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 90bfcd0ae3e..27e38040a98 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -7311,11 +7311,11 @@ def custom_formatter(prog): {heading}usage: {reset}{prog}PROG{reset} [{short}-h{reset}] [{short}+f {label}FOO{reset}] {pos}spam{reset} {heading}positional arguments:{reset} - {pos_b}spam{reset} spam help + {pos_b}spam{reset} spam help {heading}options:{reset} - {short_b}-h{reset}, {long_b}--help{reset} show this help message and exit - {short_b}+f{reset}, {long_b}++foo{reset} {label_b}FOO{reset} foo help + {short_b}-h{reset}, {long_b}--help{reset} show this help message and exit + {short_b}+f{reset}, {long_b}++foo{reset} {label_b}FOO{reset} foo help ''')) def test_custom_formatter_class(self): @@ -7348,11 +7348,11 @@ def __init__(self, prog): {heading}usage: {reset}{prog}PROG{reset} [{short}-h{reset}] [{short}+f {label}FOO{reset}] {pos}spam{reset} {heading}positional arguments:{reset} - {pos_b}spam{reset} spam help + {pos_b}spam{reset} spam help {heading}options:{reset} - {short_b}-h{reset}, {long_b}--help{reset} show this help message and exit - {short_b}+f{reset}, {long_b}++foo{reset} {label_b}FOO{reset} foo help + {short_b}-h{reset}, {long_b}--help{reset} show this help message and exit + {short_b}+f{reset}, {long_b}++foo{reset} {label_b}FOO{reset} foo help ''')) diff --git a/Misc/NEWS.d/next/Library/2025-10-08-00-06-30.gh-issue-139736.baPeBd.rst b/Misc/NEWS.d/next/Library/2025-10-08-00-06-30.gh-issue-139736.baPeBd.rst new file mode 100644 index 00000000000..820679632d1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-08-00-06-30.gh-issue-139736.baPeBd.rst @@ -0,0 +1,2 @@ +Fix excessive indentation in the default :mod:`argparse` +:class:`!HelpFormatter`. Patch by Alexander Edland. From d9cb191df804b73e90aa09a7e9ac2a8cdd9b90be Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Wed, 8 Oct 2025 17:14:09 +0100 Subject: [PATCH 073/373] gh-138843: Clean up downloads page (#138844) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Doc/tools/templates/download.html | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Doc/tools/templates/download.html b/Doc/tools/templates/download.html index 26fc9571d17..523b505f596 100644 --- a/Doc/tools/templates/download.html +++ b/Doc/tools/templates/download.html @@ -42,22 +42,22 @@

{% trans %}Download Python {{ dl_version }} documentation{% endtrans %}

{% trans %}HTML{% endtrans %} - {% trans download_size="13" %}Download (ca. {{ download_size }} MiB){% endtrans %} - {% trans download_size="8" %}Download (ca. {{ download_size }} MiB){% endtrans %} + {% trans %}Download{% endtrans %} + {% trans %}Download{% endtrans %} {% trans %}Plain text{% endtrans %} - {% trans download_size="4" %}Download (ca. {{ download_size }} MiB){% endtrans %} - {% trans download_size="3" %}Download (ca. {{ download_size }} MiB){% endtrans %} + {% trans %}Download{% endtrans %} + {% trans %}Download{% endtrans %} {% trans %}Texinfo{% endtrans %} - {% trans download_size="9" %}Download (ca. {{ download_size }} MiB){% endtrans %} - {% trans download_size="7" %}Download (ca. {{ download_size }} MiB){% endtrans %} + {% trans %}Download{% endtrans %} + {% trans %}Download{% endtrans %} {% trans %}EPUB{% endtrans %} - {% trans download_size="6" %}Download (ca. {{ download_size }} MiB){% endtrans %} + {% trans %}Download{% endtrans %} @@ -71,6 +71,9 @@

{% trans %}Download Python {{ dl_version }} documentation{% endtrans %}

and run make dist-pdf in the Doc/ directory of a copy of the CPython repository. {% endtrans %}

+

{% trans %} +See the directory listing +for file sizes.{% endtrans %}

{% trans %}Unpacking{% endtrans %}

@@ -83,9 +86,8 @@

{% trans %}Unpacking{% endtrans %}

{% trans %}Windows users can use the ZIP archives since those are customary on that platform. These are created on Unix using the Info-ZIP zip program.{% endtrans %}

-

{% trans %}Problems{% endtrans %}

- -

{% trans %}If you have comments or suggestions for the Python documentation, please send -email to docs@python.org.{% endtrans %}

+{% set bugs = pathto('bugs') %} +

{% trans bugs = bugs %}Open an issue +if you have comments or suggestions for the Python documentation.{% endtrans %}

{% endblock %} From fa603542452ead916eda3702c1175d34486d080d Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Wed, 8 Oct 2025 11:23:27 -0700 Subject: [PATCH 074/373] gh-101100: Resolve some `os` sphinx reference warnings (#139636) --- Doc/library/os.rst | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 8c81e1dcd07..ba3d1894549 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -216,8 +216,8 @@ process and user. You can delete items in this mapping to unset environment variables. :func:`unsetenv` will be called automatically when an item is deleted from - :data:`os.environ`, and when one of the :meth:`pop` or :meth:`clear` methods is - called. + :data:`os.environ`, and when one of the :meth:`~dict.pop` or + :meth:`~dict.clear` methods is called. .. seealso:: @@ -430,8 +430,8 @@ process and user. associated with the effective user id of the process; the group access list may change over the lifetime of the process, it is not affected by calls to :func:`setgroups`, and its length is not limited to 16. The - deployment target value, :const:`MACOSX_DEPLOYMENT_TARGET`, can be - obtained with :func:`sysconfig.get_config_var`. + deployment target value can be obtained with + :func:`sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') `. .. function:: getlogin() @@ -2606,10 +2606,10 @@ features: Create a filesystem node (file, device special file or named pipe) named *path*. *mode* specifies both the permissions to use and the type of node - to be created, being combined (bitwise OR) with one of ``stat.S_IFREG``, - ``stat.S_IFCHR``, ``stat.S_IFBLK``, and ``stat.S_IFIFO`` (those constants are - available in :mod:`stat`). For ``stat.S_IFCHR`` and ``stat.S_IFBLK``, - *device* defines the newly created device special file (probably using + to be created, being combined (bitwise OR) with one of :const:`stat.S_IFREG`, + :const:`stat.S_IFCHR`, :const:`stat.S_IFBLK`, and :const:`stat.S_IFIFO`. + For :const:`stat.S_IFCHR` and :const:`stat.S_IFBLK`, *device* defines the + newly created device special file (probably using :func:`os.makedev`), otherwise it is ignored. This function can also support :ref:`paths relative to directory descriptors @@ -2627,13 +2627,13 @@ features: .. function:: major(device, /) Extract the device major number from a raw device number (usually the - :attr:`st_dev` or :attr:`st_rdev` field from :c:struct:`stat`). + :attr:`~stat_result.st_dev` or :attr:`~stat_result.st_rdev` field from :c:struct:`stat`). .. function:: minor(device, /) Extract the device minor number from a raw device number (usually the - :attr:`st_dev` or :attr:`st_rdev` field from :c:struct:`stat`). + :attr:`~stat_result.st_dev` or :attr:`~stat_result.st_rdev` field from :c:struct:`stat`). .. function:: makedev(major, minor, /) @@ -3364,8 +3364,8 @@ features: .. versionchanged:: 3.8 On Windows, the :attr:`st_mode` member now identifies special - files as :const:`S_IFCHR`, :const:`S_IFIFO` or :const:`S_IFBLK` - as appropriate. + files as :const:`~stat.S_IFCHR`, :const:`~stat.S_IFIFO` or + :const:`~stat.S_IFBLK` as appropriate. .. versionchanged:: 3.12 On Windows, :attr:`st_ctime` is deprecated. Eventually, it will @@ -4285,10 +4285,10 @@ to be ignored. .. function:: abort() - Generate a :const:`SIGABRT` signal to the current process. On Unix, the default + Generate a :const:`~signal.SIGABRT` signal to the current process. On Unix, the default behavior is to produce a core dump; on Windows, the process immediately returns an exit code of ``3``. Be aware that calling this function will not call the - Python signal handler registered for :const:`SIGABRT` with + Python signal handler registered for :const:`~signal.SIGABRT` with :func:`signal.signal`. From 6954077fde4aee07b55a666a610ef4bc4b9d76a8 Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Wed, 8 Oct 2025 15:14:05 -0400 Subject: [PATCH 075/373] Remove Cirrus macOS runners from CI (#139799) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- .github/workflows/build.yml | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 252ec831ed9..625466151e3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -202,23 +202,15 @@ jobs: strategy: fail-fast: false matrix: - # Cirrus and macos-14 are M1, macos-15-intel is default GHA Intel. - # macOS 13 only runs tests against the GIL-enabled CPython. - # Cirrus used for upstream, macos-14 for forks. + # macos-14 is M1, macos-15-intel is Intel. + # macos-15-intel only runs tests against the GIL-enabled CPython. os: - - ghcr.io/cirruslabs/macos-runner:sonoma - macos-14 - macos-15-intel - is-fork: # only used for the exclusion trick - - ${{ github.repository_owner != 'python' }} free-threading: - false - true exclude: - - os: ghcr.io/cirruslabs/macos-runner:sonoma - is-fork: true - - os: macos-14 - is-fork: false - os: macos-15-intel free-threading: true uses: ./.github/workflows/reusable-macos.yml @@ -409,9 +401,8 @@ jobs: fail-fast: false matrix: include: - # Use the same runs-on configuration as build-macos and build-ubuntu. - arch: aarch64 - runs-on: ${{ github.repository_owner == 'python' && 'ghcr.io/cirruslabs/macos-runner:sonoma' || 'macos-14' }} + runs-on: macos-14 - arch: x86_64 runs-on: ubuntu-24.04 From a2850a3a91a1515cbdd9440463c78bdf2e0bd3cf Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 8 Oct 2025 22:33:45 +0200 Subject: [PATCH 076/373] gh-70030: Remove _PyCode_ConstantKey() function (#139735) Move the function to the internal C API and no longer export it. --- Include/cpython/code.h | 9 --------- Include/internal/pycore_code.h | 9 +++++++++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Include/cpython/code.h b/Include/cpython/code.h index 3f0dce03455..84456a709a6 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -282,15 +282,6 @@ typedef struct _line_offsets { */ PyAPI_FUNC(int) _PyCode_CheckLineNumber(int lasti, PyCodeAddressRange *bounds); -/* Create a comparable key used to compare constants taking in account the - * object type. It is used to make sure types are not coerced (e.g., float and - * complex) _and_ to distinguish 0.0 from -0.0 e.g. on IEEE platforms - * - * Return (type(obj), obj, ...): a tuple with variable size (at least 2 items) - * depending on the type and the value. The type is the first item to not - * compare bytes and str which can raise a BytesWarning exception. */ -PyAPI_FUNC(PyObject*) _PyCode_ConstantKey(PyObject *obj); - PyAPI_FUNC(PyObject*) PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names, PyObject *lnotab); diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 0ec47f0014b..2d7d81d491c 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -663,6 +663,15 @@ PyAPI_FUNC(int) _PyCode_VerifyStateless( PyAPI_FUNC(int) _PyCode_CheckPureFunction(PyCodeObject *, const char **); PyAPI_FUNC(int) _PyCode_ReturnsOnlyNone(PyCodeObject *); +/* Create a comparable key used to compare constants taking in account the + * object type. It is used to make sure types are not coerced (e.g., float and + * complex) _and_ to distinguish 0.0 from -0.0 e.g. on IEEE platforms + * + * Return (type(obj), obj, ...): a tuple with variable size (at least 2 items) + * depending on the type and the value. The type is the first item to not + * compare bytes and str which can raise a BytesWarning exception. */ +extern PyObject* _PyCode_ConstantKey(PyObject *obj); + #ifdef __cplusplus } From 678e0b818c0d6063907f55263bbc0e194b492c8e Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 9 Oct 2025 01:13:27 +0300 Subject: [PATCH 077/373] gh-139590: Stricter `ruff` rules for `Tools/wasm` (#139752) --- .pre-commit-config.yaml | 4 ++++ Tools/wasm/.ruff.toml | 3 --- Tools/wasm/emscripten/__main__.py | 9 ++++----- Tools/wasm/emscripten/wasm_assets.py | 5 ++--- Tools/wasm/wasi/__main__.py | 10 ++++------ 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0e00ffb3bd2..b0311f05279 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,6 +26,10 @@ repos: name: Run Ruff (lint) on Tools/peg_generator/ args: [--exit-non-zero-on-fix, --config=Tools/peg_generator/.ruff.toml] files: ^Tools/peg_generator/ + - id: ruff-check + name: Run Ruff (lint) on Tools/wasm/ + args: [--exit-non-zero-on-fix, --config=Tools/wasm/.ruff.toml] + files: ^Tools/wasm/ - id: ruff-format name: Run Ruff (format) on Doc/ args: [--check] diff --git a/Tools/wasm/.ruff.toml b/Tools/wasm/.ruff.toml index aabcf8dc4f5..3d8e59fa3f2 100644 --- a/Tools/wasm/.ruff.toml +++ b/Tools/wasm/.ruff.toml @@ -22,7 +22,4 @@ select = [ ] ignore = [ "E501", # Line too long - "F541", # f-string without any placeholders - "PYI024", # Use `typing.NamedTuple` instead of `collections.namedtuple` - "PYI025", # Use `from collections.abc import Set as AbstractSet` ] diff --git a/Tools/wasm/emscripten/__main__.py b/Tools/wasm/emscripten/__main__.py index fdf3142c0a3..c88e9edba6d 100644 --- a/Tools/wasm/emscripten/__main__.py +++ b/Tools/wasm/emscripten/__main__.py @@ -3,16 +3,16 @@ import argparse import contextlib import functools +import hashlib import os import shutil import subprocess import sys import sysconfig -import hashlib import tempfile -from urllib.request import urlopen from pathlib import Path from textwrap import dedent +from urllib.request import urlopen try: from os import process_cpu_count as cpu_count @@ -33,9 +33,7 @@ PREFIX_DIR = CROSS_BUILD_DIR / HOST_TRIPLE / "prefix" LOCAL_SETUP = CHECKOUT / "Modules" / "Setup.local" -LOCAL_SETUP_MARKER = "# Generated by Tools/wasm/emscripten.py\n".encode( - "utf-8" -) +LOCAL_SETUP_MARKER = b"# Generated by Tools/wasm/emscripten.py\n" def updated_env(updates={}): @@ -432,6 +430,7 @@ def main(): make_build, configure_host, make_host, + clean, ): subcommand.add_argument( "--quiet", diff --git a/Tools/wasm/emscripten/wasm_assets.py b/Tools/wasm/emscripten/wasm_assets.py index 90f318f319a..38479087235 100755 --- a/Tools/wasm/emscripten/wasm_assets.py +++ b/Tools/wasm/emscripten/wasm_assets.py @@ -15,7 +15,6 @@ import sys import sysconfig import zipfile -from typing import Dict # source directory SRCDIR = pathlib.Path(__file__).parents[3].absolute() @@ -134,7 +133,7 @@ def filterfunc(filename: str) -> bool: pzf.writepy(entry, filterfunc=filterfunc) -def detect_extension_modules(args: argparse.Namespace) -> Dict[str, bool]: +def detect_extension_modules(args: argparse.Namespace) -> dict[str, bool]: modules = {} # disabled by Modules/Setup.local ? @@ -149,7 +148,7 @@ def detect_extension_modules(args: argparse.Namespace) -> Dict[str, bool]: # disabled by configure? with open(args.sysconfig_data) as f: data = f.read() - loc: Dict[str, Dict[str, str]] = {} + loc: dict[str, dict[str, str]] = {} exec(data, globals(), loc) for key, value in loc["build_time_vars"].items(): diff --git a/Tools/wasm/wasi/__main__.py b/Tools/wasm/wasi/__main__.py index a0658cb351a..b2f643ddbfc 100644 --- a/Tools/wasm/wasi/__main__.py +++ b/Tools/wasm/wasi/__main__.py @@ -16,7 +16,6 @@ import sysconfig import tempfile - CHECKOUT = pathlib.Path(__file__).parent.parent.parent.parent assert (CHECKOUT / "configure").is_file(), ( "Please update the location of the file" @@ -28,9 +27,9 @@ LOCAL_SETUP = CHECKOUT / "Modules" / "Setup.local" LOCAL_SETUP_MARKER = ( - "# Generated by Tools/wasm/wasi .\n" - "# Required to statically build extension modules." -).encode("utf-8") + b"# Generated by Tools/wasm/wasi .\n" + b"# Required to statically build extension modules." +) WASI_SDK_VERSION = 24 @@ -154,8 +153,7 @@ def build_python_is_pydebug(): test = "import sys, test.support; sys.exit(test.support.Py_DEBUG)" result = subprocess.run( [build_python_path(), "-c", test], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + capture_output=True, ) return bool(result.returncode) From e7e3d1d4a8dece01b1bbd0253684d5b46b2409d7 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Wed, 8 Oct 2025 23:34:40 +0100 Subject: [PATCH 078/373] gh-139805: Bump `test_repl_eio` timeout for slow builtbots (#139807) --- Lib/test/test_pyrepl/test_unix_console.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_pyrepl/test_unix_console.py b/Lib/test/test_pyrepl/test_unix_console.py index 6e422806cec..9ac2e9961df 100644 --- a/Lib/test/test_pyrepl/test_unix_console.py +++ b/Lib/test/test_pyrepl/test_unix_console.py @@ -361,7 +361,7 @@ def test_repl_eio(self): os.kill(proc.pid, signal.SIGUSR1) # sleep for pty to settle - _, err = proc.communicate(timeout=support.SHORT_TIMEOUT) + _, err = proc.communicate(timeout=support.LONG_TIMEOUT) self.assertEqual( proc.returncode, 1, From 7f155f9c46e2dd7dec5e12ebec3323dfd95fe1f0 Mon Sep 17 00:00:00 2001 From: Alper Date: Wed, 8 Oct 2025 23:30:47 -0700 Subject: [PATCH 079/373] gh-116738: make `mmap` module thread-safe (#139237) --- Lib/test/test_free_threading/test_mmap.py | 315 +++++++ Lib/test/test_mmap.py | 8 + ...-09-21-14-33-17.gh-issue-116738.vNaI4h.rst | 2 + Modules/clinic/mmapmodule.c.h | 799 ++++++++++++++++++ Modules/mmapmodule.c | 651 +++++++++----- 5 files changed, 1568 insertions(+), 207 deletions(-) create mode 100644 Lib/test/test_free_threading/test_mmap.py create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-21-14-33-17.gh-issue-116738.vNaI4h.rst create mode 100644 Modules/clinic/mmapmodule.c.h diff --git a/Lib/test/test_free_threading/test_mmap.py b/Lib/test/test_free_threading/test_mmap.py new file mode 100644 index 00000000000..ece13d33f3b --- /dev/null +++ b/Lib/test/test_free_threading/test_mmap.py @@ -0,0 +1,315 @@ +import unittest + +from test.support import import_helper, threading_helper +from test.support.threading_helper import run_concurrently + +import os +import string +import tempfile +import threading + +from collections import Counter + +mmap = import_helper.import_module("mmap") + +NTHREADS = 10 +ANONYMOUS_MEM = -1 + + +@threading_helper.requires_working_threading() +class MmapTests(unittest.TestCase): + def test_read_and_read_byte(self): + ascii_uppercase = string.ascii_uppercase.encode() + # Choose a total mmap size that evenly divides across threads and the + # read pattern (3 bytes per loop). + mmap_size = 3 * NTHREADS * len(ascii_uppercase) + num_bytes_to_read_per_thread = mmap_size // NTHREADS + bytes_read_from_mmap = [] + + def read(mm_obj): + nread = 0 + while nread < num_bytes_to_read_per_thread: + b = mm_obj.read_byte() + bytes_read_from_mmap.append(b) + b = mm_obj.read(2) + bytes_read_from_mmap.extend(b) + nread += 3 + + with mmap.mmap(ANONYMOUS_MEM, mmap_size) as mm_obj: + for i in range(mmap_size // len(ascii_uppercase)): + mm_obj.write(ascii_uppercase) + + mm_obj.seek(0) + run_concurrently( + worker_func=read, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + self.assertEqual(len(bytes_read_from_mmap), mmap_size) + # Count each letter/byte to verify read correctness + counter = Counter(bytes_read_from_mmap) + self.assertEqual(len(counter), len(ascii_uppercase)) + # Each letter/byte should be read (3 * NTHREADS) times + for letter in ascii_uppercase: + self.assertEqual(counter[letter], 3 * NTHREADS) + + def test_readline(self): + num_lines = 1000 + lines_read_from_mmap = [] + expected_lines = [] + + def readline(mm_obj): + for i in range(num_lines // NTHREADS): + line = mm_obj.readline() + lines_read_from_mmap.append(line) + + # Allocate mmap enough for num_lines (max line 5 bytes including NL) + with mmap.mmap(ANONYMOUS_MEM, num_lines * 5) as mm_obj: + for i in range(num_lines): + line = b"%d\n" % i + mm_obj.write(line) + expected_lines.append(line) + + mm_obj.seek(0) + run_concurrently( + worker_func=readline, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + self.assertEqual(len(lines_read_from_mmap), num_lines) + # Every line should be read once by threads; order is non-deterministic + # Sort numerically by integer value + lines_read_from_mmap.sort(key=lambda x: int(x)) + self.assertEqual(lines_read_from_mmap, expected_lines) + + def test_write_and_write_byte(self): + thread_letters = list(string.ascii_uppercase) + self.assertLessEqual(NTHREADS, len(thread_letters)) + per_thread_write_loop = 100 + + def write(mm_obj): + # Each thread picks a unique letter to write + thread_letter = thread_letters.pop(0) + thread_bytes = (thread_letter * 2).encode() + for _ in range(per_thread_write_loop): + mm_obj.write_byte(thread_bytes[0]) + mm_obj.write(thread_bytes) + + with mmap.mmap( + ANONYMOUS_MEM, per_thread_write_loop * 3 * NTHREADS + ) as mm_obj: + run_concurrently( + worker_func=write, + args=(mm_obj,), + nthreads=NTHREADS, + ) + mm_obj.seek(0) + data = mm_obj.read() + self.assertEqual(len(data), NTHREADS * per_thread_write_loop * 3) + counter = Counter(data) + self.assertEqual(len(counter), NTHREADS) + # Each thread letter should be written `per_thread_write_loop` * 3 + for letter in counter: + self.assertEqual(counter[letter], per_thread_write_loop * 3) + + def test_move(self): + ascii_uppercase = string.ascii_uppercase.encode() + num_letters = len(ascii_uppercase) + + def move(mm_obj): + for i in range(num_letters): + # Move 1 byte from the first half to the second half + mm_obj.move(0 + i, num_letters + i, 1) + + with mmap.mmap(ANONYMOUS_MEM, 2 * num_letters) as mm_obj: + mm_obj.write(ascii_uppercase) + run_concurrently( + worker_func=move, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + def test_seek_and_tell(self): + seek_per_thread = 10 + + def seek(mm_obj): + self.assertTrue(mm_obj.seekable()) + for _ in range(seek_per_thread): + before_seek = mm_obj.tell() + mm_obj.seek(1, os.SEEK_CUR) + self.assertLess(before_seek, mm_obj.tell()) + + with mmap.mmap(ANONYMOUS_MEM, 1024) as mm_obj: + run_concurrently( + worker_func=seek, + args=(mm_obj,), + nthreads=NTHREADS, + ) + # Each thread seeks from current position, the end position should + # be the sum of all seeks from all threads. + self.assertEqual(mm_obj.tell(), NTHREADS * seek_per_thread) + + def test_slice_update_and_slice_read(self): + thread_letters = list(string.ascii_uppercase) + self.assertLessEqual(NTHREADS, len(thread_letters)) + + def slice_update_and_slice_read(mm_obj): + # Each thread picks a unique letter to write + thread_letter = thread_letters.pop(0) + thread_bytes = (thread_letter * 1024).encode() + for _ in range(100): + mm_obj[:] = thread_bytes + read_bytes = mm_obj[:] + # Read bytes should be all the same letter, showing no + # interleaving + self.assertTrue(all_same(read_bytes)) + + with mmap.mmap(ANONYMOUS_MEM, 1024) as mm_obj: + run_concurrently( + worker_func=slice_update_and_slice_read, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + def test_item_update_and_item_read(self): + thread_indexes = [i for i in range(NTHREADS)] + + def item_update_and_item_read(mm_obj): + # Each thread picks a unique index to write + thread_index = thread_indexes.pop() + for i in range(100): + mm_obj[thread_index] = i + self.assertEqual(mm_obj[thread_index], i) + + # Read values set by other threads, all values + # should be less than '100' + for val in mm_obj: + self.assertLess(int.from_bytes(val), 100) + + with mmap.mmap(ANONYMOUS_MEM, NTHREADS + 1) as mm_obj: + run_concurrently( + worker_func=item_update_and_item_read, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + @unittest.skipUnless(os.name == "posix", "requires Posix") + @unittest.skipUnless(hasattr(mmap.mmap, "resize"), "requires mmap.resize") + def test_resize_and_size(self): + thread_indexes = [i for i in range(NTHREADS)] + + def resize_and_item_update(mm_obj): + # Each thread picks a unique index to write + thread_index = thread_indexes.pop() + mm_obj.resize(2048) + self.assertEqual(mm_obj.size(), 2048) + for i in range(100): + mm_obj[thread_index] = i + self.assertEqual(mm_obj[thread_index], i) + + with mmap.mmap(ANONYMOUS_MEM, 1024, flags=mmap.MAP_PRIVATE) as mm_obj: + run_concurrently( + worker_func=resize_and_item_update, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + def test_close_and_closed(self): + def close_mmap(mm_obj): + mm_obj.close() + self.assertTrue(mm_obj.closed) + + with mmap.mmap(ANONYMOUS_MEM, 1) as mm_obj: + run_concurrently( + worker_func=close_mmap, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + def test_find_and_rfind(self): + per_thread_loop = 10 + + def find_and_rfind(mm_obj): + pattern = b'Thread-Ident:"%d"' % threading.get_ident() + mm_obj.write(pattern) + for _ in range(per_thread_loop): + found_at = mm_obj.find(pattern, 0) + self.assertNotEqual(found_at, -1) + # Should not find it after the `found_at` + self.assertEqual(mm_obj.find(pattern, found_at + 1), -1) + found_at_rev = mm_obj.rfind(pattern, 0) + self.assertEqual(found_at, found_at_rev) + # Should not find it after the `found_at` + self.assertEqual(mm_obj.rfind(pattern, found_at + 1), -1) + + with mmap.mmap(ANONYMOUS_MEM, 1024) as mm_obj: + run_concurrently( + worker_func=find_and_rfind, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + @unittest.skipUnless(os.name == "posix", "requires Posix") + @unittest.skipUnless(hasattr(mmap.mmap, "resize"), "requires mmap.resize") + def test_flush(self): + mmap_filename = "test_mmap_file" + resize_to = 1024 + + def resize_and_flush(mm_obj): + mm_obj.resize(resize_to) + mm_obj.flush() + + with tempfile.TemporaryDirectory() as tmpdirname: + file_path = f"{tmpdirname}/{mmap_filename}" + with open(file_path, "wb+") as file: + file.write(b"CPython") + file.flush() + with mmap.mmap(file.fileno(), 1) as mm_obj: + run_concurrently( + worker_func=resize_and_flush, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + self.assertEqual(os.path.getsize(file_path), resize_to) + + def test_mmap_export_as_memoryview(self): + """ + Each thread creates a memoryview and updates the internal state of the + mmap object. + """ + buffer_size = 42 + + def create_memoryview_from_mmap(mm_obj): + memoryviews = [] + for _ in range(100): + mv = memoryview(mm_obj) + memoryviews.append(mv) + self.assertEqual(len(mv), buffer_size) + self.assertEqual(mv[:7], b"CPython") + + # Cannot close the mmap while it is exported as buffers + with self.assertRaisesRegex( + BufferError, "cannot close exported pointers exist" + ): + mm_obj.close() + + with mmap.mmap(ANONYMOUS_MEM, 42) as mm_obj: + mm_obj.write(b"CPython") + run_concurrently( + worker_func=create_memoryview_from_mmap, + args=(mm_obj,), + nthreads=NTHREADS, + ) + # Implicit mm_obj.close() verifies all exports (memoryviews) are + # properly freed. + + +def all_same(lst): + return all(item == lst[0] for item in lst) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py index 0571eed23f7..368af0cf89c 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -871,6 +871,10 @@ def test_madvise(self): size = 2 * PAGESIZE m = mmap.mmap(-1, size) + class Number: + def __index__(self): + return 2 + with self.assertRaisesRegex(ValueError, "madvise start out of bounds"): m.madvise(mmap.MADV_NORMAL, size) with self.assertRaisesRegex(ValueError, "madvise start out of bounds"): @@ -879,10 +883,14 @@ def test_madvise(self): m.madvise(mmap.MADV_NORMAL, 0, -1) with self.assertRaisesRegex(OverflowError, "madvise length too large"): m.madvise(mmap.MADV_NORMAL, PAGESIZE, sys.maxsize) + with self.assertRaisesRegex( + TypeError, "'str' object cannot be interpreted as an integer"): + m.madvise(mmap.MADV_NORMAL, PAGESIZE, "Not a Number") self.assertEqual(m.madvise(mmap.MADV_NORMAL), None) self.assertEqual(m.madvise(mmap.MADV_NORMAL, PAGESIZE), None) self.assertEqual(m.madvise(mmap.MADV_NORMAL, PAGESIZE, size), None) self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, 2), None) + self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, Number()), None) self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, size), None) @unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize') diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-09-21-14-33-17.gh-issue-116738.vNaI4h.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-21-14-33-17.gh-issue-116738.vNaI4h.rst new file mode 100644 index 00000000000..0668d57604b --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-21-14-33-17.gh-issue-116738.vNaI4h.rst @@ -0,0 +1,2 @@ +Make :mod:`mmap` thread-safe on the :term:`free threaded ` +build. diff --git a/Modules/clinic/mmapmodule.c.h b/Modules/clinic/mmapmodule.c.h new file mode 100644 index 00000000000..f7fc172b3af --- /dev/null +++ b/Modules/clinic/mmapmodule.c.h @@ -0,0 +1,799 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() +#include "pycore_modsupport.h" // _PyArg_CheckPositional() + +PyDoc_STRVAR(mmap_mmap_close__doc__, +"close($self, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_CLOSE_METHODDEF \ + {"close", (PyCFunction)mmap_mmap_close, METH_NOARGS, mmap_mmap_close__doc__}, + +static PyObject * +mmap_mmap_close_impl(mmap_object *self); + +static PyObject * +mmap_mmap_close(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_close_impl((mmap_object *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_read_byte__doc__, +"read_byte($self, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_READ_BYTE_METHODDEF \ + {"read_byte", (PyCFunction)mmap_mmap_read_byte, METH_NOARGS, mmap_mmap_read_byte__doc__}, + +static PyObject * +mmap_mmap_read_byte_impl(mmap_object *self); + +static PyObject * +mmap_mmap_read_byte(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_read_byte_impl((mmap_object *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_readline__doc__, +"readline($self, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_READLINE_METHODDEF \ + {"readline", (PyCFunction)mmap_mmap_readline, METH_NOARGS, mmap_mmap_readline__doc__}, + +static PyObject * +mmap_mmap_readline_impl(mmap_object *self); + +static PyObject * +mmap_mmap_readline(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_readline_impl((mmap_object *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_read__doc__, +"read($self, n=None, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_READ_METHODDEF \ + {"read", _PyCFunction_CAST(mmap_mmap_read), METH_FASTCALL, mmap_mmap_read__doc__}, + +static PyObject * +mmap_mmap_read_impl(mmap_object *self, Py_ssize_t num_bytes); + +static PyObject * +mmap_mmap_read(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t num_bytes = PY_SSIZE_T_MAX; + + if (!_PyArg_CheckPositional("read", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + if (!_Py_convert_optional_to_ssize_t(args[0], &num_bytes)) { + goto exit; + } +skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_read_impl((mmap_object *)self, num_bytes); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_find__doc__, +"find($self, view, start=None, end=None, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_FIND_METHODDEF \ + {"find", _PyCFunction_CAST(mmap_mmap_find), METH_FASTCALL, mmap_mmap_find__doc__}, + +static PyObject * +mmap_mmap_find_impl(mmap_object *self, Py_buffer *view, PyObject *start, + PyObject *end); + +static PyObject * +mmap_mmap_find(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_buffer view = {NULL, NULL}; + PyObject *start = Py_None; + PyObject *end = Py_None; + + if (!_PyArg_CheckPositional("find", nargs, 1, 3)) { + goto exit; + } + if (PyObject_GetBuffer(args[0], &view, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + start = args[1]; + if (nargs < 3) { + goto skip_optional; + } + end = args[2]; +skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_find_impl((mmap_object *)self, &view, start, end); + Py_END_CRITICAL_SECTION(); + +exit: + /* Cleanup for view */ + if (view.obj) { + PyBuffer_Release(&view); + } + + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_rfind__doc__, +"rfind($self, view, start=None, end=None, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_RFIND_METHODDEF \ + {"rfind", _PyCFunction_CAST(mmap_mmap_rfind), METH_FASTCALL, mmap_mmap_rfind__doc__}, + +static PyObject * +mmap_mmap_rfind_impl(mmap_object *self, Py_buffer *view, PyObject *start, + PyObject *end); + +static PyObject * +mmap_mmap_rfind(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_buffer view = {NULL, NULL}; + PyObject *start = Py_None; + PyObject *end = Py_None; + + if (!_PyArg_CheckPositional("rfind", nargs, 1, 3)) { + goto exit; + } + if (PyObject_GetBuffer(args[0], &view, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + start = args[1]; + if (nargs < 3) { + goto skip_optional; + } + end = args[2]; +skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_rfind_impl((mmap_object *)self, &view, start, end); + Py_END_CRITICAL_SECTION(); + +exit: + /* Cleanup for view */ + if (view.obj) { + PyBuffer_Release(&view); + } + + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_write__doc__, +"write($self, bytes, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_WRITE_METHODDEF \ + {"write", (PyCFunction)mmap_mmap_write, METH_O, mmap_mmap_write__doc__}, + +static PyObject * +mmap_mmap_write_impl(mmap_object *self, Py_buffer *data); + +static PyObject * +mmap_mmap_write(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + Py_buffer data = {NULL, NULL}; + + if (PyObject_GetBuffer(arg, &data, PyBUF_SIMPLE) != 0) { + goto exit; + } + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_write_impl((mmap_object *)self, &data); + Py_END_CRITICAL_SECTION(); + +exit: + /* Cleanup for data */ + if (data.obj) { + PyBuffer_Release(&data); + } + + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_write_byte__doc__, +"write_byte($self, byte, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_WRITE_BYTE_METHODDEF \ + {"write_byte", (PyCFunction)mmap_mmap_write_byte, METH_O, mmap_mmap_write_byte__doc__}, + +static PyObject * +mmap_mmap_write_byte_impl(mmap_object *self, unsigned char value); + +static PyObject * +mmap_mmap_write_byte(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + unsigned char value; + + { + long ival = PyLong_AsLong(arg); + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + else if (ival < 0) { + PyErr_SetString(PyExc_OverflowError, + "unsigned byte integer is less than minimum"); + goto exit; + } + else if (ival > UCHAR_MAX) { + PyErr_SetString(PyExc_OverflowError, + "unsigned byte integer is greater than maximum"); + goto exit; + } + else { + value = (unsigned char) ival; + } + } + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_write_byte_impl((mmap_object *)self, value); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_size__doc__, +"size($self, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_SIZE_METHODDEF \ + {"size", (PyCFunction)mmap_mmap_size, METH_NOARGS, mmap_mmap_size__doc__}, + +static PyObject * +mmap_mmap_size_impl(mmap_object *self); + +static PyObject * +mmap_mmap_size(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_size_impl((mmap_object *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if (defined(MS_WINDOWS) || defined(HAVE_MREMAP)) + +PyDoc_STRVAR(mmap_mmap_resize__doc__, +"resize($self, newsize, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_RESIZE_METHODDEF \ + {"resize", (PyCFunction)mmap_mmap_resize, METH_O, mmap_mmap_resize__doc__}, + +static PyObject * +mmap_mmap_resize_impl(mmap_object *self, Py_ssize_t new_size); + +static PyObject * +mmap_mmap_resize(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + Py_ssize_t new_size; + + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(arg); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + new_size = ival; + } + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_resize_impl((mmap_object *)self, new_size); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +#endif /* (defined(MS_WINDOWS) || defined(HAVE_MREMAP)) */ + +PyDoc_STRVAR(mmap_mmap_tell__doc__, +"tell($self, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_TELL_METHODDEF \ + {"tell", (PyCFunction)mmap_mmap_tell, METH_NOARGS, mmap_mmap_tell__doc__}, + +static PyObject * +mmap_mmap_tell_impl(mmap_object *self); + +static PyObject * +mmap_mmap_tell(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_tell_impl((mmap_object *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_flush__doc__, +"flush($self, offset=0, size=-1, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_FLUSH_METHODDEF \ + {"flush", _PyCFunction_CAST(mmap_mmap_flush), METH_FASTCALL, mmap_mmap_flush__doc__}, + +static PyObject * +mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset, Py_ssize_t size); + +static PyObject * +mmap_mmap_flush(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t offset = 0; + Py_ssize_t size = -1; + + if (!_PyArg_CheckPositional("flush", nargs, 0, 2)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[0]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + offset = ival; + } + if (nargs < 2) { + goto skip_optional; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + size = ival; + } +skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_flush_impl((mmap_object *)self, offset, size); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_seek__doc__, +"seek($self, pos, whence=0, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_SEEK_METHODDEF \ + {"seek", _PyCFunction_CAST(mmap_mmap_seek), METH_FASTCALL, mmap_mmap_seek__doc__}, + +static PyObject * +mmap_mmap_seek_impl(mmap_object *self, Py_ssize_t dist, int how); + +static PyObject * +mmap_mmap_seek(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t dist; + int how = 0; + + if (!_PyArg_CheckPositional("seek", nargs, 1, 2)) { + goto exit; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[0]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + dist = ival; + } + if (nargs < 2) { + goto skip_optional; + } + how = PyLong_AsInt(args[1]); + if (how == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_seek_impl((mmap_object *)self, dist, how); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_seekable__doc__, +"seekable($self, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_SEEKABLE_METHODDEF \ + {"seekable", (PyCFunction)mmap_mmap_seekable, METH_NOARGS, mmap_mmap_seekable__doc__}, + +static PyObject * +mmap_mmap_seekable_impl(mmap_object *self); + +static PyObject * +mmap_mmap_seekable(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return mmap_mmap_seekable_impl((mmap_object *)self); +} + +PyDoc_STRVAR(mmap_mmap_move__doc__, +"move($self, dest, src, count, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_MOVE_METHODDEF \ + {"move", _PyCFunction_CAST(mmap_mmap_move), METH_FASTCALL, mmap_mmap_move__doc__}, + +static PyObject * +mmap_mmap_move_impl(mmap_object *self, Py_ssize_t dest, Py_ssize_t src, + Py_ssize_t cnt); + +static PyObject * +mmap_mmap_move(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t dest; + Py_ssize_t src; + Py_ssize_t cnt; + + if (!_PyArg_CheckPositional("move", nargs, 3, 3)) { + goto exit; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[0]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + dest = ival; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + src = ival; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[2]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + cnt = ival; + } + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_move_impl((mmap_object *)self, dest, src, cnt); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(mmap_mmap___enter____doc__, +"__enter__($self, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP___ENTER___METHODDEF \ + {"__enter__", (PyCFunction)mmap_mmap___enter__, METH_NOARGS, mmap_mmap___enter____doc__}, + +static PyObject * +mmap_mmap___enter___impl(mmap_object *self); + +static PyObject * +mmap_mmap___enter__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap___enter___impl((mmap_object *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(mmap_mmap___exit____doc__, +"__exit__($self, exc_type, exc_value, traceback, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP___EXIT___METHODDEF \ + {"__exit__", _PyCFunction_CAST(mmap_mmap___exit__), METH_FASTCALL, mmap_mmap___exit____doc__}, + +static PyObject * +mmap_mmap___exit___impl(mmap_object *self, PyObject *exc_type, + PyObject *exc_value, PyObject *traceback); + +static PyObject * +mmap_mmap___exit__(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *exc_type; + PyObject *exc_value; + PyObject *traceback; + + if (!_PyArg_CheckPositional("__exit__", nargs, 3, 3)) { + goto exit; + } + exc_type = args[0]; + exc_value = args[1]; + traceback = args[2]; + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap___exit___impl((mmap_object *)self, exc_type, exc_value, traceback); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(mmap_mmap___sizeof____doc__, +"__sizeof__($self, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP___SIZEOF___METHODDEF \ + {"__sizeof__", (PyCFunction)mmap_mmap___sizeof__, METH_NOARGS, mmap_mmap___sizeof____doc__}, + +static PyObject * +mmap_mmap___sizeof___impl(mmap_object *self); + +static PyObject * +mmap_mmap___sizeof__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap___sizeof___impl((mmap_object *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + +#if (defined(MS_WINDOWS) && defined(Py_DEBUG)) + +PyDoc_STRVAR(mmap_mmap__protect__doc__, +"_protect($self, flNewProtect, start, length, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP__PROTECT_METHODDEF \ + {"_protect", _PyCFunction_CAST(mmap_mmap__protect), METH_FASTCALL, mmap_mmap__protect__doc__}, + +static PyObject * +mmap_mmap__protect_impl(mmap_object *self, unsigned int flNewProtect, + Py_ssize_t start, Py_ssize_t length); + +static PyObject * +mmap_mmap__protect(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + unsigned int flNewProtect; + Py_ssize_t start; + Py_ssize_t length; + + if (!_PyArg_CheckPositional("_protect", nargs, 3, 3)) { + goto exit; + } + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[0], &flNewProtect, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + start = ival; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[2]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + length = ival; + } + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap__protect_impl((mmap_object *)self, flNewProtect, start, length); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +#endif /* (defined(MS_WINDOWS) && defined(Py_DEBUG)) */ + +#if defined(HAVE_MADVISE) + +PyDoc_STRVAR(mmap_mmap_madvise__doc__, +"madvise($self, option, start=0, length=None, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_MADVISE_METHODDEF \ + {"madvise", _PyCFunction_CAST(mmap_mmap_madvise), METH_FASTCALL, mmap_mmap_madvise__doc__}, + +static PyObject * +mmap_mmap_madvise_impl(mmap_object *self, int option, Py_ssize_t start, + PyObject *length_obj); + +static PyObject * +mmap_mmap_madvise(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int option; + Py_ssize_t start = 0; + PyObject *length_obj = Py_None; + + if (!_PyArg_CheckPositional("madvise", nargs, 1, 3)) { + goto exit; + } + option = PyLong_AsInt(args[0]); + if (option == -1 && PyErr_Occurred()) { + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + start = ival; + } + if (nargs < 3) { + goto skip_optional; + } + length_obj = args[2]; +skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_madvise_impl((mmap_object *)self, option, start, length_obj); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +#endif /* defined(HAVE_MADVISE) */ + +#ifndef MMAP_MMAP_RESIZE_METHODDEF + #define MMAP_MMAP_RESIZE_METHODDEF +#endif /* !defined(MMAP_MMAP_RESIZE_METHODDEF) */ + +#ifndef MMAP_MMAP___SIZEOF___METHODDEF + #define MMAP_MMAP___SIZEOF___METHODDEF +#endif /* !defined(MMAP_MMAP___SIZEOF___METHODDEF) */ + +#ifndef MMAP_MMAP__PROTECT_METHODDEF + #define MMAP_MMAP__PROTECT_METHODDEF +#endif /* !defined(MMAP_MMAP__PROTECT_METHODDEF) */ + +#ifndef MMAP_MMAP_MADVISE_METHODDEF + #define MMAP_MMAP_MADVISE_METHODDEF +#endif /* !defined(MMAP_MMAP_MADVISE_METHODDEF) */ +/*[clinic end generated code: output=381f6cf4986ac867 input=a9049054013a1b77]*/ diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 41d11716288..ac8521f8aa9 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -91,6 +91,12 @@ my_getpagesize(void) # define MAP_ANONYMOUS MAP_ANON #endif +/*[clinic input] +module mmap +class mmap.mmap "mmap_object *" "" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=82a9f8a529905b9b]*/ + typedef enum { ACCESS_DEFAULT, @@ -129,6 +135,26 @@ typedef struct { #define mmap_object_CAST(op) ((mmap_object *)(op)) +#include "clinic/mmapmodule.c.h" + + +/* Return a Py_ssize_t from the object arg. This conversion logic is similar + to what AC uses for `Py_ssize_t` arguments. + + Returns -1 on error. Use PyErr_Occurred() to disambiguate. +*/ +static Py_ssize_t +_As_Py_ssize_t(PyObject *arg) { + assert(arg != NULL); + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(arg); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + return ival; +} + static void mmap_object_dealloc(PyObject *op) { @@ -165,10 +191,16 @@ mmap_object_dealloc(PyObject *op) Py_DECREF(tp); } +/*[clinic input] +@critical_section +mmap.mmap.close + +[clinic start generated code]*/ + static PyObject * -mmap_close_method(PyObject *op, PyObject *Py_UNUSED(ignored)) +mmap_mmap_close_impl(mmap_object *self) +/*[clinic end generated code: output=a1ae0c727546f78d input=25020035f047eae1]*/ { - mmap_object *self = mmap_object_CAST(op); if (self->exports > 0) { PyErr_SetString(PyExc_BufferError, "cannot close "\ "exported pointers exist"); @@ -472,10 +504,16 @@ _safe_PyBytes_FromStringAndSize(char *start, size_t num_bytes) } } +/*[clinic input] +@critical_section +mmap.mmap.read_byte + +[clinic start generated code]*/ + static PyObject * -mmap_read_byte_method(PyObject *op, PyObject *Py_UNUSED(ignored)) +mmap_mmap_read_byte_impl(mmap_object *self) +/*[clinic end generated code: output=d931da1319f3869b input=5b8c6a904bdddda9]*/ { - mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); if (self->pos >= self->size) { PyErr_SetString(PyExc_ValueError, "read byte out of range"); @@ -489,12 +527,18 @@ mmap_read_byte_method(PyObject *op, PyObject *Py_UNUSED(ignored)) return PyLong_FromLong((unsigned char) dest); } +/*[clinic input] +@critical_section +mmap.mmap.readline + +[clinic start generated code]*/ + static PyObject * -mmap_read_line_method(PyObject *op, PyObject *Py_UNUSED(ignored)) +mmap_mmap_readline_impl(mmap_object *self) +/*[clinic end generated code: output=b9d2bf9999283311 input=2c4efd1d06e1cdd1]*/ { Py_ssize_t remaining; char *start, *eol; - mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); @@ -519,15 +563,21 @@ mmap_read_line_method(PyObject *op, PyObject *Py_UNUSED(ignored)) return result; } -static PyObject * -mmap_read_method(PyObject *op, PyObject *args) -{ - Py_ssize_t num_bytes = PY_SSIZE_T_MAX, remaining; - mmap_object *self = mmap_object_CAST(op); +/*[clinic input] +@critical_section +mmap.mmap.read + + n as num_bytes: object(converter='_Py_convert_optional_to_ssize_t', type='Py_ssize_t', c_default='PY_SSIZE_T_MAX') = None + / + +[clinic start generated code]*/ + +static PyObject * +mmap_mmap_read_impl(mmap_object *self, Py_ssize_t num_bytes) +/*[clinic end generated code: output=3b4d4f3704ed0969 input=8f97f361d435e357]*/ +{ + Py_ssize_t remaining; - CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "|O&:read", _Py_convert_optional_to_ssize_t, &num_bytes)) - return NULL; CHECK_VALID(NULL); /* silently 'adjust' out-of-range requests */ @@ -544,81 +594,105 @@ mmap_read_method(PyObject *op, PyObject *args) } static PyObject * -mmap_gfind(mmap_object *self, - PyObject *args, - int reverse) +mmap_gfind_lock_held(mmap_object *self, Py_buffer *view, PyObject *start_obj, + PyObject *end_obj, int reverse) { Py_ssize_t start = self->pos; Py_ssize_t end = self->size; - Py_buffer view; CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, reverse ? "y*|nn:rfind" : "y*|nn:find", - &view, &start, &end)) { - return NULL; - } - else { - if (start < 0) - start += self->size; - if (start < 0) - start = 0; - else if (start > self->size) - start = self->size; - - if (end < 0) - end += self->size; - if (end < 0) - end = 0; - else if (end > self->size) - end = self->size; - - Py_ssize_t index; - PyObject *result; - CHECK_VALID_OR_RELEASE(NULL, view); - if (end < start) { - result = PyLong_FromSsize_t(-1); + if (start_obj != Py_None) { + start = _As_Py_ssize_t(start_obj); + if (start == -1 && PyErr_Occurred()) { + return NULL; } - else if (reverse) { - assert(0 <= start && start <= end && end <= self->size); - if (_safe_PyBytes_ReverseFind(&index, self, - self->data + start, end - start, - view.buf, view.len, start) < 0) - { - result = NULL; - } - else { - result = PyLong_FromSsize_t(index); + + if (end_obj != Py_None) { + end = _As_Py_ssize_t(end_obj); + if (end == -1 && PyErr_Occurred()) { + return NULL; } } + } + + if (start < 0) + start += self->size; + if (start < 0) + start = 0; + else if (start > self->size) + start = self->size; + + if (end < 0) + end += self->size; + if (end < 0) + end = 0; + else if (end > self->size) + end = self->size; + + Py_ssize_t index; + PyObject *result; + CHECK_VALID(NULL); + if (end < start) { + result = PyLong_FromSsize_t(-1); + } + else if (reverse) { + assert(0 <= start && start <= end && end <= self->size); + if (_safe_PyBytes_ReverseFind(&index, self, + self->data + start, end - start, + view->buf, view->len, start) < 0) + { + result = NULL; + } else { - assert(0 <= start && start <= end && end <= self->size); - if (_safe_PyBytes_Find(&index, self, - self->data + start, end - start, - view.buf, view.len, start) < 0) - { - result = NULL; - } - else { - result = PyLong_FromSsize_t(index); - } + result = PyLong_FromSsize_t(index); } - PyBuffer_Release(&view); - return result; } + else { + assert(0 <= start && start <= end && end <= self->size); + if (_safe_PyBytes_Find(&index, self, + self->data + start, end - start, + view->buf, view->len, start) < 0) + { + result = NULL; + } + else { + result = PyLong_FromSsize_t(index); + } + } + return result; } -static PyObject * -mmap_find_method(PyObject *op, PyObject *args) -{ - mmap_object *self = mmap_object_CAST(op); - return mmap_gfind(self, args, 0); -} +/*[clinic input] +@critical_section +mmap.mmap.find + + view: Py_buffer + start: object = None + end: object = None + / + +[clinic start generated code]*/ static PyObject * -mmap_rfind_method(PyObject *op, PyObject *args) +mmap_mmap_find_impl(mmap_object *self, Py_buffer *view, PyObject *start, + PyObject *end) +/*[clinic end generated code: output=ef8878a322f00192 input=0135504494b52c2b]*/ { - mmap_object *self = mmap_object_CAST(op); - return mmap_gfind(self, args, 1); + return mmap_gfind_lock_held(self, view, start, end, 0); +} + +/*[clinic input] +@critical_section +mmap.mmap.rfind = mmap.mmap.find + +[clinic start generated code]*/ + +static PyObject * +mmap_mmap_rfind_impl(mmap_object *self, Py_buffer *view, PyObject *start, + PyObject *end) +/*[clinic end generated code: output=73b918940d67c2b8 input=8aecdd1f70c06c62]*/ +{ + return mmap_gfind_lock_held(self, view, start, end, 1); } static int @@ -654,50 +728,55 @@ is_resizeable(mmap_object *self) #endif /* MS_WINDOWS || HAVE_MREMAP */ +/*[clinic input] +@critical_section +mmap.mmap.write + + bytes as data: Py_buffer + / + +[clinic start generated code]*/ + static PyObject * -mmap_write_method(PyObject *op, PyObject *args) +mmap_mmap_write_impl(mmap_object *self, Py_buffer *data) +/*[clinic end generated code: output=9e97063efb6fb27b input=3f16fa79aa89d6f7]*/ { - Py_buffer data; - mmap_object *self = mmap_object_CAST(op); - CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "y*:write", &data)) - return NULL; - if (!is_writable(self)) { - PyBuffer_Release(&data); return NULL; } - if (self->pos > self->size || self->size - self->pos < data.len) { - PyBuffer_Release(&data); + if (self->pos > self->size || self->size - self->pos < data->len) { PyErr_SetString(PyExc_ValueError, "data out of range"); return NULL; } - CHECK_VALID_OR_RELEASE(NULL, data); + CHECK_VALID(NULL); PyObject *result; - if (safe_memcpy(self->data + self->pos, data.buf, data.len) < 0) { + if (safe_memcpy(self->data + self->pos, data->buf, data->len) < 0) { result = NULL; } else { - self->pos += data.len; - result = PyLong_FromSsize_t(data.len); + self->pos += data->len; + result = PyLong_FromSsize_t(data->len); } - PyBuffer_Release(&data); return result; } +/*[clinic input] +@critical_section +mmap.mmap.write_byte + + byte as value: unsigned_char + / + +[clinic start generated code]*/ + static PyObject * -mmap_write_byte_method(PyObject *op, PyObject *args) +mmap_mmap_write_byte_impl(mmap_object *self, unsigned char value) +/*[clinic end generated code: output=aa11adada9b17510 input=32740bfa174f0991]*/ { - char value; - mmap_object *self = mmap_object_CAST(op); - CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "b:write_byte", &value)) - return(NULL); - if (!is_writable(self)) return NULL; @@ -707,17 +786,23 @@ mmap_write_byte_method(PyObject *op, PyObject *args) return NULL; } - if (safe_byte_copy(self->data + self->pos, &value) < 0) { + if (safe_byte_copy(self->data + self->pos, (const char*)&value) < 0) { return NULL; } self->pos++; Py_RETURN_NONE; } +/*[clinic input] +@critical_section +mmap.mmap.size + +[clinic start generated code]*/ + static PyObject * -mmap_size_method(PyObject *op, PyObject *Py_UNUSED(ignored)) +mmap_mmap_size_impl(mmap_object *self) +/*[clinic end generated code: output=c177e65e83a648ff input=f69c072efd2e1595]*/ { - mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); #ifdef MS_WINDOWS @@ -771,14 +856,21 @@ mmap_size_method(PyObject *op, PyObject *Py_UNUSED(ignored)) */ #if defined(MS_WINDOWS) || defined(HAVE_MREMAP) +/*[clinic input] +@critical_section +mmap.mmap.resize + + newsize as new_size: Py_ssize_t + / + +[clinic start generated code]*/ + static PyObject * -mmap_resize_method(PyObject *op, PyObject *args) +mmap_mmap_resize_impl(mmap_object *self, Py_ssize_t new_size) +/*[clinic end generated code: output=6f262537ce9c2dcc input=b6b5dee52a41b79f]*/ { - Py_ssize_t new_size; - mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "n:resize", &new_size) || - !is_resizeable(self)) { + if (!is_resizeable(self)) { return NULL; } if (new_size < 0 || PY_SSIZE_T_MAX - new_size < self->offset) { @@ -921,24 +1013,35 @@ mmap_resize_method(PyObject *op, PyObject *args) } #endif /* MS_WINDOWS || HAVE_MREMAP */ +/*[clinic input] +@critical_section +mmap.mmap.tell + +[clinic start generated code]*/ + static PyObject * -mmap_tell_method(PyObject *op, PyObject *Py_UNUSED(ignored)) +mmap_mmap_tell_impl(mmap_object *self) +/*[clinic end generated code: output=6034958630e1b1d1 input=fd163acacf45c3a5]*/ { - mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); return PyLong_FromSize_t(self->pos); } +/*[clinic input] +@critical_section +mmap.mmap.flush + + offset: Py_ssize_t = 0 + size: Py_ssize_t = -1 + / + +[clinic start generated code]*/ + static PyObject * -mmap_flush_method(PyObject *op, PyObject *args) +mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset, Py_ssize_t size) +/*[clinic end generated code: output=956ced67466149cf input=c50b893bc69520ec]*/ { - Py_ssize_t offset = 0; - Py_ssize_t size = -1; - mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size)) { - return NULL; - } if (size == -1) { size = self->size - offset; } @@ -969,60 +1072,80 @@ mmap_flush_method(PyObject *op, PyObject *args) #endif } +/*[clinic input] +@critical_section +mmap.mmap.seek + + pos as dist: Py_ssize_t + whence as how: int = 0 + / + +[clinic start generated code]*/ + static PyObject * -mmap_seek_method(PyObject *op, PyObject *args) +mmap_mmap_seek_impl(mmap_object *self, Py_ssize_t dist, int how) +/*[clinic end generated code: output=00310494e8b8c592 input=e2fda5d081c3db22]*/ { - Py_ssize_t dist; - mmap_object *self = mmap_object_CAST(op); - int how=0; CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how)) - return NULL; - else { - Py_ssize_t where; - switch (how) { - case 0: /* relative to start */ - where = dist; - break; - case 1: /* relative to current position */ - if (PY_SSIZE_T_MAX - self->pos < dist) - goto onoutofrange; - where = self->pos + dist; - break; - case 2: /* relative to end */ - if (PY_SSIZE_T_MAX - self->size < dist) - goto onoutofrange; - where = self->size + dist; - break; - default: - PyErr_SetString(PyExc_ValueError, "unknown seek type"); - return NULL; - } - if (where > self->size || where < 0) + Py_ssize_t where; + switch (how) { + case 0: /* relative to start */ + where = dist; + break; + case 1: /* relative to current position */ + if (PY_SSIZE_T_MAX - self->pos < dist) goto onoutofrange; - self->pos = where; - return PyLong_FromSsize_t(self->pos); + where = self->pos + dist; + break; + case 2: /* relative to end */ + if (PY_SSIZE_T_MAX - self->size < dist) + goto onoutofrange; + where = self->size + dist; + break; + default: + PyErr_SetString(PyExc_ValueError, "unknown seek type"); + return NULL; } + if (where > self->size || where < 0) + goto onoutofrange; + self->pos = where; + return PyLong_FromSsize_t(self->pos); onoutofrange: PyErr_SetString(PyExc_ValueError, "seek out of range"); return NULL; } +/*[clinic input] +mmap.mmap.seekable + +[clinic start generated code]*/ + static PyObject * -mmap_seekable_method(PyObject *op, PyObject *Py_UNUSED(ignored)) +mmap_mmap_seekable_impl(mmap_object *self) +/*[clinic end generated code: output=6311dc3ea300fa38 input=5132505f6e259001]*/ { Py_RETURN_TRUE; } +/*[clinic input] +@critical_section +mmap.mmap.move + + dest: Py_ssize_t + src: Py_ssize_t + count as cnt: Py_ssize_t + / + +[clinic start generated code]*/ + static PyObject * -mmap_move_method(PyObject *op, PyObject *args) +mmap_mmap_move_impl(mmap_object *self, Py_ssize_t dest, Py_ssize_t src, + Py_ssize_t cnt) +/*[clinic end generated code: output=391f549a44181793 input=cf8cfe10d9f6b448]*/ { - Py_ssize_t dest, src, cnt; - mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "nnn:move", &dest, &src, &cnt) || - !is_writable(self)) { + if (!is_writable(self)) { return NULL; } else { /* bounds check the values */ @@ -1048,30 +1171,53 @@ static PyObject * mmap_closed_get(PyObject *op, void *Py_UNUSED(closure)) { mmap_object *self = mmap_object_CAST(op); + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); #ifdef MS_WINDOWS - return PyBool_FromLong(self->map_handle == NULL ? 1 : 0); + result = PyBool_FromLong(self->map_handle == NULL ? 1 : 0); #elif defined(UNIX) - return PyBool_FromLong(self->data == NULL ? 1 : 0); + result = PyBool_FromLong(self->data == NULL ? 1 : 0); #endif + Py_END_CRITICAL_SECTION(); + return result; } +/*[clinic input] +@critical_section +mmap.mmap.__enter__ + +[clinic start generated code]*/ + static PyObject * -mmap__enter__method(PyObject *op, PyObject *Py_UNUSED(ignored)) +mmap_mmap___enter___impl(mmap_object *self) +/*[clinic end generated code: output=92cfc59f4c4e2d26 input=a446541fbfe0b890]*/ { - mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); return Py_NewRef(self); } +/*[clinic input] +@critical_section +mmap.mmap.__exit__ + + exc_type: object + exc_value: object + traceback: object + / + +[clinic start generated code]*/ + static PyObject * -mmap__exit__method(PyObject *op, PyObject *Py_UNUSED(args)) +mmap_mmap___exit___impl(mmap_object *self, PyObject *exc_type, + PyObject *exc_value, PyObject *traceback) +/*[clinic end generated code: output=bec7e3e319c1f07e input=5f28e91cf752bc64]*/ { - return mmap_close_method(op, NULL); + return mmap_mmap_close_impl(self); } static PyObject * -mmap__repr__method(PyObject *op) +mmap__repr__method_lock_held(PyObject *op) { mmap_object *mobj = mmap_object_CAST(op); @@ -1115,11 +1261,27 @@ mmap__repr__method(PyObject *op) } } -#ifdef MS_WINDOWS static PyObject * -mmap__sizeof__method(PyObject *op, PyObject *Py_UNUSED(dummy)) +mmap__repr__method(PyObject *op) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = mmap__repr__method_lock_held(op); + Py_END_CRITICAL_SECTION(); + return result; +} + +#ifdef MS_WINDOWS +/*[clinic input] +@critical_section +mmap.mmap.__sizeof__ + +[clinic start generated code]*/ + +static PyObject * +mmap_mmap___sizeof___impl(mmap_object *self) +/*[clinic end generated code: output=1aed30daff807d09 input=8a648868a089553c]*/ { - mmap_object *self = mmap_object_CAST(op); size_t res = _PyObject_SIZE(Py_TYPE(self)); if (self->tagname) { res += (wcslen(self->tagname) + 1) * sizeof(self->tagname[0]); @@ -1129,18 +1291,26 @@ mmap__sizeof__method(PyObject *op, PyObject *Py_UNUSED(dummy)) #endif #if defined(MS_WINDOWS) && defined(Py_DEBUG) +/*[clinic input] +@critical_section +mmap.mmap._protect + + flNewProtect: unsigned_int(bitwise=True) + start: Py_ssize_t + length: Py_ssize_t + / + +[clinic start generated code]*/ + static PyObject * -mmap_protect_method(PyObject *op, PyObject *args) { - DWORD flNewProtect, flOldProtect; - Py_ssize_t start, length; - mmap_object *self = mmap_object_CAST(op); +mmap_mmap__protect_impl(mmap_object *self, unsigned int flNewProtect, + Py_ssize_t start, Py_ssize_t length) +/*[clinic end generated code: output=a87271a34d1ad6cf input=9170498c5e1482da]*/ +{ + DWORD flOldProtect; CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "Inn:protect", &flNewProtect, &start, &length)) { - return NULL; - } - if (!VirtualProtect((void *) (self->data + start), length, flNewProtect, &flOldProtect)) { @@ -1153,18 +1323,32 @@ mmap_protect_method(PyObject *op, PyObject *args) { #endif #ifdef HAVE_MADVISE +/*[clinic input] +@critical_section +mmap.mmap.madvise + + option: int + start: Py_ssize_t = 0 + length as length_obj: object = None + / + +[clinic start generated code]*/ + static PyObject * -mmap_madvise_method(PyObject *op, PyObject *args) +mmap_mmap_madvise_impl(mmap_object *self, int option, Py_ssize_t start, + PyObject *length_obj) +/*[clinic end generated code: output=816be656f08c0e3c input=2d37f7a4c87f1053]*/ { - int option; - Py_ssize_t start = 0, length; - mmap_object *self = mmap_object_CAST(op); + Py_ssize_t length; CHECK_VALID(NULL); - length = self->size; - - if (!PyArg_ParseTuple(args, "i|nn:madvise", &option, &start, &length)) { - return NULL; + if (length_obj == Py_None) { + length = self->size; + } else { + length = _As_Py_ssize_t(length_obj); + if (length == -1 && PyErr_Occurred()) { + return NULL; + } } if (start < 0 || start >= self->size) { @@ -1200,34 +1384,26 @@ static struct PyMemberDef mmap_object_members[] = { }; static struct PyMethodDef mmap_object_methods[] = { - {"close", mmap_close_method, METH_NOARGS}, - {"find", mmap_find_method, METH_VARARGS}, - {"rfind", mmap_rfind_method, METH_VARARGS}, - {"flush", mmap_flush_method, METH_VARARGS}, -#ifdef HAVE_MADVISE - {"madvise", mmap_madvise_method, METH_VARARGS}, -#endif - {"move", mmap_move_method, METH_VARARGS}, - {"read", mmap_read_method, METH_VARARGS}, - {"read_byte", mmap_read_byte_method, METH_NOARGS}, - {"readline", mmap_read_line_method, METH_NOARGS}, -#if defined(MS_WINDOWS) || defined(HAVE_MREMAP) - {"resize", mmap_resize_method, METH_VARARGS}, -#endif - {"seek", mmap_seek_method, METH_VARARGS}, - {"seekable", mmap_seekable_method, METH_NOARGS}, - {"size", mmap_size_method, METH_NOARGS}, - {"tell", mmap_tell_method, METH_NOARGS}, - {"write", mmap_write_method, METH_VARARGS}, - {"write_byte", mmap_write_byte_method, METH_VARARGS}, - {"__enter__", mmap__enter__method, METH_NOARGS}, - {"__exit__", mmap__exit__method, METH_VARARGS}, -#ifdef MS_WINDOWS - {"__sizeof__", mmap__sizeof__method, METH_NOARGS}, -#ifdef Py_DEBUG - {"_protect", mmap_protect_method, METH_VARARGS}, -#endif // Py_DEBUG -#endif // MS_WINDOWS + MMAP_MMAP_CLOSE_METHODDEF + MMAP_MMAP_FIND_METHODDEF + MMAP_MMAP_RFIND_METHODDEF + MMAP_MMAP_FLUSH_METHODDEF + MMAP_MMAP_MADVISE_METHODDEF + MMAP_MMAP_MOVE_METHODDEF + MMAP_MMAP_READ_METHODDEF + MMAP_MMAP_READ_BYTE_METHODDEF + MMAP_MMAP_READLINE_METHODDEF + MMAP_MMAP_RESIZE_METHODDEF + MMAP_MMAP_SEEK_METHODDEF + MMAP_MMAP_SEEKABLE_METHODDEF + MMAP_MMAP_SIZE_METHODDEF + MMAP_MMAP_TELL_METHODDEF + MMAP_MMAP_WRITE_METHODDEF + MMAP_MMAP_WRITE_BYTE_METHODDEF + MMAP_MMAP___ENTER___METHODDEF + MMAP_MMAP___EXIT___METHODDEF + MMAP_MMAP___SIZEOF___METHODDEF + MMAP_MMAP__PROTECT_METHODDEF {NULL, NULL} /* sentinel */ }; @@ -1240,7 +1416,7 @@ static PyGetSetDef mmap_object_getset[] = { /* Functions for treating an mmap'ed file as a buffer */ static int -mmap_buffer_getbuf(PyObject *op, Py_buffer *view, int flags) +mmap_buffer_getbuf_lock_held(PyObject *op, Py_buffer *view, int flags) { mmap_object *self = mmap_object_CAST(op); CHECK_VALID(-1); @@ -1251,23 +1427,45 @@ mmap_buffer_getbuf(PyObject *op, Py_buffer *view, int flags) return 0; } +static int +mmap_buffer_getbuf(PyObject *op, Py_buffer *view, int flags) +{ + int result; + Py_BEGIN_CRITICAL_SECTION(op); + result = mmap_buffer_getbuf_lock_held(op, view, flags); + Py_END_CRITICAL_SECTION(); + return result; +} + static void mmap_buffer_releasebuf(PyObject *op, Py_buffer *Py_UNUSED(view)) { mmap_object *self = mmap_object_CAST(op); + Py_BEGIN_CRITICAL_SECTION(self); self->exports--; + Py_END_CRITICAL_SECTION(); } static Py_ssize_t -mmap_length(PyObject *op) +mmap_length_lock_held(PyObject *op) { mmap_object *self = mmap_object_CAST(op); CHECK_VALID(-1); return self->size; } +static Py_ssize_t +mmap_length(PyObject *op) +{ + Py_ssize_t result; + Py_BEGIN_CRITICAL_SECTION(op); + result = mmap_length_lock_held(op); + Py_END_CRITICAL_SECTION(); + return result; +} + static PyObject * -mmap_item(PyObject *op, Py_ssize_t i) +mmap_item_lock_held(PyObject *op, Py_ssize_t i) { mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); @@ -1284,7 +1482,16 @@ mmap_item(PyObject *op, Py_ssize_t i) } static PyObject * -mmap_subscript(PyObject *op, PyObject *item) +mmap_item(PyObject *op, Py_ssize_t i) { + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = mmap_item_lock_held(op, i); + Py_END_CRITICAL_SECTION(); + return result; +} + +static PyObject * +mmap_subscript_lock_held(PyObject *op, PyObject *item) { mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); @@ -1346,8 +1553,18 @@ mmap_subscript(PyObject *op, PyObject *item) } } +static PyObject * +mmap_subscript(PyObject *op, PyObject *item) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = mmap_subscript_lock_held(op, item); + Py_END_CRITICAL_SECTION(); + return result; +} + static int -mmap_ass_item(PyObject *op, Py_ssize_t i, PyObject *v) +mmap_ass_item_lock_held(PyObject *op, Py_ssize_t i, PyObject *v) { const char *buf; mmap_object *self = mmap_object_CAST(op); @@ -1378,7 +1595,17 @@ mmap_ass_item(PyObject *op, Py_ssize_t i, PyObject *v) } static int -mmap_ass_subscript(PyObject *op, PyObject *item, PyObject *value) +mmap_ass_item(PyObject *op, Py_ssize_t i, PyObject *v) +{ + int result; + Py_BEGIN_CRITICAL_SECTION(op); + result = mmap_ass_item_lock_held(op, i, v); + Py_END_CRITICAL_SECTION(); + return result; +} + +static int +mmap_ass_subscript_lock_held(PyObject *op, PyObject *item, PyObject *value) { mmap_object *self = mmap_object_CAST(op); CHECK_VALID(-1); @@ -1474,6 +1701,16 @@ mmap_ass_subscript(PyObject *op, PyObject *item, PyObject *value) } } +static int +mmap_ass_subscript(PyObject *op, PyObject *item, PyObject *value) +{ + int result; + Py_BEGIN_CRITICAL_SECTION(op); + result = mmap_ass_subscript_lock_held(op, item, value); + Py_END_CRITICAL_SECTION(); + return result; +} + static PyObject * new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict); From 65089406a54df6d3fdc65f5f7e7691ec5de088d8 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 9 Oct 2025 16:00:37 +0800 Subject: [PATCH 080/373] gh-139743: Avoid import-time print in test_sqlite3 (GH-139746) --- Lib/test/test_sqlite3/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_sqlite3/__init__.py b/Lib/test/test_sqlite3/__init__.py index d777fca82da..78a1e2078a5 100644 --- a/Lib/test/test_sqlite3/__init__.py +++ b/Lib/test/test_sqlite3/__init__.py @@ -8,8 +8,7 @@ # Implement the unittest "load tests" protocol. def load_tests(*args): + if verbose: + print(f"test_sqlite3: testing with SQLite version {sqlite3.sqlite_version}") pkg_dir = os.path.dirname(__file__) return load_package_tests(pkg_dir, *args) - -if verbose: - print(f"test_sqlite3: testing with SQLite version {sqlite3.sqlite_version}") From 197c610a1e8e396e354c10f6f5dd60ed6548be77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 9 Oct 2025 11:14:36 +0200 Subject: [PATCH 081/373] gh-88046: remove impossible conditional import for `_ssl.RAND_egd` (#139648) `_ssl.RAND_egd` was removed in b8d0fa035d74ae6ae00794c9af636b427c5dc650. --- Doc/library/ssl.rst | 5 ++--- Lib/ssl.py | 5 ----- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 0f2c2b89295..5b59a0698e4 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -354,9 +354,8 @@ Random generation .. function:: RAND_status() Return ``True`` if the SSL pseudo-random number generator has been seeded - with 'enough' randomness, and ``False`` otherwise. You can use - :func:`ssl.RAND_egd` and :func:`ssl.RAND_add` to increase the randomness of - the pseudo-random number generator. + with 'enough' randomness, and ``False`` otherwise. Use :func:`ssl.RAND_add` + to increase the randomness of the pseudo-random number generator. .. function:: RAND_add(bytes, entropy, /) diff --git a/Lib/ssl.py b/Lib/ssl.py index 493ea901d93..7ad7969a821 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -110,11 +110,6 @@ ) from _ssl import txt2obj as _txt2obj, nid2obj as _nid2obj from _ssl import RAND_status, RAND_add, RAND_bytes -try: - from _ssl import RAND_egd -except ImportError: - # RAND_egd is not supported on some platforms - pass from _ssl import get_sigalgs From 6fd141834193e3dc2423e785d97eb127c972a70e Mon Sep 17 00:00:00 2001 From: Anuradha Agrawal Date: Thu, 9 Oct 2025 14:54:53 +0530 Subject: [PATCH 082/373] gh-139742: Add support for Python 3.14 t-string prefixes in IDLE colorizer and tests (#139756) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add 't' prefix to colorizer.py stringprefix regex to support Python 3.14 template strings. Add t prefixes to test_colorizer.py source test text and adjust line numbers on test methods. --------- Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Łukasz Langa Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> --- Lib/idlelib/colorizer.py | 2 +- Lib/idlelib/idle_test/test_colorizer.py | 29 ++++++++++--------- ...-10-08-08-35-50.gh-issue-139742.B3fZLg.rst | 1 + 3 files changed, 17 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/IDLE/2025-10-08-08-35-50.gh-issue-139742.B3fZLg.rst diff --git a/Lib/idlelib/colorizer.py b/Lib/idlelib/colorizer.py index b4df353012b..bffa2ddd3cd 100644 --- a/Lib/idlelib/colorizer.py +++ b/Lib/idlelib/colorizer.py @@ -47,7 +47,7 @@ def make_pat(): name not in keyword.kwlist] builtin = r"([^.'\"\\#]\b|^)" + any("BUILTIN", builtinlist) + r"\b" comment = any("COMMENT", [r"#[^\n]*"]) - stringprefix = r"(?i:r|u|f|fr|rf|b|br|rb)?" + stringprefix = r"(?i:r|u|f|fr|rf|b|br|rb|t|rt|tr)?" sqstring = stringprefix + r"'[^'\\\n]*(\\.[^'\\\n]*)*'?" dqstring = stringprefix + r'"[^"\\\n]*(\\.[^"\\\n]*)*"?' sq3string = stringprefix + r"'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?" diff --git a/Lib/idlelib/idle_test/test_colorizer.py b/Lib/idlelib/idle_test/test_colorizer.py index 308bc389384..40800df97b0 100644 --- a/Lib/idlelib/idle_test/test_colorizer.py +++ b/Lib/idlelib/idle_test/test_colorizer.py @@ -36,6 +36,7 @@ async def f(): await g() # All valid prefixes for unicode and byte strings should be colored. r'x', u'x', R'x', U'x', f'x', F'x' fr'x', Fr'x', fR'x', FR'x', rf'x', rF'x', Rf'x', RF'x' + tr'x', Tr'x', tR'x', TR'x', rt'x', rT'x', Rt'x', RT'x' b'x',B'x', br'x',Br'x',bR'x',BR'x', rb'x', rB'x',Rb'x',RB'x' # Invalid combinations of legal characters should be half colored. ur'x', ru'x', uf'x', fu'x', UR'x', ufr'x', rfu'x', xf'x', fx'x' @@ -390,19 +391,19 @@ def test_recolorize_main(self, mock_notify): ('6.0', ('KEYWORD',)), ('6.10', ('DEFINITION',)), ('6.11', ()), ('8.0', ('STRING',)), ('8.4', ()), ('8.5', ('STRING',)), ('8.12', ()), ('8.14', ('STRING',)), - ('19.0', ('KEYWORD',)), - ('20.4', ('KEYWORD',)), ('20.16', ('KEYWORD',)),# ('20.19', ('KEYWORD',)), - #('22.4', ('KEYWORD',)), ('22.10', ('KEYWORD',)), ('22.14', ('KEYWORD',)), ('22.19', ('STRING',)), - #('23.12', ('KEYWORD',)), - ('24.8', ('KEYWORD',)), - ('25.4', ('KEYWORD',)), ('25.9', ('KEYWORD',)), - ('25.11', ('KEYWORD',)), ('25.15', ('STRING',)), - ('25.19', ('KEYWORD',)), ('25.22', ()), - ('25.24', ('KEYWORD',)), ('25.29', ('BUILTIN',)), ('25.37', ('KEYWORD',)), - ('26.4', ('KEYWORD',)), ('26.9', ('KEYWORD',)),# ('26.11', ('KEYWORD',)), ('26.14', (),), - ('27.25', ('STRING',)), ('27.38', ('STRING',)), - ('29.0', ('STRING',)), - ('30.1', ('STRING',)), + ('20.0', ('KEYWORD',)), + ('21.4', ('KEYWORD',)), ('21.16', ('KEYWORD',)),# ('21.19', ('KEYWORD',)), + #('23.4', ('KEYWORD',)), ('23.10', ('KEYWORD',)), ('23.14', ('KEYWORD',)), ('23.19', ('STRING',)), + #('24.12', ('KEYWORD',)), + ('25.8', ('KEYWORD',)), + ('26.4', ('KEYWORD',)), ('26.9', ('KEYWORD',)), + ('26.11', ('KEYWORD',)), ('26.15', ('STRING',)), + ('26.19', ('KEYWORD',)), ('26.22', ()), + ('26.24', ('KEYWORD',)), ('26.29', ('BUILTIN',)), ('26.37', ('KEYWORD',)), + ('27.4', ('KEYWORD',)), ('27.9', ('KEYWORD',)),# ('27.11', ('KEYWORD',)), ('27.14', (),), + ('28.25', ('STRING',)), ('28.38', ('STRING',)), + ('30.0', ('STRING',)), + ('31.1', ('STRING',)), # SYNC at the end of every line. ('1.55', ('SYNC',)), ('2.50', ('SYNC',)), ('3.34', ('SYNC',)), ) @@ -433,7 +434,7 @@ def test_recolorize_main(self, mock_notify): eq(text.tag_nextrange('STRING', '8.12'), ('8.14', '8.17')) eq(text.tag_nextrange('STRING', '8.17'), ('8.19', '8.26')) eq(text.tag_nextrange('SYNC', '8.0'), ('8.26', '9.0')) - eq(text.tag_nextrange('SYNC', '30.0'), ('30.10', '32.0')) + eq(text.tag_nextrange('SYNC', '31.0'), ('31.10', '33.0')) def _assert_highlighting(self, source, tag_ranges): """Check highlighting of a given piece of code. diff --git a/Misc/NEWS.d/next/IDLE/2025-10-08-08-35-50.gh-issue-139742.B3fZLg.rst b/Misc/NEWS.d/next/IDLE/2025-10-08-08-35-50.gh-issue-139742.B3fZLg.rst new file mode 100644 index 00000000000..c9e2f977a79 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2025-10-08-08-35-50.gh-issue-139742.B3fZLg.rst @@ -0,0 +1 @@ +Colorize t-string prefixes for template strings in IDLE, as done for f-string prefixes. From d2deb8fdef1ac5e54564448677cdb1522f1b776d Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Thu, 9 Oct 2025 10:34:35 +0100 Subject: [PATCH 083/373] gh-101100: Fix reference warnings in `c-api/init.rst` documenting `PyGILState_STATE` (#139572) --- Doc/c-api/init.rst | 25 +++++++++++++++++++------ Doc/tools/.nitignore | 1 - 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index ccf85e627f9..7a501ea22be 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1113,7 +1113,7 @@ code, or when embedding the Python interpreter: This function is safe to call without an :term:`attached thread state`; it will simply return ``NULL`` indicating that there was no prior thread state. - .. seealso: + .. seealso:: :c:func:`PyEval_ReleaseThread` .. note:: @@ -1124,6 +1124,19 @@ code, or when embedding the Python interpreter: The following functions use thread-local storage, and are not compatible with sub-interpreters: +.. c:type:: PyGILState_STATE + + The type of the value returned by :c:func:`PyGILState_Ensure` and passed to + :c:func:`PyGILState_Release`. + + .. c:enumerator:: PyGILState_LOCKED + + The GIL was already held when :c:func:`PyGILState_Ensure` was called. + + .. c:enumerator:: PyGILState_UNLOCKED + + The GIL was not held when :c:func:`PyGILState_Ensure` was called. + .. c:function:: PyGILState_STATE PyGILState_Ensure() Ensure that the current thread is ready to call the Python C API regardless @@ -1174,12 +1187,12 @@ with sub-interpreters: made on the main thread. This is mainly a helper/diagnostic function. .. note:: - This function does not account for :term:`thread states ` created - by something other than :c:func:`PyGILState_Ensure` (such as :c:func:`PyThreadState_New`). + This function may return non-``NULL`` even when the :term:`thread state` + is detached. Prefer :c:func:`PyThreadState_Get` or :c:func:`PyThreadState_GetUnchecked` for most cases. - .. seealso: :c:func:`PyThreadState_Get`` + .. seealso:: :c:func:`PyThreadState_Get` .. c:function:: int PyGILState_Check() @@ -1278,11 +1291,11 @@ All of the following functions must be called after :c:func:`Py_Initialize`. must be :term:`attached ` .. versionchanged:: 3.9 - This function now calls the :c:member:`PyThreadState.on_delete` callback. + This function now calls the :c:member:`!PyThreadState.on_delete` callback. Previously, that happened in :c:func:`PyThreadState_Delete`. .. versionchanged:: 3.13 - The :c:member:`PyThreadState.on_delete` callback was removed. + The :c:member:`!PyThreadState.on_delete` callback was removed. .. c:function:: void PyThreadState_Delete(PyThreadState *tstate) diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 4ee09c6bbaa..827c5808fa5 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -4,7 +4,6 @@ Doc/c-api/descriptor.rst Doc/c-api/float.rst -Doc/c-api/init.rst Doc/c-api/init_config.rst Doc/c-api/intro.rst Doc/c-api/module.rst From 81959a0364f5bc15414d5bd61c5c0d019d486fe5 Mon Sep 17 00:00:00 2001 From: DeepWzh Date: Thu, 9 Oct 2025 22:16:48 +0800 Subject: [PATCH 084/373] gh-133400: Fixed Ctrl+D (^D) behavior in :mod:`_pyrepl` module (GH-133883) Co-authored-by: adam j hartz --- Lib/_pyrepl/commands.py | 19 +++-- Lib/test/test_pyrepl/test_pyrepl.py | 83 +++++++++++++++++++ ...-05-11-09-40-19.gh-issue-133400.zkWla8.rst | 1 + 3 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-11-09-40-19.gh-issue-133400.zkWla8.rst diff --git a/Lib/_pyrepl/commands.py b/Lib/_pyrepl/commands.py index 50c824995d8..10127e58897 100644 --- a/Lib/_pyrepl/commands.py +++ b/Lib/_pyrepl/commands.py @@ -420,14 +420,17 @@ class delete(EditCommand): def do(self) -> None: r = self.reader b = r.buffer - if ( - r.pos == 0 - and len(b) == 0 # this is something of a hack - and self.event[-1] == "\004" - ): - r.update_screen() - r.console.finish() - raise EOFError + if self.event[-1] == "\004": + if b and b[-1].endswith("\n"): + self.finish = True + elif ( + r.pos == 0 + and len(b) == 0 # this is something of a hack + ): + r.update_screen() + r.console.finish() + raise EOFError + for i in range(r.get_arg()): if r.pos != len(b): del b[r.pos] diff --git a/Lib/test/test_pyrepl/test_pyrepl.py b/Lib/test/test_pyrepl/test_pyrepl.py index 47d384a209e..ad7464dc3d3 100644 --- a/Lib/test/test_pyrepl/test_pyrepl.py +++ b/Lib/test/test_pyrepl/test_pyrepl.py @@ -1825,3 +1825,86 @@ def test_detect_pip_usage_in_repl(self): " outside of the Python REPL" ) self.assertIn(hint, output) + +class TestPyReplCtrlD(TestCase): + """Test Ctrl+D behavior in _pyrepl to match old pre-3.13 REPL behavior. + + Ctrl+D should: + - Exit on empty buffer (raises EOFError) + - Delete character when cursor is in middle of line + - Perform no operation when cursor is at end of line without newline + - Exit multiline mode when cursor is at end with trailing newline + - Run code up to that point when pressed on blank line with preceding lines + """ + def prepare_reader(self, events): + console = FakeConsole(events) + config = ReadlineConfig(readline_completer=None) + reader = ReadlineAlikeReader(console=console, config=config) + return reader + + def test_ctrl_d_empty_line(self): + """Test that pressing Ctrl+D on empty line exits the program""" + events = [ + Event(evt="key", data="\x04", raw=bytearray(b"\x04")), # Ctrl+D + ] + reader = self.prepare_reader(events) + with self.assertRaises(EOFError): + multiline_input(reader) + + def test_ctrl_d_multiline_with_new_line(self): + """Test that pressing Ctrl+D in multiline mode with trailing newline exits multiline mode""" + events = itertools.chain( + code_to_events("def f():\n pass\n"), # Enter multiline mode with trailing newline + [ + Event(evt="key", data="\x04", raw=bytearray(b"\x04")), # Ctrl+D + ], + ) + reader, _ = handle_all_events(events) + self.assertTrue(reader.finished) + self.assertEqual("def f():\n pass\n", "".join(reader.buffer)) + + def test_ctrl_d_multiline_middle_of_line(self): + """Test that pressing Ctrl+D in multiline mode with cursor in middle deletes character""" + events = itertools.chain( + code_to_events("def f():\n hello world"), # Enter multiline mode + [ + Event(evt="key", data="left", raw=bytearray(b"\x1bOD")) + ] * 5, # move cursor to 'w' in "world" + [ + Event(evt="key", data="\x04", raw=bytearray(b"\x04")) + ], # Ctrl+D should delete 'w' + ) + reader, _ = handle_all_events(events) + self.assertFalse(reader.finished) + self.assertEqual("def f():\n hello orld", "".join(reader.buffer)) + + def test_ctrl_d_multiline_end_of_line_no_newline(self): + """Test that pressing Ctrl+D at end of line without newline performs no operation""" + events = itertools.chain( + code_to_events("def f():\n hello"), # Enter multiline mode, no trailing newline + [ + Event(evt="key", data="\x04", raw=bytearray(b"\x04")) + ], # Ctrl+D should be no-op + ) + reader, _ = handle_all_events(events) + self.assertFalse(reader.finished) + self.assertEqual("def f():\n hello", "".join(reader.buffer)) + + def test_ctrl_d_single_line_middle_of_line(self): + """Test that pressing Ctrl+D in single line mode deletes current character""" + events = itertools.chain( + code_to_events("hello"), + [Event(evt="key", data="left", raw=bytearray(b"\x1bOD"))], # move left + [Event(evt="key", data="\x04", raw=bytearray(b"\x04"))], # Ctrl+D + ) + reader, _ = handle_all_events(events) + self.assertEqual("hell", "".join(reader.buffer)) + + def test_ctrl_d_single_line_end_no_newline(self): + """Test that pressing Ctrl+D at end of single line without newline does nothing""" + events = itertools.chain( + code_to_events("hello"), # cursor at end of line + [Event(evt="key", data="\x04", raw=bytearray(b"\x04"))], # Ctrl+D + ) + reader, _ = handle_all_events(events) + self.assertEqual("hello", "".join(reader.buffer)) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-11-09-40-19.gh-issue-133400.zkWla8.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-11-09-40-19.gh-issue-133400.zkWla8.rst new file mode 100644 index 00000000000..2498d6ebaa5 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-11-09-40-19.gh-issue-133400.zkWla8.rst @@ -0,0 +1 @@ +Fixed Ctrl+D (^D) behavior in _pyrepl module to match old pre-3.13 REPL behavior. From b8c8b8f1d30767e5358ffa93d41e6e27ca60570d Mon Sep 17 00:00:00 2001 From: yihong Date: Thu, 9 Oct 2025 22:58:01 +0800 Subject: [PATCH 085/373] gh-139391: properly handle `signal.signal()` in `UnixConsole` when called from a non-main thread (#139392) --- Lib/_pyrepl/unix_console.py | 7 ++++++- Lib/test/test_pyrepl/test_unix_console.py | 14 +++++++++++++- .../2025-09-28-16-34-11.gh-issue-139391.nRFnmx.rst | 3 +++ 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-09-28-16-34-11.gh-issue-139391.nRFnmx.rst diff --git a/Lib/_pyrepl/unix_console.py b/Lib/_pyrepl/unix_console.py index fe45b4eb384..09247de748e 100644 --- a/Lib/_pyrepl/unix_console.py +++ b/Lib/_pyrepl/unix_console.py @@ -390,7 +390,12 @@ def restore(self): os.write(self.output_fd, b"\033[?7h") if hasattr(self, "old_sigwinch"): - signal.signal(signal.SIGWINCH, self.old_sigwinch) + try: + signal.signal(signal.SIGWINCH, self.old_sigwinch) + except ValueError as e: + import threading + if threading.current_thread() is threading.main_thread(): + raise e del self.old_sigwinch def push_char(self, char: int | bytes) -> None: diff --git a/Lib/test/test_pyrepl/test_unix_console.py b/Lib/test/test_pyrepl/test_unix_console.py index 9ac2e9961df..f4fb9237ffd 100644 --- a/Lib/test/test_pyrepl/test_unix_console.py +++ b/Lib/test/test_pyrepl/test_unix_console.py @@ -4,11 +4,12 @@ import signal import subprocess import sys +import threading import unittest from functools import partial from test import support from test.support import os_helper, force_not_colorized_test_class -from test.support import script_helper +from test.support import script_helper, threading_helper from unittest import TestCase from unittest.mock import MagicMock, call, patch, ANY, Mock @@ -318,6 +319,17 @@ def test_restore_with_invalid_environ_on_macos(self, _os_write): console.prepare() # needed to call restore() console.restore() # this should succeed + @threading_helper.reap_threads + @threading_helper.requires_working_threading() + def test_restore_in_thread(self, _os_write): + # gh-139391: ensure that console.restore() silently suppresses + # exceptions when calling signal.signal() from a non-main thread. + console = unix_console([]) + console.old_sigwinch = signal.SIG_DFL + thread = threading.Thread(target=console.restore) + thread.start() + thread.join() # this should not raise + @unittest.skipIf(sys.platform == "win32", "No Unix console on Windows") class TestUnixConsoleEIOHandling(TestCase): diff --git a/Misc/NEWS.d/next/Library/2025-09-28-16-34-11.gh-issue-139391.nRFnmx.rst b/Misc/NEWS.d/next/Library/2025-09-28-16-34-11.gh-issue-139391.nRFnmx.rst new file mode 100644 index 00000000000..93d1ce613bc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-28-16-34-11.gh-issue-139391.nRFnmx.rst @@ -0,0 +1,3 @@ +Fix an issue when, on non-Windows platforms, it was not possible to +gracefully exit a ``python -m asyncio`` process suspended by Ctrl+Z +and later resumed by :manpage:`fg` other than with :manpage:`kill`. From a310b3a99d05e107963023a5736b67afe4ae1968 Mon Sep 17 00:00:00 2001 From: yihong Date: Thu, 9 Oct 2025 23:24:52 +0800 Subject: [PATCH 086/373] gh-139845: do not print twice in default asyncio REPL (#139846) Co-authored-by: Kumar Aditya --- Lib/asyncio/__main__.py | 3 ++- .../Library/2025-10-09-21-37-20.gh-issue-139845.dzx5UP.rst | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-09-21-37-20.gh-issue-139845.dzx5UP.rst diff --git a/Lib/asyncio/__main__.py b/Lib/asyncio/__main__.py index 12d6f7714ee..10bfca3cf96 100644 --- a/Lib/asyncio/__main__.py +++ b/Lib/asyncio/__main__.py @@ -74,7 +74,8 @@ def callback(): return except BaseException: if keyboard_interrupted: - self.write("\nKeyboardInterrupt\n") + if not CAN_USE_PYREPL: + self.write("\nKeyboardInterrupt\n") else: self.showtraceback() return self.STATEMENT_FAILED diff --git a/Misc/NEWS.d/next/Library/2025-10-09-21-37-20.gh-issue-139845.dzx5UP.rst b/Misc/NEWS.d/next/Library/2025-10-09-21-37-20.gh-issue-139845.dzx5UP.rst new file mode 100644 index 00000000000..3cd294e49cd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-09-21-37-20.gh-issue-139845.dzx5UP.rst @@ -0,0 +1 @@ +Fix to not print KeyboardInterrupt twice in default asyncio REPL. From 9fc4366f09904fd9aac797d542e332e8a4c1a921 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Thu, 9 Oct 2025 09:53:14 -0700 Subject: [PATCH 087/373] GH-139809: Fix argparse subcommand prog not respecting color environment variables (#139818) --- Lib/argparse.py | 4 +++- Lib/test/test_argparse.py | 15 ++++++++++++++- ...2025-10-09-03-06-19.gh-issue-139809.lzHJNu.rst | 1 + 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-09-03-06-19.gh-issue-139809.lzHJNu.rst diff --git a/Lib/argparse.py b/Lib/argparse.py index d71e551401c..1ddb7abbb35 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -1959,7 +1959,9 @@ def add_subparsers(self, **kwargs): # prog defaults to the usage message of this parser, skipping # optional arguments and with no "usage:" prefix if kwargs.get('prog') is None: - formatter = self._get_formatter() + # Create formatter without color to avoid storing ANSI codes in prog + formatter = self.formatter_class(prog=self.prog) + formatter._set_color(False) positionals = self._get_positional_actions() groups = self._mutually_exclusive_groups formatter.add_usage(None, positionals, groups, '') diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 27e38040a98..6a07f190285 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -22,6 +22,7 @@ captured_stderr, force_not_colorized, force_not_colorized_test_class, + swap_attr, ) from test.support import import_helper from test.support import os_helper @@ -7128,7 +7129,8 @@ class TestColorized(TestCase): def setUp(self): super().setUp() # Ensure color even if ran with NO_COLOR=1 - _colorize.can_colorize = lambda *args, **kwargs: True + self.enterContext(swap_attr(_colorize, 'can_colorize', + lambda *args, **kwargs: True)) self.theme = _colorize.get_theme(force_color=True).argparse def test_argparse_color(self): @@ -7355,6 +7357,17 @@ def __init__(self, prog): {short_b}+f{reset}, {long_b}++foo{reset} {label_b}FOO{reset} foo help ''')) + def test_subparser_prog_is_stored_without_color(self): + parser = argparse.ArgumentParser(prog='complex', color=True) + sub = parser.add_subparsers(dest='command') + demo_parser = sub.add_parser('demo') + + self.assertNotIn('\x1b[', demo_parser.prog) + + demo_parser.color = False + help_text = demo_parser.format_help() + self.assertNotIn('\x1b[', help_text) + class TestModule(unittest.TestCase): def test_deprecated__version__(self): diff --git a/Misc/NEWS.d/next/Library/2025-10-09-03-06-19.gh-issue-139809.lzHJNu.rst b/Misc/NEWS.d/next/Library/2025-10-09-03-06-19.gh-issue-139809.lzHJNu.rst new file mode 100644 index 00000000000..498b8f7fd27 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-09-03-06-19.gh-issue-139809.lzHJNu.rst @@ -0,0 +1 @@ +Prevent premature colorization of subparser ``prog`` in :meth:`argparse.ArgumentParser.add_subparsers` to respect color environment variable changes after parser creation. From 04461510fb8bedc054477c2634ffd0e575485b12 Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Thu, 9 Oct 2025 20:13:38 +0300 Subject: [PATCH 088/373] gh-139672: Remove references to `passlib` (#139673) --- Doc/library/crypt.rst | 2 +- Doc/whatsnew/3.13.rst | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Doc/library/crypt.rst b/Doc/library/crypt.rst index 9ff37196ccf..647cb4925f3 100644 --- a/Doc/library/crypt.rst +++ b/Doc/library/crypt.rst @@ -13,7 +13,7 @@ being deprecated in Python 3.11. The removal was decided in :pep:`594`. Applications can use the :mod:`hashlib` module from the standard library. Other possible replacements are third-party libraries from PyPI: -:pypi:`legacycrypt`, :pypi:`bcrypt`, :pypi:`argon2-cffi`, or :pypi:`passlib`. +:pypi:`legacycrypt`, :pypi:`bcrypt`, or :pypi:`argon2-cffi`. These are not supported or maintained by the Python core team. The last version of Python that provided the :mod:`!crypt` module was diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 1548f128b5b..499f6f0ff4e 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1569,8 +1569,6 @@ and are now removed: * :pypi:`bcrypt`: Modern password hashing for your software and your servers. - * :pypi:`passlib`: - Comprehensive password hashing framework supporting over 30 schemes. * :pypi:`argon2-cffi`: The secure Argon2 password hashing algorithm. * :pypi:`legacycrypt`: From f575dd9ef815e79cb359f5466375363f0a5756ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 9 Oct 2025 19:53:42 +0200 Subject: [PATCH 089/373] gh-139842: Clarify `__module__` description in typing.rst (#139863) --- Doc/library/typing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index cfeeb19efbd..279ae3ef820 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2269,7 +2269,7 @@ without the dedicated syntax, as documented below. .. attribute:: __module__ - The module in which the type alias was defined:: + The name of the module in which the type alias was defined:: >>> type Alias = int >>> Alias.__module__ @@ -2449,7 +2449,7 @@ types. .. attribute:: __module__ - The module in which the new type is defined. + The name of the module in which the new type is defined. .. attribute:: __name__ From 34503111fe2724986701799dc994c6b0cb32f4f2 Mon Sep 17 00:00:00 2001 From: adam j hartz Date: Thu, 9 Oct 2025 18:36:40 -0400 Subject: [PATCH 090/373] gh-137025: Update Emscripten Build Docs (#137312) Update Emscripten build docs to point at the devguide as the primary reference for managing an Emscripten build. --- ...-08-01-13-27-43.gh-issue-137025.ubuhQC.rst | 3 + Tools/wasm/README.md | 66 ++----------------- 2 files changed, 9 insertions(+), 60 deletions(-) create mode 100644 Misc/NEWS.d/next/Tools-Demos/2025-08-01-13-27-43.gh-issue-137025.ubuhQC.rst diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-08-01-13-27-43.gh-issue-137025.ubuhQC.rst b/Misc/NEWS.d/next/Tools-Demos/2025-08-01-13-27-43.gh-issue-137025.ubuhQC.rst new file mode 100644 index 00000000000..73c79564006 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2025-08-01-13-27-43.gh-issue-137025.ubuhQC.rst @@ -0,0 +1,3 @@ +The ``wasm_build.py`` script has been removed. ``Tools/wasm/emscripten`` +and ``Tools/wasm/wasi`` should be used instead, as described in the `Dev +Guide `__. diff --git a/Tools/wasm/README.md b/Tools/wasm/README.md index efe9a3550c3..6615f1e2664 100644 --- a/Tools/wasm/README.md +++ b/Tools/wasm/README.md @@ -9,68 +9,14 @@ # Python WebAssembly (WASM) build run in modern browsers and JavaScript runtimes like *Node.js*. WASI builds use WASM runtimes such as *wasmtime*. -Users and developers are encouraged to use the script -`Tools/wasm/wasm_build.py`. The tool automates the build process and provides -assistance with installation of SDKs, running tests, etc. +**NOTE**: If you are looking for general information about WebAssembly that is +not directly related to CPython, please see https://github.com/psf/webassembly. -**NOTE**: If you are looking for information that is not directly related to -building CPython for WebAssembly (or the resulting build), please see -https://github.com/psf/webassembly for more information. - -## wasm32-emscripten +## Emscripten (wasm32-emscripten) ### Build -To cross compile to the ``wasm32-emscripten`` platform you need -[the Emscripten compiler toolchain](https://emscripten.org/), -a Python interpreter, and an installation of Node version 18 or newer. -Emscripten version 4.0.2 is recommended; newer versions may also work, but all -official testing is performed with that version. All commands below are relative -to a checkout of the Python repository. - -#### Install [the Emscripten compiler toolchain](https://emscripten.org/docs/getting_started/downloads.html) - -You can install the Emscripten toolchain as follows: -```shell -git clone https://github.com/emscripten-core/emsdk.git --depth 1 -./emsdk/emsdk install latest -./emsdk/emsdk activate latest -``` -To add the Emscripten compiler to your path: -```shell -source ./emsdk/emsdk_env.sh -``` -This adds `emcc` and `emconfigure` to your path. - -##### Optionally: enable ccache for EMSDK - -The ``EM_COMPILER_WRAPPER`` must be set after the EMSDK environment is -sourced. Otherwise the source script removes the environment variable. - -```shell -export EM_COMPILER_WRAPPER=ccache -``` - -#### Compile and build Python interpreter - -You can use `python Tools/wasm/emscripten` to compile and build targeting -Emscripten. You can do everything at once with: -```shell -python Tools/wasm/emscripten build -``` -or you can break it out into four separate steps: -```shell -python Tools/wasm/emscripten configure-build-python -python Tools/wasm/emscripten make-build-python -python Tools/wasm/emscripten make-libffi -python Tools/wasm/emscripten configure-host -python Tools/wasm/emscripten make-host -``` -Extra arguments to the configure steps are passed along to configure. For -instance, to do a debug build, you can use: -```shell -python Tools/wasm/emscripten build --with-py-debug -``` +See [the devguide instructions for building for Emscripten](https://devguide.python.org/getting-started/setup-building/#emscripten). ### Running from node @@ -97,8 +43,8 @@ ### Running tests ### The Web Example -When building for Emscripten, the web example will be built automatically. It -is in the ``web_example`` directory. To run the web example, ``cd`` into the +When building for Emscripten, a small web example will be built automatically +in the ``web_example`` directory. To run the web example, ``cd`` into the ``web_example`` directory, then run ``python server.py``. This will start a web server; you can then visit ``http://localhost:8000/`` in a browser to see a simple REPL example. From 744ec1d6c395f7a443ba4609d6023f86c245db58 Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Fri, 10 Oct 2025 08:08:51 +0300 Subject: [PATCH 091/373] gh-138614: `site._get_path` to respect non-default implementation name (#138610) * `site._get_path` to respect non-default implementation name * Add news entry * Remove NEWS entry --- Lib/site.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/site.py b/Lib/site.py index f9327197159..f0e74eeee2f 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -333,7 +333,7 @@ def _get_path(userbase): if sys.platform == 'darwin' and sys._framework: return f'{userbase}/lib/{implementation_lower}/site-packages' - return f'{userbase}/lib/python{version[0]}.{version[1]}{abi_thread}/site-packages' + return f'{userbase}/lib/{implementation_lower}{version[0]}.{version[1]}{abi_thread}/site-packages' def getuserbase(): From 33f32d6740810b7b97e311fe9790a4fa4b4920dc Mon Sep 17 00:00:00 2001 From: partev Date: Fri, 10 Oct 2025 01:38:13 -0400 Subject: [PATCH 092/373] Replace obsolete platforms with more recent examples (#132455) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Filipe Laíns 🇵🇸 --- Doc/library/sysconfig.rst | 3 +++ Lib/sysconfig/__init__.py | 3 +++ Tools/c-analyzer/distutils/util.py | 4 ++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Doc/library/sysconfig.rst b/Doc/library/sysconfig.rst index 532facb45f8..3b0bfb85da7 100644 --- a/Doc/library/sysconfig.rst +++ b/Doc/library/sysconfig.rst @@ -382,6 +382,9 @@ Other functions Examples of returned values: + - linux-x86_64 + - linux-aarch64 + - solaris-2.6-sun4u Windows: diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index 7374bde223e..8ff9c99435b 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -645,6 +645,9 @@ def get_platform(): isn't particularly important. Examples of returned values: + linux-x86_64 + linux-aarch64 + solaris-2.6-sun4u Windows: diff --git a/Tools/c-analyzer/distutils/util.py b/Tools/c-analyzer/distutils/util.py index 89ca094336f..f687a28ec2f 100644 --- a/Tools/c-analyzer/distutils/util.py +++ b/Tools/c-analyzer/distutils/util.py @@ -19,8 +19,8 @@ def get_host_platform(): particularly important. Examples of returned values: - linux-i586 - linux-alpha (?) + linux-x86_64 + linux-aarch64 solaris-2.6-sun4u Windows will return one of: From 9e15770d621780bf9f368f4a58379f9ff4a173fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Fri, 10 Oct 2025 06:52:13 +0100 Subject: [PATCH 093/373] GH-78870: copy test from GH-20439 (#139884) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- Lib/test/test_sysconfig.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index 59ea623bb92..8101657b04a 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -770,6 +770,7 @@ def test_parse_makefile(self): print("var8=$$(var3)", file=makefile) print("var9=$(var10)(var3)", file=makefile) print("var10=$$", file=makefile) + print("var11=$${ORIGIN}${var5}", file=makefile) vars = _parse_makefile(TESTFN) self.assertEqual(vars, { 'var1': 'ab42', @@ -782,6 +783,7 @@ def test_parse_makefile(self): 'var8': '$(var3)', 'var9': '$(var3)', 'var10': '$', + 'var11': '${ORIGIN}dollar$5', }) def _test_parse_makefile_recursion(self): From 1f87d528a1fb999ff9cdb411a1ccc3f8ee674293 Mon Sep 17 00:00:00 2001 From: Weilin Du <108666168+LamentXU123@users.noreply.github.com> Date: Fri, 10 Oct 2025 14:24:41 +0800 Subject: [PATCH 094/373] gh-139843: Document `signal.SIGQUIT` to fix Sphinx references (#139844) --- Doc/library/signal.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Doc/library/signal.rst b/Doc/library/signal.rst index 66f31b28da2..0c681c795c1 100644 --- a/Doc/library/signal.rst +++ b/Doc/library/signal.rst @@ -205,6 +205,12 @@ The variables defined in the :mod:`signal` module are: .. availability:: Unix. +.. data:: SIGQUIT + + Terminal quit signal. + + .. availability:: Unix. + .. data:: SIGSEGV Segmentation fault: invalid memory reference. From 8f14bddeae8935817166c4f594466f42c9f14139 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Fri, 10 Oct 2025 07:48:09 +0100 Subject: [PATCH 095/373] gh-139823: Extend list of optional dependencies in `configure.rst` (#139826) Co-authored-by: Emma Smith Co-authored-by: Victor Stinner --- Doc/using/configure.rst | 43 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index b05e0600114..01537951aeb 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -22,16 +22,51 @@ Features and minimum versions required to build CPython: * Support for threads. -* OpenSSL 1.1.1 is the minimum version and OpenSSL 3.0.18 is the recommended - minimum version for the :mod:`ssl` and :mod:`hashlib` extension modules. +To build optional modules: -* SQLite 3.15.2 for the :mod:`sqlite3` extension module. +* `libbz2 `_ for the :mod:`bz2` module. -* Tcl/Tk 8.5.12 for the :mod:`tkinter` module. +* `libb2 `_ (:ref:`BLAKE2 `), + used by :mod:`hashlib` module. + +* `libffi `_ 3.3.0 is the recommended + minimum version for the :mod:`ctypes` module. + +* ``liblzma``, for the :mod:`lzma` module. * `libmpdec `_ 2.5.0 for the :mod:`decimal` module. +* ``libncurses`` or ``libncursesw``, + for the :mod:`curses` module. + +* ``libpanel`` or ``libpanelw``, + for the :mod:`curses.panel` module. + +* `libreadline `_ or + `libedit `_ + for the :mod:`readline` module. + +* `libuuid `_, for the :mod:`uuid` module. + +* `OpenSSL `_ 1.1.1 is the minimum version and + OpenSSL 3.0.18 is the recommended minimum version for the + :mod:`ssl` and :mod:`hashlib` extension modules. + +* `SQLite `_ 3.15.2 for the :mod:`sqlite3` extension module. + +* `Tcl/Tk `_ 8.5.12 for the :mod:`tkinter` module. + +* `zlib `_ 1.1.4 is the reccomended minimum version for the + :mod:`zlib` module. + +* `zstd `_ 1.4.5 is the minimum version for + the :mod:`compression.zstd` module. + +For a full list of dependencies required to build all modules and how to install +them, see the +`devguide `_. + * Autoconf 2.72 and aclocal 1.16.5 are required to regenerate the :file:`configure` script. From e31c22dbf9b35bdc6b63871f2ca9a0a0d6786b28 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 10 Oct 2025 08:54:12 +0200 Subject: [PATCH 096/373] gh-111489: Add PyTuple_FromArray() function (#139691) --- Doc/c-api/tuple.rst | 13 +++++++++ Doc/whatsnew/3.15.rst | 3 ++ Include/cpython/tupleobject.h | 4 +++ Include/internal/pycore_tuple.h | 4 ++- Lib/test/test_capi/test_tuple.py | 22 +++++++++++++++ ...-10-07-12-51-32.gh-issue-111489.LCKKlg.rst | 2 ++ Modules/_testcapi/tuple.c | 28 +++++++++++++++++++ Objects/tupleobject.c | 2 +- 8 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2025-10-07-12-51-32.gh-issue-111489.LCKKlg.rst diff --git a/Doc/c-api/tuple.rst b/Doc/c-api/tuple.rst index 815afddad19..65f8334c437 100644 --- a/Doc/c-api/tuple.rst +++ b/Doc/c-api/tuple.rst @@ -37,6 +37,19 @@ Tuple Objects or ``NULL`` with an exception set on failure. +.. c:function:: PyObject* PyTuple_FromArray(PyObject *const *array, Py_ssize_t size) + + Create a tuple of *size* items and copy references from *array* to the new + tuple. + + *array* can be NULL if *size* is ``0``. + + On success, return a new reference. + On error, set an exception and return ``NULL``. + + .. versionadded:: next + + .. c:function:: PyObject* PyTuple_Pack(Py_ssize_t n, ...) Return a new tuple object of size *n*, diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 4b176d6c8e6..40286d4fe85 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -852,6 +852,9 @@ New features (Contributed by Victor Stinner in :gh:`129813`.) +* Add :c:func:`PyTuple_FromArray` to create a :class:`tuple` from an array. + (Contributed by Victor Stinner in :gh:`111489`.) + Porting to Python 3.15 ---------------------- diff --git a/Include/cpython/tupleobject.h b/Include/cpython/tupleobject.h index afb98ccbb81..888baaf3358 100644 --- a/Include/cpython/tupleobject.h +++ b/Include/cpython/tupleobject.h @@ -38,3 +38,7 @@ PyTuple_SET_ITEM(PyObject *op, Py_ssize_t index, PyObject *value) { } #define PyTuple_SET_ITEM(op, index, value) \ PyTuple_SET_ITEM(_PyObject_CAST(op), (index), _PyObject_CAST(value)) + +PyAPI_FUNC(PyObject*) PyTuple_FromArray( + PyObject *const *array, + Py_ssize_t size); diff --git a/Include/internal/pycore_tuple.h b/Include/internal/pycore_tuple.h index acf1bec4602..be1961cbf77 100644 --- a/Include/internal/pycore_tuple.h +++ b/Include/internal/pycore_tuple.h @@ -23,7 +23,9 @@ extern PyStatus _PyTuple_InitGlobalObjects(PyInterpreterState *); #define _PyTuple_ITEMS(op) _Py_RVALUE(_PyTuple_CAST(op)->ob_item) -PyAPI_FUNC(PyObject *)_PyTuple_FromArray(PyObject *const *, Py_ssize_t); +// Alias for backward compatibility +#define _PyTuple_FromArray PyTuple_FromArray + PyAPI_FUNC(PyObject *)_PyTuple_FromStackRefStealOnSuccess(const union _PyStackRef *, Py_ssize_t); PyAPI_FUNC(PyObject *)_PyTuple_FromArraySteal(PyObject *const *, Py_ssize_t); diff --git a/Lib/test/test_capi/test_tuple.py b/Lib/test/test_capi/test_tuple.py index 7c07bc64e24..b6d6da008d0 100644 --- a/Lib/test/test_capi/test_tuple.py +++ b/Lib/test/test_capi/test_tuple.py @@ -62,6 +62,28 @@ def test_tuple_new(self): self.assertRaises(SystemError, tuple_new, PY_SSIZE_T_MIN) self.assertRaises(MemoryError, tuple_new, PY_SSIZE_T_MAX) + def test_tuple_fromarray(self): + # Test PyTuple_FromArray() + tuple_fromarray = _testcapi.tuple_fromarray + + tup = tuple([i] for i in range(5)) + copy = tuple_fromarray(tup) + self.assertEqual(copy, tup) + + tup = () + copy = tuple_fromarray(tup) + self.assertIs(copy, tup) + + copy = tuple_fromarray(NULL, 0) + self.assertIs(copy, ()) + + with self.assertRaises(SystemError): + tuple_fromarray(NULL, -1) + with self.assertRaises(SystemError): + tuple_fromarray(NULL, PY_SSIZE_T_MIN) + with self.assertRaises(MemoryError): + tuple_fromarray(NULL, PY_SSIZE_T_MAX) + def test_tuple_pack(self): # Test PyTuple_Pack() pack = _testlimitedcapi.tuple_pack diff --git a/Misc/NEWS.d/next/C_API/2025-10-07-12-51-32.gh-issue-111489.LCKKlg.rst b/Misc/NEWS.d/next/C_API/2025-10-07-12-51-32.gh-issue-111489.LCKKlg.rst new file mode 100644 index 00000000000..9c044f7796b --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2025-10-07-12-51-32.gh-issue-111489.LCKKlg.rst @@ -0,0 +1,2 @@ +Add :c:func:`PyTuple_FromArray` to create a :class:`tuple` from an array. +Patch by Victor Stinner. diff --git a/Modules/_testcapi/tuple.c b/Modules/_testcapi/tuple.c index d9c02ba0ff0..5de1c494c0a 100644 --- a/Modules/_testcapi/tuple.c +++ b/Modules/_testcapi/tuple.c @@ -104,12 +104,40 @@ _check_tuple_item_is_NULL(PyObject *Py_UNUSED(module), PyObject *args) } +static PyObject * +tuple_fromarray(PyObject* Py_UNUSED(module), PyObject *args) +{ + PyObject *src; + Py_ssize_t size = UNINITIALIZED_SIZE; + if (!PyArg_ParseTuple(args, "O|n", &src, &size)) { + return NULL; + } + if (src != Py_None && !PyTuple_Check(src)) { + PyErr_SetString(PyExc_TypeError, "expect a tuple"); + return NULL; + } + + PyObject **items; + if (src != Py_None) { + items = &PyTuple_GET_ITEM(src, 0); + if (size == UNINITIALIZED_SIZE) { + size = PyTuple_GET_SIZE(src); + } + } + else { + items = NULL; + } + return PyTuple_FromArray(items, size); +} + + static PyMethodDef test_methods[] = { {"tuple_get_size", tuple_get_size, METH_O}, {"tuple_get_item", tuple_get_item, METH_VARARGS}, {"tuple_set_item", tuple_set_item, METH_VARARGS}, {"_tuple_resize", _tuple_resize, METH_VARARGS}, {"_check_tuple_item_is_NULL", _check_tuple_item_is_NULL, METH_VARARGS}, + {"tuple_fromarray", tuple_fromarray, METH_VARARGS}, {NULL}, }; diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 9b31758485c..1fa4bae638a 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -366,7 +366,7 @@ tuple_item(PyObject *op, Py_ssize_t i) } PyObject * -_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n) +PyTuple_FromArray(PyObject *const *src, Py_ssize_t n) { if (n == 0) { return tuple_get_empty(); From f4104f5d74b99712253fceb39a4460ee3f7a281c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 10 Oct 2025 10:51:24 +0300 Subject: [PATCH 097/373] gh-139783: Fix inspect.getsourcelines() for the case when a decorator is followed by a comment or an empty line (GH-139836) --- Lib/inspect.py | 4 +++- Lib/test/test_inspect/inspect_fodder2.py | 12 ++++++++++++ Lib/test/test_inspect/test_inspect.py | 4 ++++ .../2025-10-09-13-48-28.gh-issue-139783.__NUgo.rst | 2 ++ 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-09-13-48-28.gh-issue-139783.__NUgo.rst diff --git a/Lib/inspect.py b/Lib/inspect.py index b345623b3fa..bb22bab3040 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1065,7 +1065,9 @@ def __init__(self): def tokeneater(self, type, token, srowcol, erowcol, line): if not self.started and not self.indecorator: - if type == tokenize.INDENT or token == "async": + if type in (tokenize.INDENT, tokenize.COMMENT, tokenize.NL): + pass + elif token == "async": pass # skip any decorators elif token == "@": diff --git a/Lib/test/test_inspect/inspect_fodder2.py b/Lib/test/test_inspect/inspect_fodder2.py index 1de283f672d..157e12167b5 100644 --- a/Lib/test/test_inspect/inspect_fodder2.py +++ b/Lib/test/test_inspect/inspect_fodder2.py @@ -388,4 +388,16 @@ def func383(): ) return ge385 +# line 391 +@decorator +# comment +def func394(): + return 395 + +# line 397 +@decorator + +def func400(): + return 401 + pass # end of file diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index e32e34c63b5..d42f2dbff99 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -1223,6 +1223,10 @@ def test_generator_expression(self): self.assertSourceEqual(next(mod2.ge377), 377, 380) self.assertSourceEqual(next(mod2.func383()), 385, 388) + def test_comment_or_empty_line_after_decorator(self): + self.assertSourceEqual(mod2.func394, 392, 395) + self.assertSourceEqual(mod2.func400, 398, 401) + class TestNoEOL(GetSourceBase): def setUp(self): diff --git a/Misc/NEWS.d/next/Library/2025-10-09-13-48-28.gh-issue-139783.__NUgo.rst b/Misc/NEWS.d/next/Library/2025-10-09-13-48-28.gh-issue-139783.__NUgo.rst new file mode 100644 index 00000000000..336653e73bf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-09-13-48-28.gh-issue-139783.__NUgo.rst @@ -0,0 +1,2 @@ +Fix :func:`inspect.getsourcelines` for the case when a decorator is followed +by a comment or an empty line. From 7cafd76a7fc6e52d93dd11e454a49c4dbebdf080 Mon Sep 17 00:00:00 2001 From: Nadeshiko Manju Date: Fri, 10 Oct 2025 16:56:10 +0800 Subject: [PATCH 098/373] gh-139184: Set O_CLOEXEC for master_fd when calling os.forkpty() (#139408) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manjusaka Co-authored-by: Shamil Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/os.rst | 5 +++++ Doc/library/pty.rst | 5 +++++ Lib/test/test_pty.py | 1 + .../2025-09-29-14-15-20.gh-issue-139184.dNl9O4.rst | 1 + Modules/clinic/posixmodule.c.h | 5 +++-- Modules/posixmodule.c | 10 +++++++++- 6 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-09-29-14-15-20.gh-issue-139184.dNl9O4.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index ba3d1894549..540eaa09d0e 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -4592,6 +4592,8 @@ written in Python, such as a mail server's external command delivery program. master end of the pseudo-terminal. For a more portable approach, use the :mod:`pty` module. If an error occurs :exc:`OSError` is raised. + The returned file descriptor *fd* is :ref:`non-inheritable `. + .. audit-event:: os.forkpty "" os.forkpty .. warning:: @@ -4608,6 +4610,9 @@ written in Python, such as a mail server's external command delivery program. threads, this now raises a :exc:`DeprecationWarning`. See the longer explanation on :func:`os.fork`. + .. versionchanged:: next + The returned file descriptor is now made non-inheritable. + .. availability:: Unix, not WASI, not Android, not iOS. diff --git a/Doc/library/pty.rst b/Doc/library/pty.rst index 1a44bb13a84..9fef8760b62 100644 --- a/Doc/library/pty.rst +++ b/Doc/library/pty.rst @@ -33,9 +33,14 @@ The :mod:`pty` module defines the following functions: file descriptor connected to the child's controlling terminal (and also to the child's standard input and output). + The returned file descriptor *fd* is :ref:`non-inheritable `. + .. warning:: On macOS the use of this function is unsafe when mixed with using higher-level system APIs, and that includes using :mod:`urllib.request`. + .. versionchanged:: next + The returned file descriptor is now made non-inheritable. + .. function:: openpty() diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index fbba7025ac4..a2018e86444 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -230,6 +230,7 @@ def test_fork(self): os._exit(2) os._exit(4) else: + self.assertFalse(os.get_inheritable(master_fd)) debug("Waiting for child (%d) to finish." % pid) # In verbose mode, we have to consume the debug output from the # child or the child will block, causing this test to hang in the diff --git a/Misc/NEWS.d/next/Library/2025-09-29-14-15-20.gh-issue-139184.dNl9O4.rst b/Misc/NEWS.d/next/Library/2025-09-29-14-15-20.gh-issue-139184.dNl9O4.rst new file mode 100644 index 00000000000..d50cdeaadc4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-29-14-15-20.gh-issue-139184.dNl9O4.rst @@ -0,0 +1 @@ +:func:`os.forkpty` does now make the returned file descriptor non-inheritable. diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index dddf98d127c..3d9863ad179 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -5035,7 +5035,8 @@ PyDoc_STRVAR(os_forkpty__doc__, "Returns a tuple of (pid, master_fd).\n" "Like fork(), return pid of 0 to the child process,\n" "and pid of child to the parent process.\n" -"To both, return fd of newly opened pseudo-terminal."); +"To both, return fd of newly opened pseudo-terminal.\n" +"The master_fd is non-inheritable."); #define OS_FORKPTY_METHODDEF \ {"forkpty", (PyCFunction)os_forkpty, METH_NOARGS, os_forkpty__doc__}, @@ -13446,4 +13447,4 @@ exit: #ifndef OS__EMSCRIPTEN_LOG_METHODDEF #define OS__EMSCRIPTEN_LOG_METHODDEF #endif /* !defined(OS__EMSCRIPTEN_LOG_METHODDEF) */ -/*[clinic end generated code: output=b5b370c499174f85 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=47ace1528820858b input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 4189d300856..7a2e36bf294 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9018,11 +9018,12 @@ Returns a tuple of (pid, master_fd). Like fork(), return pid of 0 to the child process, and pid of child to the parent process. To both, return fd of newly opened pseudo-terminal. +The master_fd is non-inheritable. [clinic start generated code]*/ static PyObject * os_forkpty_impl(PyObject *module) -/*[clinic end generated code: output=60d0a5c7512e4087 input=f1f7f4bae3966010]*/ +/*[clinic end generated code: output=60d0a5c7512e4087 input=24765e0f33275b3b]*/ { int master_fd = -1; pid_t pid; @@ -9048,6 +9049,12 @@ os_forkpty_impl(PyObject *module) } else { /* parent: release the import lock. */ PyOS_AfterFork_Parent(); + /* set O_CLOEXEC on master_fd */ + if (_Py_set_inheritable(master_fd, 0, NULL) < 0) { + PyErr_FormatUnraisable("Exception ignored when setting master_fd " + "non-inheritable in forkpty()"); + } + // After PyOS_AfterFork_Parent() starts the world to avoid deadlock. if (warn_about_fork_with_threads("forkpty") < 0) return NULL; @@ -9055,6 +9062,7 @@ os_forkpty_impl(PyObject *module) if (pid == -1) { return posix_error(); } + return Py_BuildValue("(Ni)", PyLong_FromPid(pid), master_fd); } #endif /* HAVE_FORKPTY */ From 4c119714d58912b10cb803a813da0364a0cc01e8 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 10 Oct 2025 12:52:59 +0200 Subject: [PATCH 099/373] gh-139353: Add Objects/unicode_format.c file (#139491) * Move PyUnicode_Format() implementation from unicodeobject.c to unicode_format.c. * Replace unicode_modifiable() with _PyUnicode_IsModifiable() * Add empty lines to have two empty lines between functions. --- Include/internal/pycore_unicodeobject.h | 26 + Makefile.pre.in | 4 +- Objects/unicode_format.c | 1002 +++++++++++++++++++++++ Objects/unicodeobject.c | 980 +--------------------- PCbuild/_freeze_module.vcxproj | 1 + PCbuild/_freeze_module.vcxproj.filters | 3 + PCbuild/pythoncore.vcxproj | 1 + PCbuild/pythoncore.vcxproj.filters | 3 + 8 files changed, 1047 insertions(+), 973 deletions(-) create mode 100644 Objects/unicode_format.c diff --git a/Include/internal/pycore_unicodeobject.h b/Include/internal/pycore_unicodeobject.h index f1c9bcd4788..b83039c1869 100644 --- a/Include/internal/pycore_unicodeobject.h +++ b/Include/internal/pycore_unicodeobject.h @@ -11,10 +11,14 @@ extern "C" { #include "pycore_fileutils.h" // _Py_error_handler #include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI + // Maximum code point of Unicode 6.0: 0x10ffff (1,114,111). #define _Py_MAX_UNICODE 0x10ffff +extern int _PyUnicode_IsModifiable(PyObject *unicode); + + static inline void _PyUnicode_Fill(int kind, void *data, Py_UCS4 value, Py_ssize_t start, Py_ssize_t length) @@ -48,6 +52,28 @@ _PyUnicode_Fill(int kind, void *data, Py_UCS4 value, } } +static inline int +_PyUnicode_EnsureUnicode(PyObject *obj) +{ + if (!PyUnicode_Check(obj)) { + PyErr_Format(PyExc_TypeError, + "must be str, not %T", obj); + return -1; + } + return 0; +} + +static inline int +_PyUnicodeWriter_WriteCharInline(_PyUnicodeWriter *writer, Py_UCS4 ch) +{ + assert(ch <= _Py_MAX_UNICODE); + if (_PyUnicodeWriter_Prepare(writer, 1, ch) < 0) + return -1; + PyUnicode_WRITE(writer->kind, writer->data, writer->pos, ch); + writer->pos++; + return 0; +} + /* --- Characters Type APIs ----------------------------------------------- */ diff --git a/Makefile.pre.in b/Makefile.pre.in index a5223246845..19423c11545 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -557,9 +557,10 @@ OBJECT_OBJS= \ Objects/tupleobject.o \ Objects/typeobject.o \ Objects/typevarobject.o \ + Objects/unicode_format.o \ Objects/unicode_formatter.o \ - Objects/unicodeobject.o \ Objects/unicodectype.o \ + Objects/unicodeobject.o \ Objects/unionobject.o \ Objects/weakrefobject.o \ @PERF_TRAMPOLINE_OBJ@ @@ -2105,6 +2106,7 @@ Objects/bytes_methods.o: $(srcdir)/Objects/bytes_methods.c $(BYTESTR_DEPS) Objects/bytesobject.o: $(srcdir)/Objects/bytesobject.c $(BYTESTR_DEPS) Objects/bytearrayobject.o: $(srcdir)/Objects/bytearrayobject.c $(BYTESTR_DEPS) +Objects/unicode_format.o: $(srcdir)/Objects/unicode_format.c $(UNICODE_DEPS) Objects/unicodeobject.o: $(srcdir)/Objects/unicodeobject.c $(UNICODE_DEPS) Objects/dictobject.o: $(srcdir)/Objects/stringlib/eq.h diff --git a/Objects/unicode_format.c b/Objects/unicode_format.c new file mode 100644 index 00000000000..26bdae55d8b --- /dev/null +++ b/Objects/unicode_format.c @@ -0,0 +1,1002 @@ +/* + +Unicode implementation based on original code by Fredrik Lundh, +modified by Marc-Andre Lemburg . + +Major speed upgrades to the method implementations at the Reykjavik +NeedForSpeed sprint, by Fredrik Lundh and Andrew Dalke. + +Copyright (c) Corporation for National Research Initiatives. + +-------------------------------------------------------------------- +The original string type implementation is: + + Copyright (c) 1999 by Secret Labs AB + Copyright (c) 1999 by Fredrik Lundh + +By obtaining, using, and/or copying this software and/or its +associated documentation, you agree that you have read, understood, +and will comply with the following terms and conditions: + +Permission to use, copy, modify, and distribute this software and its +associated documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies, and that both that copyright notice and this permission notice +appear in supporting documentation, and that the name of Secret Labs +AB or the author not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR +ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +-------------------------------------------------------------------- + +*/ + +// PyUnicode_Format() implementation + +#include "Python.h" +#include "pycore_abstract.h" // _PyIndex_Check() +#include "pycore_format.h" // F_ALT +#include "pycore_long.h" // _PyLong_FormatWriter() +#include "pycore_object.h" // _PyObject_IsUniquelyReferenced() +#include "pycore_unicodeobject.h" // _Py_MAX_UNICODE + + +#define MAX_UNICODE _Py_MAX_UNICODE +#define ensure_unicode _PyUnicode_EnsureUnicode + +struct unicode_formatter_t { + PyObject *args; + int args_owned; + Py_ssize_t arglen, argidx; + PyObject *dict; + + int fmtkind; + Py_ssize_t fmtcnt, fmtpos; + const void *fmtdata; + PyObject *fmtstr; + + _PyUnicodeWriter writer; +}; + + +struct unicode_format_arg_t { + Py_UCS4 ch; + int flags; + Py_ssize_t width; + int prec; + int sign; +}; + + +static PyObject * +unicode_format_getnextarg(struct unicode_formatter_t *ctx) +{ + Py_ssize_t argidx = ctx->argidx; + + if (argidx < ctx->arglen) { + ctx->argidx++; + if (ctx->arglen < 0) + return ctx->args; + else + return PyTuple_GetItem(ctx->args, argidx); + } + PyErr_SetString(PyExc_TypeError, + "not enough arguments for format string"); + return NULL; +} + + +/* Returns a new reference to a PyUnicode object, or NULL on failure. */ + +/* Format a float into the writer if the writer is not NULL, or into *p_output + otherwise. + + Return 0 on success, raise an exception and return -1 on error. */ +static int +formatfloat(PyObject *v, struct unicode_format_arg_t *arg, + PyObject **p_output, + _PyUnicodeWriter *writer) +{ + char *p; + double x; + Py_ssize_t len; + int prec; + int dtoa_flags = 0; + + x = PyFloat_AsDouble(v); + if (x == -1.0 && PyErr_Occurred()) + return -1; + + prec = arg->prec; + if (prec < 0) + prec = 6; + + if (arg->flags & F_ALT) + dtoa_flags |= Py_DTSF_ALT; + p = PyOS_double_to_string(x, arg->ch, prec, dtoa_flags, NULL); + if (p == NULL) + return -1; + len = strlen(p); + if (writer) { + if (_PyUnicodeWriter_WriteASCIIString(writer, p, len) < 0) { + PyMem_Free(p); + return -1; + } + } + else + *p_output = _PyUnicode_FromASCII(p, len); + PyMem_Free(p); + return 0; +} + + +/* formatlong() emulates the format codes d, u, o, x and X, and + * the F_ALT flag, for Python's long (unbounded) ints. It's not used for + * Python's regular ints. + * Return value: a new PyUnicodeObject*, or NULL if error. + * The output string is of the form + * "-"? ("0x" | "0X")? digit+ + * "0x"/"0X" are present only for x and X conversions, with F_ALT + * set in flags. The case of hex digits will be correct, + * There will be at least prec digits, zero-filled on the left if + * necessary to get that many. + * val object to be converted + * flags bitmask of format flags; only F_ALT is looked at + * prec minimum number of digits; 0-fill on left if needed + * type a character in [duoxX]; u acts the same as d + * + * CAUTION: o, x and X conversions on regular ints can never + * produce a '-' sign, but can for Python's unbounded ints. + */ +PyObject * +_PyUnicode_FormatLong(PyObject *val, int alt, int prec, int type) +{ + PyObject *result = NULL; + char *buf; + Py_ssize_t i; + int sign; /* 1 if '-', else 0 */ + int len; /* number of characters */ + Py_ssize_t llen; + int numdigits; /* len == numnondigits + numdigits */ + int numnondigits = 0; + + /* Avoid exceeding SSIZE_T_MAX */ + if (prec > INT_MAX-3) { + PyErr_SetString(PyExc_OverflowError, + "precision too large"); + return NULL; + } + + assert(PyLong_Check(val)); + + switch (type) { + default: + Py_UNREACHABLE(); + case 'd': + case 'i': + case 'u': + /* int and int subclasses should print numerically when a numeric */ + /* format code is used (see issue18780) */ + result = PyNumber_ToBase(val, 10); + break; + case 'o': + numnondigits = 2; + result = PyNumber_ToBase(val, 8); + break; + case 'x': + case 'X': + numnondigits = 2; + result = PyNumber_ToBase(val, 16); + break; + } + if (!result) + return NULL; + + assert(_PyUnicode_IsModifiable(result)); + assert(PyUnicode_IS_ASCII(result)); + + /* To modify the string in-place, there can only be one reference. */ + if (!_PyObject_IsUniquelyReferenced(result)) { + Py_DECREF(result); + PyErr_BadInternalCall(); + return NULL; + } + buf = PyUnicode_DATA(result); + llen = PyUnicode_GET_LENGTH(result); + if (llen > INT_MAX) { + Py_DECREF(result); + PyErr_SetString(PyExc_ValueError, + "string too large in _PyUnicode_FormatLong"); + return NULL; + } + len = (int)llen; + sign = buf[0] == '-'; + numnondigits += sign; + numdigits = len - numnondigits; + assert(numdigits > 0); + + /* Get rid of base marker unless F_ALT */ + if (((alt) == 0 && + (type == 'o' || type == 'x' || type == 'X'))) { + assert(buf[sign] == '0'); + assert(buf[sign+1] == 'x' || buf[sign+1] == 'X' || + buf[sign+1] == 'o'); + numnondigits -= 2; + buf += 2; + len -= 2; + if (sign) + buf[0] = '-'; + assert(len == numnondigits + numdigits); + assert(numdigits > 0); + } + + /* Fill with leading zeroes to meet minimum width. */ + if (prec > numdigits) { + PyObject *r1 = PyBytes_FromStringAndSize(NULL, + numnondigits + prec); + char *b1; + if (!r1) { + Py_DECREF(result); + return NULL; + } + b1 = PyBytes_AS_STRING(r1); + for (i = 0; i < numnondigits; ++i) + *b1++ = *buf++; + for (i = 0; i < prec - numdigits; i++) + *b1++ = '0'; + for (i = 0; i < numdigits; i++) + *b1++ = *buf++; + *b1 = '\0'; + Py_SETREF(result, r1); + buf = PyBytes_AS_STRING(result); + len = numnondigits + prec; + } + + /* Fix up case for hex conversions. */ + if (type == 'X') { + /* Need to convert all lower case letters to upper case. + and need to convert 0x to 0X (and -0x to -0X). */ + for (i = 0; i < len; i++) + if (buf[i] >= 'a' && buf[i] <= 'x') + buf[i] -= 'a'-'A'; + } + if (!PyUnicode_Check(result) + || buf != PyUnicode_DATA(result)) { + PyObject *unicode; + unicode = _PyUnicode_FromASCII(buf, len); + Py_SETREF(result, unicode); + } + else if (len != PyUnicode_GET_LENGTH(result)) { + if (PyUnicode_Resize(&result, len) < 0) + Py_CLEAR(result); + } + return result; +} + + +/* Format an integer or a float as an integer. + * Return 1 if the number has been formatted into the writer, + * 0 if the number has been formatted into *p_output + * -1 and raise an exception on error */ +static int +mainformatlong(PyObject *v, + struct unicode_format_arg_t *arg, + PyObject **p_output, + _PyUnicodeWriter *writer) +{ + PyObject *iobj, *res; + char type = (char)arg->ch; + + if (!PyNumber_Check(v)) + goto wrongtype; + + /* make sure number is a type of integer for o, x, and X */ + if (!PyLong_Check(v)) { + if (type == 'o' || type == 'x' || type == 'X') { + iobj = _PyNumber_Index(v); + } + else { + iobj = PyNumber_Long(v); + } + if (iobj == NULL ) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) + goto wrongtype; + return -1; + } + assert(PyLong_Check(iobj)); + } + else { + iobj = Py_NewRef(v); + } + + if (PyLong_CheckExact(v) + && arg->width == -1 && arg->prec == -1 + && !(arg->flags & (F_SIGN | F_BLANK)) + && type != 'X') + { + /* Fast path */ + int alternate = arg->flags & F_ALT; + int base; + + switch(type) + { + default: + Py_UNREACHABLE(); + case 'd': + case 'i': + case 'u': + base = 10; + break; + case 'o': + base = 8; + break; + case 'x': + case 'X': + base = 16; + break; + } + + if (_PyLong_FormatWriter(writer, v, base, alternate) == -1) { + Py_DECREF(iobj); + return -1; + } + Py_DECREF(iobj); + return 1; + } + + res = _PyUnicode_FormatLong(iobj, arg->flags & F_ALT, arg->prec, type); + Py_DECREF(iobj); + if (res == NULL) + return -1; + *p_output = res; + return 0; + +wrongtype: + switch(type) + { + case 'o': + case 'x': + case 'X': + PyErr_Format(PyExc_TypeError, + "%%%c format: an integer is required, " + "not %.200s", + type, Py_TYPE(v)->tp_name); + break; + default: + PyErr_Format(PyExc_TypeError, + "%%%c format: a real number is required, " + "not %.200s", + type, Py_TYPE(v)->tp_name); + break; + } + return -1; +} + + +static Py_UCS4 +formatchar(PyObject *v) +{ + /* presume that the buffer is at least 3 characters long */ + if (PyUnicode_Check(v)) { + if (PyUnicode_GET_LENGTH(v) == 1) { + return PyUnicode_READ_CHAR(v, 0); + } + PyErr_Format(PyExc_TypeError, + "%%c requires an int or a unicode character, " + "not a string of length %zd", + PyUnicode_GET_LENGTH(v)); + return (Py_UCS4) -1; + } + else { + int overflow; + long x = PyLong_AsLongAndOverflow(v, &overflow); + if (x == -1 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_Format(PyExc_TypeError, + "%%c requires an int or a unicode character, not %T", + v); + return (Py_UCS4) -1; + } + return (Py_UCS4) -1; + } + + if (x < 0 || x > MAX_UNICODE) { + /* this includes an overflow in converting to C long */ + PyErr_SetString(PyExc_OverflowError, + "%c arg not in range(0x110000)"); + return (Py_UCS4) -1; + } + + return (Py_UCS4) x; + } +} + + +/* Parse options of an argument: flags, width, precision. + Handle also "%(name)" syntax. + + Return 0 if the argument has been formatted into arg->str. + Return 1 if the argument has been written into ctx->writer, + Raise an exception and return -1 on error. */ +static int +unicode_format_arg_parse(struct unicode_formatter_t *ctx, + struct unicode_format_arg_t *arg) +{ +#define FORMAT_READ(ctx) \ + PyUnicode_READ((ctx)->fmtkind, (ctx)->fmtdata, (ctx)->fmtpos) + + PyObject *v; + + if (arg->ch == '(') { + /* Get argument value from a dictionary. Example: "%(name)s". */ + Py_ssize_t keystart; + Py_ssize_t keylen; + PyObject *key; + int pcount = 1; + + if (ctx->dict == NULL) { + PyErr_SetString(PyExc_TypeError, + "format requires a mapping"); + return -1; + } + ++ctx->fmtpos; + --ctx->fmtcnt; + keystart = ctx->fmtpos; + /* Skip over balanced parentheses */ + while (pcount > 0 && --ctx->fmtcnt >= 0) { + arg->ch = FORMAT_READ(ctx); + if (arg->ch == ')') + --pcount; + else if (arg->ch == '(') + ++pcount; + ctx->fmtpos++; + } + keylen = ctx->fmtpos - keystart - 1; + if (ctx->fmtcnt < 0 || pcount > 0) { + PyErr_SetString(PyExc_ValueError, + "incomplete format key"); + return -1; + } + key = PyUnicode_Substring(ctx->fmtstr, + keystart, keystart + keylen); + if (key == NULL) + return -1; + if (ctx->args_owned) { + ctx->args_owned = 0; + Py_DECREF(ctx->args); + } + ctx->args = PyObject_GetItem(ctx->dict, key); + Py_DECREF(key); + if (ctx->args == NULL) + return -1; + ctx->args_owned = 1; + ctx->arglen = -1; + ctx->argidx = -2; + } + + /* Parse flags. Example: "%+i" => flags=F_SIGN. */ + while (--ctx->fmtcnt >= 0) { + arg->ch = FORMAT_READ(ctx); + ctx->fmtpos++; + switch (arg->ch) { + case '-': arg->flags |= F_LJUST; continue; + case '+': arg->flags |= F_SIGN; continue; + case ' ': arg->flags |= F_BLANK; continue; + case '#': arg->flags |= F_ALT; continue; + case '0': arg->flags |= F_ZERO; continue; + } + break; + } + + /* Parse width. Example: "%10s" => width=10 */ + if (arg->ch == '*') { + v = unicode_format_getnextarg(ctx); + if (v == NULL) + return -1; + if (!PyLong_Check(v)) { + PyErr_SetString(PyExc_TypeError, + "* wants int"); + return -1; + } + arg->width = PyLong_AsSsize_t(v); + if (arg->width == -1 && PyErr_Occurred()) + return -1; + if (arg->width < 0) { + arg->flags |= F_LJUST; + arg->width = -arg->width; + } + if (--ctx->fmtcnt >= 0) { + arg->ch = FORMAT_READ(ctx); + ctx->fmtpos++; + } + } + else if (arg->ch >= '0' && arg->ch <= '9') { + arg->width = arg->ch - '0'; + while (--ctx->fmtcnt >= 0) { + arg->ch = FORMAT_READ(ctx); + ctx->fmtpos++; + if (arg->ch < '0' || arg->ch > '9') + break; + /* Since arg->ch is unsigned, the RHS would end up as unsigned, + mixing signed and unsigned comparison. Since arg->ch is between + '0' and '9', casting to int is safe. */ + if (arg->width > (PY_SSIZE_T_MAX - ((int)arg->ch - '0')) / 10) { + PyErr_SetString(PyExc_ValueError, + "width too big"); + return -1; + } + arg->width = arg->width*10 + (arg->ch - '0'); + } + } + + /* Parse precision. Example: "%.3f" => prec=3 */ + if (arg->ch == '.') { + arg->prec = 0; + if (--ctx->fmtcnt >= 0) { + arg->ch = FORMAT_READ(ctx); + ctx->fmtpos++; + } + if (arg->ch == '*') { + v = unicode_format_getnextarg(ctx); + if (v == NULL) + return -1; + if (!PyLong_Check(v)) { + PyErr_SetString(PyExc_TypeError, + "* wants int"); + return -1; + } + arg->prec = PyLong_AsInt(v); + if (arg->prec == -1 && PyErr_Occurred()) + return -1; + if (arg->prec < 0) + arg->prec = 0; + if (--ctx->fmtcnt >= 0) { + arg->ch = FORMAT_READ(ctx); + ctx->fmtpos++; + } + } + else if (arg->ch >= '0' && arg->ch <= '9') { + arg->prec = arg->ch - '0'; + while (--ctx->fmtcnt >= 0) { + arg->ch = FORMAT_READ(ctx); + ctx->fmtpos++; + if (arg->ch < '0' || arg->ch > '9') + break; + if (arg->prec > (INT_MAX - ((int)arg->ch - '0')) / 10) { + PyErr_SetString(PyExc_ValueError, + "precision too big"); + return -1; + } + arg->prec = arg->prec*10 + (arg->ch - '0'); + } + } + } + + /* Ignore "h", "l" and "L" format prefix (ex: "%hi" or "%ls") */ + if (ctx->fmtcnt >= 0) { + if (arg->ch == 'h' || arg->ch == 'l' || arg->ch == 'L') { + if (--ctx->fmtcnt >= 0) { + arg->ch = FORMAT_READ(ctx); + ctx->fmtpos++; + } + } + } + if (ctx->fmtcnt < 0) { + PyErr_SetString(PyExc_ValueError, + "incomplete format"); + return -1; + } + return 0; + +#undef FORMAT_READ +} + + +/* Format one argument. Supported conversion specifiers: + + - "s", "r", "a": any type + - "i", "d", "u": int or float + - "o", "x", "X": int + - "e", "E", "f", "F", "g", "G": float + - "c": int or str (1 character) + + When possible, the output is written directly into the Unicode writer + (ctx->writer). A string is created when padding is required. + + Return 0 if the argument has been formatted into *p_str, + 1 if the argument has been written into ctx->writer, + -1 on error. */ +static int +unicode_format_arg_format(struct unicode_formatter_t *ctx, + struct unicode_format_arg_t *arg, + PyObject **p_str) +{ + PyObject *v; + _PyUnicodeWriter *writer = &ctx->writer; + + if (ctx->fmtcnt == 0) + ctx->writer.overallocate = 0; + + v = unicode_format_getnextarg(ctx); + if (v == NULL) + return -1; + + + switch (arg->ch) { + case 's': + case 'r': + case 'a': + if (PyLong_CheckExact(v) && arg->width == -1 && arg->prec == -1) { + /* Fast path */ + if (_PyLong_FormatWriter(writer, v, 10, arg->flags & F_ALT) == -1) + return -1; + return 1; + } + + if (PyUnicode_CheckExact(v) && arg->ch == 's') { + *p_str = Py_NewRef(v); + } + else { + if (arg->ch == 's') + *p_str = PyObject_Str(v); + else if (arg->ch == 'r') + *p_str = PyObject_Repr(v); + else + *p_str = PyObject_ASCII(v); + } + break; + + case 'i': + case 'd': + case 'u': + case 'o': + case 'x': + case 'X': + { + int ret = mainformatlong(v, arg, p_str, writer); + if (ret != 0) + return ret; + arg->sign = 1; + break; + } + + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + if (arg->width == -1 && arg->prec == -1 + && !(arg->flags & (F_SIGN | F_BLANK))) + { + /* Fast path */ + if (formatfloat(v, arg, NULL, writer) == -1) + return -1; + return 1; + } + + arg->sign = 1; + if (formatfloat(v, arg, p_str, NULL) == -1) + return -1; + break; + + case 'c': + { + Py_UCS4 ch = formatchar(v); + if (ch == (Py_UCS4) -1) + return -1; + if (arg->width == -1 && arg->prec == -1) { + /* Fast path */ + if (_PyUnicodeWriter_WriteCharInline(writer, ch) < 0) + return -1; + return 1; + } + *p_str = PyUnicode_FromOrdinal(ch); + break; + } + + default: + PyErr_Format(PyExc_ValueError, + "unsupported format character '%c' (0x%x) " + "at index %zd", + (31<=arg->ch && arg->ch<=126) ? (char)arg->ch : '?', + (int)arg->ch, + ctx->fmtpos - 1); + return -1; + } + if (*p_str == NULL) + return -1; + assert (PyUnicode_Check(*p_str)); + return 0; +} + + +static int +unicode_format_arg_output(struct unicode_formatter_t *ctx, + struct unicode_format_arg_t *arg, + PyObject *str) +{ + Py_ssize_t len; + int kind; + const void *pbuf; + Py_ssize_t pindex; + Py_UCS4 signchar; + Py_ssize_t buflen; + Py_UCS4 maxchar; + Py_ssize_t sublen; + _PyUnicodeWriter *writer = &ctx->writer; + Py_UCS4 fill; + + fill = ' '; + if (arg->sign && arg->flags & F_ZERO) + fill = '0'; + + len = PyUnicode_GET_LENGTH(str); + if ((arg->width == -1 || arg->width <= len) + && (arg->prec == -1 || arg->prec >= len) + && !(arg->flags & (F_SIGN | F_BLANK))) + { + /* Fast path */ + if (_PyUnicodeWriter_WriteStr(writer, str) == -1) + return -1; + return 0; + } + + /* Truncate the string for "s", "r" and "a" formats + if the precision is set */ + if (arg->ch == 's' || arg->ch == 'r' || arg->ch == 'a') { + if (arg->prec >= 0 && len > arg->prec) + len = arg->prec; + } + + /* Adjust sign and width */ + kind = PyUnicode_KIND(str); + pbuf = PyUnicode_DATA(str); + pindex = 0; + signchar = '\0'; + if (arg->sign) { + Py_UCS4 ch = PyUnicode_READ(kind, pbuf, pindex); + if (ch == '-' || ch == '+') { + signchar = ch; + len--; + pindex++; + } + else if (arg->flags & F_SIGN) + signchar = '+'; + else if (arg->flags & F_BLANK) + signchar = ' '; + else + arg->sign = 0; + } + if (arg->width < len) + arg->width = len; + + /* Prepare the writer */ + maxchar = writer->maxchar; + if (!(arg->flags & F_LJUST)) { + if (arg->sign) { + if ((arg->width-1) > len) + maxchar = Py_MAX(maxchar, fill); + } + else { + if (arg->width > len) + maxchar = Py_MAX(maxchar, fill); + } + } + if (PyUnicode_MAX_CHAR_VALUE(str) > maxchar) { + Py_UCS4 strmaxchar = _PyUnicode_FindMaxChar(str, 0, pindex+len); + maxchar = Py_MAX(maxchar, strmaxchar); + } + + buflen = arg->width; + if (arg->sign && len == arg->width) + buflen++; + if (_PyUnicodeWriter_Prepare(writer, buflen, maxchar) == -1) + return -1; + + /* Write the sign if needed */ + if (arg->sign) { + if (fill != ' ') { + PyUnicode_WRITE(writer->kind, writer->data, writer->pos, signchar); + writer->pos += 1; + } + if (arg->width > len) + arg->width--; + } + + /* Write the numeric prefix for "x", "X" and "o" formats + if the alternate form is used. + For example, write "0x" for the "%#x" format. */ + if ((arg->flags & F_ALT) && (arg->ch == 'x' || arg->ch == 'X' || arg->ch == 'o')) { + assert(PyUnicode_READ(kind, pbuf, pindex) == '0'); + assert(PyUnicode_READ(kind, pbuf, pindex + 1) == arg->ch); + if (fill != ' ') { + PyUnicode_WRITE(writer->kind, writer->data, writer->pos, '0'); + PyUnicode_WRITE(writer->kind, writer->data, writer->pos+1, arg->ch); + writer->pos += 2; + pindex += 2; + } + arg->width -= 2; + if (arg->width < 0) + arg->width = 0; + len -= 2; + } + + /* Pad left with the fill character if needed */ + if (arg->width > len && !(arg->flags & F_LJUST)) { + sublen = arg->width - len; + _PyUnicode_Fill(writer->kind, writer->data, fill, writer->pos, sublen); + writer->pos += sublen; + arg->width = len; + } + + /* If padding with spaces: write sign if needed and/or numeric prefix if + the alternate form is used */ + if (fill == ' ') { + if (arg->sign) { + PyUnicode_WRITE(writer->kind, writer->data, writer->pos, signchar); + writer->pos += 1; + } + if ((arg->flags & F_ALT) && (arg->ch == 'x' || arg->ch == 'X' || arg->ch == 'o')) { + assert(PyUnicode_READ(kind, pbuf, pindex) == '0'); + assert(PyUnicode_READ(kind, pbuf, pindex+1) == arg->ch); + PyUnicode_WRITE(writer->kind, writer->data, writer->pos, '0'); + PyUnicode_WRITE(writer->kind, writer->data, writer->pos+1, arg->ch); + writer->pos += 2; + pindex += 2; + } + } + + /* Write characters */ + if (len) { + _PyUnicode_FastCopyCharacters(writer->buffer, writer->pos, + str, pindex, len); + writer->pos += len; + } + + /* Pad right with the fill character if needed */ + if (arg->width > len) { + sublen = arg->width - len; + _PyUnicode_Fill(writer->kind, writer->data, ' ', writer->pos, sublen); + writer->pos += sublen; + } + return 0; +} + + +/* Helper of PyUnicode_Format(): format one arg. + Return 0 on success, raise an exception and return -1 on error. */ +static int +unicode_format_arg(struct unicode_formatter_t *ctx) +{ + struct unicode_format_arg_t arg; + PyObject *str; + int ret; + + arg.ch = PyUnicode_READ(ctx->fmtkind, ctx->fmtdata, ctx->fmtpos); + if (arg.ch == '%') { + ctx->fmtpos++; + ctx->fmtcnt--; + if (_PyUnicodeWriter_WriteCharInline(&ctx->writer, '%') < 0) + return -1; + return 0; + } + arg.flags = 0; + arg.width = -1; + arg.prec = -1; + arg.sign = 0; + str = NULL; + + ret = unicode_format_arg_parse(ctx, &arg); + if (ret == -1) + return -1; + + ret = unicode_format_arg_format(ctx, &arg, &str); + if (ret == -1) + return -1; + + if (ret != 1) { + ret = unicode_format_arg_output(ctx, &arg, str); + Py_DECREF(str); + if (ret == -1) + return -1; + } + + if (ctx->dict && (ctx->argidx < ctx->arglen)) { + PyErr_SetString(PyExc_TypeError, + "not all arguments converted during string formatting"); + return -1; + } + return 0; +} + + +PyObject * +PyUnicode_Format(PyObject *format, PyObject *args) +{ + struct unicode_formatter_t ctx; + + if (format == NULL || args == NULL) { + PyErr_BadInternalCall(); + return NULL; + } + + if (ensure_unicode(format) < 0) + return NULL; + + ctx.fmtstr = format; + ctx.fmtdata = PyUnicode_DATA(ctx.fmtstr); + ctx.fmtkind = PyUnicode_KIND(ctx.fmtstr); + ctx.fmtcnt = PyUnicode_GET_LENGTH(ctx.fmtstr); + ctx.fmtpos = 0; + + _PyUnicodeWriter_Init(&ctx.writer); + ctx.writer.min_length = ctx.fmtcnt + 100; + ctx.writer.overallocate = 1; + + if (PyTuple_Check(args)) { + ctx.arglen = PyTuple_Size(args); + ctx.argidx = 0; + } + else { + ctx.arglen = -1; + ctx.argidx = -2; + } + ctx.args_owned = 0; + if (PyMapping_Check(args) && !PyTuple_Check(args) && !PyUnicode_Check(args)) + ctx.dict = args; + else + ctx.dict = NULL; + ctx.args = args; + + while (--ctx.fmtcnt >= 0) { + if (PyUnicode_READ(ctx.fmtkind, ctx.fmtdata, ctx.fmtpos) != '%') { + Py_ssize_t nonfmtpos; + + nonfmtpos = ctx.fmtpos++; + while (ctx.fmtcnt >= 0 && + PyUnicode_READ(ctx.fmtkind, ctx.fmtdata, ctx.fmtpos) != '%') { + ctx.fmtpos++; + ctx.fmtcnt--; + } + if (ctx.fmtcnt < 0) { + ctx.fmtpos--; + ctx.writer.overallocate = 0; + } + + if (_PyUnicodeWriter_WriteSubstring(&ctx.writer, ctx.fmtstr, + nonfmtpos, ctx.fmtpos) < 0) + goto onError; + } + else { + ctx.fmtpos++; + if (unicode_format_arg(&ctx) == -1) + goto onError; + } + } + + if (ctx.argidx < ctx.arglen && !ctx.dict) { + PyErr_SetString(PyExc_TypeError, + "not all arguments converted during string formatting"); + goto onError; + } + + if (ctx.args_owned) { + Py_DECREF(ctx.args); + } + return _PyUnicodeWriter_Finish(&ctx.writer); + + onError: + _PyUnicodeWriter_Dealloc(&ctx.writer); + if (ctx.args_owned) { + Py_DECREF(ctx.args); + } + return NULL; +} diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index c71f9d3f71d..a67bf9b1c53 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -105,6 +105,7 @@ NOTE: In the interpreter's initialization phase, some globals are currently */ #define MAX_UNICODE _Py_MAX_UNICODE +#define ensure_unicode _PyUnicode_EnsureUnicode #ifdef Py_DEBUG # define _PyUnicode_CHECK(op) _PyUnicode_CheckConsistency(op, 0) @@ -452,7 +453,6 @@ const unsigned char _Py_ascii_whitespace[] = { /* forward */ static PyObject* get_latin1_char(unsigned char ch); -static int unicode_modifiable(PyObject *unicode); static PyObject * @@ -983,18 +983,6 @@ make_bloom_mask(int kind, const void* ptr, Py_ssize_t len) #undef BLOOM_UPDATE } -static int -ensure_unicode(PyObject *obj) -{ - if (!PyUnicode_Check(obj)) { - PyErr_Format(PyExc_TypeError, - "must be str, not %.100s", - Py_TYPE(obj)->tp_name); - return -1; - } - return 0; -} - /* Compilation of templated routines */ #define STRINGLIB_GET_EMPTY() unicode_get_empty() @@ -1120,7 +1108,7 @@ resize_compact(PyObject *unicode, Py_ssize_t length) Py_ssize_t old_length = _PyUnicode_LENGTH(unicode); #endif - if (!unicode_modifiable(unicode)) { + if (!_PyUnicode_IsModifiable(unicode)) { PyObject *copy = resize_copy(unicode, length); if (copy == NULL) { return NULL; @@ -1412,7 +1400,7 @@ PyUnicode_New(Py_ssize_t size, Py_UCS4 maxchar) static int unicode_check_modifiable(PyObject *unicode) { - if (!unicode_modifiable(unicode)) { + if (!_PyUnicode_IsModifiable(unicode)) { PyErr_SetString(PyExc_SystemError, "Cannot modify a string currently used"); return -1; @@ -1774,8 +1762,8 @@ unicode_is_singleton(PyObject *unicode) } #endif -static int -unicode_modifiable(PyObject *unicode) +int +_PyUnicode_IsModifiable(PyObject *unicode) { assert(_PyUnicode_CHECK(unicode)); if (!_PyObject_IsUniquelyReferenced(unicode)) @@ -1816,7 +1804,7 @@ unicode_resize(PyObject **p_unicode, Py_ssize_t length) return 0; } - if (!unicode_modifiable(unicode)) { + if (!_PyUnicode_IsModifiable(unicode)) { PyObject *copy = resize_copy(unicode, length); if (copy == NULL) return -1; @@ -10252,7 +10240,7 @@ _PyUnicode_FastFill(PyObject *unicode, Py_ssize_t start, Py_ssize_t length, { const int kind = PyUnicode_KIND(unicode); void *data = PyUnicode_DATA(unicode); - assert(unicode_modifiable(unicode)); + assert(_PyUnicode_IsModifiable(unicode)); assert(fill_char <= PyUnicode_MAX_CHAR_VALUE(unicode)); assert(start >= 0); assert(start + length <= PyUnicode_GET_LENGTH(unicode)); @@ -11524,7 +11512,7 @@ PyUnicode_Append(PyObject **p_left, PyObject *right) } new_len = left_len + right_len; - if (unicode_modifiable(left) + if (_PyUnicode_IsModifiable(left) && PyUnicode_CheckExact(right) && PyUnicode_KIND(right) <= PyUnicode_KIND(left) /* Don't resize for ascii += latin1. Convert ascii to latin1 requires @@ -13722,17 +13710,6 @@ _PyUnicodeWriter_PrepareKindInternal(_PyUnicodeWriter *writer, return _PyUnicodeWriter_PrepareInternal(writer, 0, maxchar); } -static inline int -_PyUnicodeWriter_WriteCharInline(_PyUnicodeWriter *writer, Py_UCS4 ch) -{ - assert(ch <= MAX_UNICODE); - if (_PyUnicodeWriter_Prepare(writer, 1, ch) < 0) - return -1; - PyUnicode_WRITE(writer->kind, writer->data, writer->pos, ch); - writer->pos++; - return 0; -} - int _PyUnicodeWriter_WriteChar(_PyUnicodeWriter *writer, Py_UCS4 ch) { @@ -14461,947 +14438,6 @@ static PyMappingMethods unicode_as_mapping = { }; -/* Helpers for PyUnicode_Format() */ - -struct unicode_formatter_t { - PyObject *args; - int args_owned; - Py_ssize_t arglen, argidx; - PyObject *dict; - - int fmtkind; - Py_ssize_t fmtcnt, fmtpos; - const void *fmtdata; - PyObject *fmtstr; - - _PyUnicodeWriter writer; -}; - -struct unicode_format_arg_t { - Py_UCS4 ch; - int flags; - Py_ssize_t width; - int prec; - int sign; -}; - -static PyObject * -unicode_format_getnextarg(struct unicode_formatter_t *ctx) -{ - Py_ssize_t argidx = ctx->argidx; - - if (argidx < ctx->arglen) { - ctx->argidx++; - if (ctx->arglen < 0) - return ctx->args; - else - return PyTuple_GetItem(ctx->args, argidx); - } - PyErr_SetString(PyExc_TypeError, - "not enough arguments for format string"); - return NULL; -} - -/* Returns a new reference to a PyUnicode object, or NULL on failure. */ - -/* Format a float into the writer if the writer is not NULL, or into *p_output - otherwise. - - Return 0 on success, raise an exception and return -1 on error. */ -static int -formatfloat(PyObject *v, struct unicode_format_arg_t *arg, - PyObject **p_output, - _PyUnicodeWriter *writer) -{ - char *p; - double x; - Py_ssize_t len; - int prec; - int dtoa_flags = 0; - - x = PyFloat_AsDouble(v); - if (x == -1.0 && PyErr_Occurred()) - return -1; - - prec = arg->prec; - if (prec < 0) - prec = 6; - - if (arg->flags & F_ALT) - dtoa_flags |= Py_DTSF_ALT; - p = PyOS_double_to_string(x, arg->ch, prec, dtoa_flags, NULL); - if (p == NULL) - return -1; - len = strlen(p); - if (writer) { - if (_PyUnicodeWriter_WriteASCIIString(writer, p, len) < 0) { - PyMem_Free(p); - return -1; - } - } - else - *p_output = _PyUnicode_FromASCII(p, len); - PyMem_Free(p); - return 0; -} - -/* formatlong() emulates the format codes d, u, o, x and X, and - * the F_ALT flag, for Python's long (unbounded) ints. It's not used for - * Python's regular ints. - * Return value: a new PyUnicodeObject*, or NULL if error. - * The output string is of the form - * "-"? ("0x" | "0X")? digit+ - * "0x"/"0X" are present only for x and X conversions, with F_ALT - * set in flags. The case of hex digits will be correct, - * There will be at least prec digits, zero-filled on the left if - * necessary to get that many. - * val object to be converted - * flags bitmask of format flags; only F_ALT is looked at - * prec minimum number of digits; 0-fill on left if needed - * type a character in [duoxX]; u acts the same as d - * - * CAUTION: o, x and X conversions on regular ints can never - * produce a '-' sign, but can for Python's unbounded ints. - */ -PyObject * -_PyUnicode_FormatLong(PyObject *val, int alt, int prec, int type) -{ - PyObject *result = NULL; - char *buf; - Py_ssize_t i; - int sign; /* 1 if '-', else 0 */ - int len; /* number of characters */ - Py_ssize_t llen; - int numdigits; /* len == numnondigits + numdigits */ - int numnondigits = 0; - - /* Avoid exceeding SSIZE_T_MAX */ - if (prec > INT_MAX-3) { - PyErr_SetString(PyExc_OverflowError, - "precision too large"); - return NULL; - } - - assert(PyLong_Check(val)); - - switch (type) { - default: - Py_UNREACHABLE(); - case 'd': - case 'i': - case 'u': - /* int and int subclasses should print numerically when a numeric */ - /* format code is used (see issue18780) */ - result = PyNumber_ToBase(val, 10); - break; - case 'o': - numnondigits = 2; - result = PyNumber_ToBase(val, 8); - break; - case 'x': - case 'X': - numnondigits = 2; - result = PyNumber_ToBase(val, 16); - break; - } - if (!result) - return NULL; - - assert(unicode_modifiable(result)); - assert(PyUnicode_IS_ASCII(result)); - - /* To modify the string in-place, there can only be one reference. */ - if (!_PyObject_IsUniquelyReferenced(result)) { - Py_DECREF(result); - PyErr_BadInternalCall(); - return NULL; - } - buf = PyUnicode_DATA(result); - llen = PyUnicode_GET_LENGTH(result); - if (llen > INT_MAX) { - Py_DECREF(result); - PyErr_SetString(PyExc_ValueError, - "string too large in _PyUnicode_FormatLong"); - return NULL; - } - len = (int)llen; - sign = buf[0] == '-'; - numnondigits += sign; - numdigits = len - numnondigits; - assert(numdigits > 0); - - /* Get rid of base marker unless F_ALT */ - if (((alt) == 0 && - (type == 'o' || type == 'x' || type == 'X'))) { - assert(buf[sign] == '0'); - assert(buf[sign+1] == 'x' || buf[sign+1] == 'X' || - buf[sign+1] == 'o'); - numnondigits -= 2; - buf += 2; - len -= 2; - if (sign) - buf[0] = '-'; - assert(len == numnondigits + numdigits); - assert(numdigits > 0); - } - - /* Fill with leading zeroes to meet minimum width. */ - if (prec > numdigits) { - PyObject *r1 = PyBytes_FromStringAndSize(NULL, - numnondigits + prec); - char *b1; - if (!r1) { - Py_DECREF(result); - return NULL; - } - b1 = PyBytes_AS_STRING(r1); - for (i = 0; i < numnondigits; ++i) - *b1++ = *buf++; - for (i = 0; i < prec - numdigits; i++) - *b1++ = '0'; - for (i = 0; i < numdigits; i++) - *b1++ = *buf++; - *b1 = '\0'; - Py_SETREF(result, r1); - buf = PyBytes_AS_STRING(result); - len = numnondigits + prec; - } - - /* Fix up case for hex conversions. */ - if (type == 'X') { - /* Need to convert all lower case letters to upper case. - and need to convert 0x to 0X (and -0x to -0X). */ - for (i = 0; i < len; i++) - if (buf[i] >= 'a' && buf[i] <= 'x') - buf[i] -= 'a'-'A'; - } - if (!PyUnicode_Check(result) - || buf != PyUnicode_DATA(result)) { - PyObject *unicode; - unicode = _PyUnicode_FromASCII(buf, len); - Py_SETREF(result, unicode); - } - else if (len != PyUnicode_GET_LENGTH(result)) { - if (PyUnicode_Resize(&result, len) < 0) - Py_CLEAR(result); - } - return result; -} - -/* Format an integer or a float as an integer. - * Return 1 if the number has been formatted into the writer, - * 0 if the number has been formatted into *p_output - * -1 and raise an exception on error */ -static int -mainformatlong(PyObject *v, - struct unicode_format_arg_t *arg, - PyObject **p_output, - _PyUnicodeWriter *writer) -{ - PyObject *iobj, *res; - char type = (char)arg->ch; - - if (!PyNumber_Check(v)) - goto wrongtype; - - /* make sure number is a type of integer for o, x, and X */ - if (!PyLong_Check(v)) { - if (type == 'o' || type == 'x' || type == 'X') { - iobj = _PyNumber_Index(v); - } - else { - iobj = PyNumber_Long(v); - } - if (iobj == NULL ) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) - goto wrongtype; - return -1; - } - assert(PyLong_Check(iobj)); - } - else { - iobj = Py_NewRef(v); - } - - if (PyLong_CheckExact(v) - && arg->width == -1 && arg->prec == -1 - && !(arg->flags & (F_SIGN | F_BLANK)) - && type != 'X') - { - /* Fast path */ - int alternate = arg->flags & F_ALT; - int base; - - switch(type) - { - default: - Py_UNREACHABLE(); - case 'd': - case 'i': - case 'u': - base = 10; - break; - case 'o': - base = 8; - break; - case 'x': - case 'X': - base = 16; - break; - } - - if (_PyLong_FormatWriter(writer, v, base, alternate) == -1) { - Py_DECREF(iobj); - return -1; - } - Py_DECREF(iobj); - return 1; - } - - res = _PyUnicode_FormatLong(iobj, arg->flags & F_ALT, arg->prec, type); - Py_DECREF(iobj); - if (res == NULL) - return -1; - *p_output = res; - return 0; - -wrongtype: - switch(type) - { - case 'o': - case 'x': - case 'X': - PyErr_Format(PyExc_TypeError, - "%%%c format: an integer is required, " - "not %.200s", - type, Py_TYPE(v)->tp_name); - break; - default: - PyErr_Format(PyExc_TypeError, - "%%%c format: a real number is required, " - "not %.200s", - type, Py_TYPE(v)->tp_name); - break; - } - return -1; -} - -static Py_UCS4 -formatchar(PyObject *v) -{ - /* presume that the buffer is at least 3 characters long */ - if (PyUnicode_Check(v)) { - if (PyUnicode_GET_LENGTH(v) == 1) { - return PyUnicode_READ_CHAR(v, 0); - } - PyErr_Format(PyExc_TypeError, - "%%c requires an int or a unicode character, " - "not a string of length %zd", - PyUnicode_GET_LENGTH(v)); - return (Py_UCS4) -1; - } - else { - int overflow; - long x = PyLong_AsLongAndOverflow(v, &overflow); - if (x == -1 && PyErr_Occurred()) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Format(PyExc_TypeError, - "%%c requires an int or a unicode character, not %T", - v); - return (Py_UCS4) -1; - } - return (Py_UCS4) -1; - } - - if (x < 0 || x > MAX_UNICODE) { - /* this includes an overflow in converting to C long */ - PyErr_SetString(PyExc_OverflowError, - "%c arg not in range(0x110000)"); - return (Py_UCS4) -1; - } - - return (Py_UCS4) x; - } -} - -/* Parse options of an argument: flags, width, precision. - Handle also "%(name)" syntax. - - Return 0 if the argument has been formatted into arg->str. - Return 1 if the argument has been written into ctx->writer, - Raise an exception and return -1 on error. */ -static int -unicode_format_arg_parse(struct unicode_formatter_t *ctx, - struct unicode_format_arg_t *arg) -{ -#define FORMAT_READ(ctx) \ - PyUnicode_READ((ctx)->fmtkind, (ctx)->fmtdata, (ctx)->fmtpos) - - PyObject *v; - - if (arg->ch == '(') { - /* Get argument value from a dictionary. Example: "%(name)s". */ - Py_ssize_t keystart; - Py_ssize_t keylen; - PyObject *key; - int pcount = 1; - - if (ctx->dict == NULL) { - PyErr_SetString(PyExc_TypeError, - "format requires a mapping"); - return -1; - } - ++ctx->fmtpos; - --ctx->fmtcnt; - keystart = ctx->fmtpos; - /* Skip over balanced parentheses */ - while (pcount > 0 && --ctx->fmtcnt >= 0) { - arg->ch = FORMAT_READ(ctx); - if (arg->ch == ')') - --pcount; - else if (arg->ch == '(') - ++pcount; - ctx->fmtpos++; - } - keylen = ctx->fmtpos - keystart - 1; - if (ctx->fmtcnt < 0 || pcount > 0) { - PyErr_SetString(PyExc_ValueError, - "incomplete format key"); - return -1; - } - key = PyUnicode_Substring(ctx->fmtstr, - keystart, keystart + keylen); - if (key == NULL) - return -1; - if (ctx->args_owned) { - ctx->args_owned = 0; - Py_DECREF(ctx->args); - } - ctx->args = PyObject_GetItem(ctx->dict, key); - Py_DECREF(key); - if (ctx->args == NULL) - return -1; - ctx->args_owned = 1; - ctx->arglen = -1; - ctx->argidx = -2; - } - - /* Parse flags. Example: "%+i" => flags=F_SIGN. */ - while (--ctx->fmtcnt >= 0) { - arg->ch = FORMAT_READ(ctx); - ctx->fmtpos++; - switch (arg->ch) { - case '-': arg->flags |= F_LJUST; continue; - case '+': arg->flags |= F_SIGN; continue; - case ' ': arg->flags |= F_BLANK; continue; - case '#': arg->flags |= F_ALT; continue; - case '0': arg->flags |= F_ZERO; continue; - } - break; - } - - /* Parse width. Example: "%10s" => width=10 */ - if (arg->ch == '*') { - v = unicode_format_getnextarg(ctx); - if (v == NULL) - return -1; - if (!PyLong_Check(v)) { - PyErr_SetString(PyExc_TypeError, - "* wants int"); - return -1; - } - arg->width = PyLong_AsSsize_t(v); - if (arg->width == -1 && PyErr_Occurred()) - return -1; - if (arg->width < 0) { - arg->flags |= F_LJUST; - arg->width = -arg->width; - } - if (--ctx->fmtcnt >= 0) { - arg->ch = FORMAT_READ(ctx); - ctx->fmtpos++; - } - } - else if (arg->ch >= '0' && arg->ch <= '9') { - arg->width = arg->ch - '0'; - while (--ctx->fmtcnt >= 0) { - arg->ch = FORMAT_READ(ctx); - ctx->fmtpos++; - if (arg->ch < '0' || arg->ch > '9') - break; - /* Since arg->ch is unsigned, the RHS would end up as unsigned, - mixing signed and unsigned comparison. Since arg->ch is between - '0' and '9', casting to int is safe. */ - if (arg->width > (PY_SSIZE_T_MAX - ((int)arg->ch - '0')) / 10) { - PyErr_SetString(PyExc_ValueError, - "width too big"); - return -1; - } - arg->width = arg->width*10 + (arg->ch - '0'); - } - } - - /* Parse precision. Example: "%.3f" => prec=3 */ - if (arg->ch == '.') { - arg->prec = 0; - if (--ctx->fmtcnt >= 0) { - arg->ch = FORMAT_READ(ctx); - ctx->fmtpos++; - } - if (arg->ch == '*') { - v = unicode_format_getnextarg(ctx); - if (v == NULL) - return -1; - if (!PyLong_Check(v)) { - PyErr_SetString(PyExc_TypeError, - "* wants int"); - return -1; - } - arg->prec = PyLong_AsInt(v); - if (arg->prec == -1 && PyErr_Occurred()) - return -1; - if (arg->prec < 0) - arg->prec = 0; - if (--ctx->fmtcnt >= 0) { - arg->ch = FORMAT_READ(ctx); - ctx->fmtpos++; - } - } - else if (arg->ch >= '0' && arg->ch <= '9') { - arg->prec = arg->ch - '0'; - while (--ctx->fmtcnt >= 0) { - arg->ch = FORMAT_READ(ctx); - ctx->fmtpos++; - if (arg->ch < '0' || arg->ch > '9') - break; - if (arg->prec > (INT_MAX - ((int)arg->ch - '0')) / 10) { - PyErr_SetString(PyExc_ValueError, - "precision too big"); - return -1; - } - arg->prec = arg->prec*10 + (arg->ch - '0'); - } - } - } - - /* Ignore "h", "l" and "L" format prefix (ex: "%hi" or "%ls") */ - if (ctx->fmtcnt >= 0) { - if (arg->ch == 'h' || arg->ch == 'l' || arg->ch == 'L') { - if (--ctx->fmtcnt >= 0) { - arg->ch = FORMAT_READ(ctx); - ctx->fmtpos++; - } - } - } - if (ctx->fmtcnt < 0) { - PyErr_SetString(PyExc_ValueError, - "incomplete format"); - return -1; - } - return 0; - -#undef FORMAT_READ -} - -/* Format one argument. Supported conversion specifiers: - - - "s", "r", "a": any type - - "i", "d", "u": int or float - - "o", "x", "X": int - - "e", "E", "f", "F", "g", "G": float - - "c": int or str (1 character) - - When possible, the output is written directly into the Unicode writer - (ctx->writer). A string is created when padding is required. - - Return 0 if the argument has been formatted into *p_str, - 1 if the argument has been written into ctx->writer, - -1 on error. */ -static int -unicode_format_arg_format(struct unicode_formatter_t *ctx, - struct unicode_format_arg_t *arg, - PyObject **p_str) -{ - PyObject *v; - _PyUnicodeWriter *writer = &ctx->writer; - - if (ctx->fmtcnt == 0) - ctx->writer.overallocate = 0; - - v = unicode_format_getnextarg(ctx); - if (v == NULL) - return -1; - - - switch (arg->ch) { - case 's': - case 'r': - case 'a': - if (PyLong_CheckExact(v) && arg->width == -1 && arg->prec == -1) { - /* Fast path */ - if (_PyLong_FormatWriter(writer, v, 10, arg->flags & F_ALT) == -1) - return -1; - return 1; - } - - if (PyUnicode_CheckExact(v) && arg->ch == 's') { - *p_str = Py_NewRef(v); - } - else { - if (arg->ch == 's') - *p_str = PyObject_Str(v); - else if (arg->ch == 'r') - *p_str = PyObject_Repr(v); - else - *p_str = PyObject_ASCII(v); - } - break; - - case 'i': - case 'd': - case 'u': - case 'o': - case 'x': - case 'X': - { - int ret = mainformatlong(v, arg, p_str, writer); - if (ret != 0) - return ret; - arg->sign = 1; - break; - } - - case 'e': - case 'E': - case 'f': - case 'F': - case 'g': - case 'G': - if (arg->width == -1 && arg->prec == -1 - && !(arg->flags & (F_SIGN | F_BLANK))) - { - /* Fast path */ - if (formatfloat(v, arg, NULL, writer) == -1) - return -1; - return 1; - } - - arg->sign = 1; - if (formatfloat(v, arg, p_str, NULL) == -1) - return -1; - break; - - case 'c': - { - Py_UCS4 ch = formatchar(v); - if (ch == (Py_UCS4) -1) - return -1; - if (arg->width == -1 && arg->prec == -1) { - /* Fast path */ - if (_PyUnicodeWriter_WriteCharInline(writer, ch) < 0) - return -1; - return 1; - } - *p_str = PyUnicode_FromOrdinal(ch); - break; - } - - default: - PyErr_Format(PyExc_ValueError, - "unsupported format character '%c' (0x%x) " - "at index %zd", - (31<=arg->ch && arg->ch<=126) ? (char)arg->ch : '?', - (int)arg->ch, - ctx->fmtpos - 1); - return -1; - } - if (*p_str == NULL) - return -1; - assert (PyUnicode_Check(*p_str)); - return 0; -} - -static int -unicode_format_arg_output(struct unicode_formatter_t *ctx, - struct unicode_format_arg_t *arg, - PyObject *str) -{ - Py_ssize_t len; - int kind; - const void *pbuf; - Py_ssize_t pindex; - Py_UCS4 signchar; - Py_ssize_t buflen; - Py_UCS4 maxchar; - Py_ssize_t sublen; - _PyUnicodeWriter *writer = &ctx->writer; - Py_UCS4 fill; - - fill = ' '; - if (arg->sign && arg->flags & F_ZERO) - fill = '0'; - - len = PyUnicode_GET_LENGTH(str); - if ((arg->width == -1 || arg->width <= len) - && (arg->prec == -1 || arg->prec >= len) - && !(arg->flags & (F_SIGN | F_BLANK))) - { - /* Fast path */ - if (_PyUnicodeWriter_WriteStr(writer, str) == -1) - return -1; - return 0; - } - - /* Truncate the string for "s", "r" and "a" formats - if the precision is set */ - if (arg->ch == 's' || arg->ch == 'r' || arg->ch == 'a') { - if (arg->prec >= 0 && len > arg->prec) - len = arg->prec; - } - - /* Adjust sign and width */ - kind = PyUnicode_KIND(str); - pbuf = PyUnicode_DATA(str); - pindex = 0; - signchar = '\0'; - if (arg->sign) { - Py_UCS4 ch = PyUnicode_READ(kind, pbuf, pindex); - if (ch == '-' || ch == '+') { - signchar = ch; - len--; - pindex++; - } - else if (arg->flags & F_SIGN) - signchar = '+'; - else if (arg->flags & F_BLANK) - signchar = ' '; - else - arg->sign = 0; - } - if (arg->width < len) - arg->width = len; - - /* Prepare the writer */ - maxchar = writer->maxchar; - if (!(arg->flags & F_LJUST)) { - if (arg->sign) { - if ((arg->width-1) > len) - maxchar = Py_MAX(maxchar, fill); - } - else { - if (arg->width > len) - maxchar = Py_MAX(maxchar, fill); - } - } - if (PyUnicode_MAX_CHAR_VALUE(str) > maxchar) { - Py_UCS4 strmaxchar = _PyUnicode_FindMaxChar(str, 0, pindex+len); - maxchar = Py_MAX(maxchar, strmaxchar); - } - - buflen = arg->width; - if (arg->sign && len == arg->width) - buflen++; - if (_PyUnicodeWriter_Prepare(writer, buflen, maxchar) == -1) - return -1; - - /* Write the sign if needed */ - if (arg->sign) { - if (fill != ' ') { - PyUnicode_WRITE(writer->kind, writer->data, writer->pos, signchar); - writer->pos += 1; - } - if (arg->width > len) - arg->width--; - } - - /* Write the numeric prefix for "x", "X" and "o" formats - if the alternate form is used. - For example, write "0x" for the "%#x" format. */ - if ((arg->flags & F_ALT) && (arg->ch == 'x' || arg->ch == 'X' || arg->ch == 'o')) { - assert(PyUnicode_READ(kind, pbuf, pindex) == '0'); - assert(PyUnicode_READ(kind, pbuf, pindex + 1) == arg->ch); - if (fill != ' ') { - PyUnicode_WRITE(writer->kind, writer->data, writer->pos, '0'); - PyUnicode_WRITE(writer->kind, writer->data, writer->pos+1, arg->ch); - writer->pos += 2; - pindex += 2; - } - arg->width -= 2; - if (arg->width < 0) - arg->width = 0; - len -= 2; - } - - /* Pad left with the fill character if needed */ - if (arg->width > len && !(arg->flags & F_LJUST)) { - sublen = arg->width - len; - _PyUnicode_Fill(writer->kind, writer->data, fill, writer->pos, sublen); - writer->pos += sublen; - arg->width = len; - } - - /* If padding with spaces: write sign if needed and/or numeric prefix if - the alternate form is used */ - if (fill == ' ') { - if (arg->sign) { - PyUnicode_WRITE(writer->kind, writer->data, writer->pos, signchar); - writer->pos += 1; - } - if ((arg->flags & F_ALT) && (arg->ch == 'x' || arg->ch == 'X' || arg->ch == 'o')) { - assert(PyUnicode_READ(kind, pbuf, pindex) == '0'); - assert(PyUnicode_READ(kind, pbuf, pindex+1) == arg->ch); - PyUnicode_WRITE(writer->kind, writer->data, writer->pos, '0'); - PyUnicode_WRITE(writer->kind, writer->data, writer->pos+1, arg->ch); - writer->pos += 2; - pindex += 2; - } - } - - /* Write characters */ - if (len) { - _PyUnicode_FastCopyCharacters(writer->buffer, writer->pos, - str, pindex, len); - writer->pos += len; - } - - /* Pad right with the fill character if needed */ - if (arg->width > len) { - sublen = arg->width - len; - _PyUnicode_Fill(writer->kind, writer->data, ' ', writer->pos, sublen); - writer->pos += sublen; - } - return 0; -} - -/* Helper of PyUnicode_Format(): format one arg. - Return 0 on success, raise an exception and return -1 on error. */ -static int -unicode_format_arg(struct unicode_formatter_t *ctx) -{ - struct unicode_format_arg_t arg; - PyObject *str; - int ret; - - arg.ch = PyUnicode_READ(ctx->fmtkind, ctx->fmtdata, ctx->fmtpos); - if (arg.ch == '%') { - ctx->fmtpos++; - ctx->fmtcnt--; - if (_PyUnicodeWriter_WriteCharInline(&ctx->writer, '%') < 0) - return -1; - return 0; - } - arg.flags = 0; - arg.width = -1; - arg.prec = -1; - arg.sign = 0; - str = NULL; - - ret = unicode_format_arg_parse(ctx, &arg); - if (ret == -1) - return -1; - - ret = unicode_format_arg_format(ctx, &arg, &str); - if (ret == -1) - return -1; - - if (ret != 1) { - ret = unicode_format_arg_output(ctx, &arg, str); - Py_DECREF(str); - if (ret == -1) - return -1; - } - - if (ctx->dict && (ctx->argidx < ctx->arglen)) { - PyErr_SetString(PyExc_TypeError, - "not all arguments converted during string formatting"); - return -1; - } - return 0; -} - -PyObject * -PyUnicode_Format(PyObject *format, PyObject *args) -{ - struct unicode_formatter_t ctx; - - if (format == NULL || args == NULL) { - PyErr_BadInternalCall(); - return NULL; - } - - if (ensure_unicode(format) < 0) - return NULL; - - ctx.fmtstr = format; - ctx.fmtdata = PyUnicode_DATA(ctx.fmtstr); - ctx.fmtkind = PyUnicode_KIND(ctx.fmtstr); - ctx.fmtcnt = PyUnicode_GET_LENGTH(ctx.fmtstr); - ctx.fmtpos = 0; - - _PyUnicodeWriter_Init(&ctx.writer); - ctx.writer.min_length = ctx.fmtcnt + 100; - ctx.writer.overallocate = 1; - - if (PyTuple_Check(args)) { - ctx.arglen = PyTuple_Size(args); - ctx.argidx = 0; - } - else { - ctx.arglen = -1; - ctx.argidx = -2; - } - ctx.args_owned = 0; - if (PyMapping_Check(args) && !PyTuple_Check(args) && !PyUnicode_Check(args)) - ctx.dict = args; - else - ctx.dict = NULL; - ctx.args = args; - - while (--ctx.fmtcnt >= 0) { - if (PyUnicode_READ(ctx.fmtkind, ctx.fmtdata, ctx.fmtpos) != '%') { - Py_ssize_t nonfmtpos; - - nonfmtpos = ctx.fmtpos++; - while (ctx.fmtcnt >= 0 && - PyUnicode_READ(ctx.fmtkind, ctx.fmtdata, ctx.fmtpos) != '%') { - ctx.fmtpos++; - ctx.fmtcnt--; - } - if (ctx.fmtcnt < 0) { - ctx.fmtpos--; - ctx.writer.overallocate = 0; - } - - if (_PyUnicodeWriter_WriteSubstring(&ctx.writer, ctx.fmtstr, - nonfmtpos, ctx.fmtpos) < 0) - goto onError; - } - else { - ctx.fmtpos++; - if (unicode_format_arg(&ctx) == -1) - goto onError; - } - } - - if (ctx.argidx < ctx.arglen && !ctx.dict) { - PyErr_SetString(PyExc_TypeError, - "not all arguments converted during string formatting"); - goto onError; - } - - if (ctx.args_owned) { - Py_DECREF(ctx.args); - } - return _PyUnicodeWriter_Finish(&ctx.writer); - - onError: - _PyUnicodeWriter_Dealloc(&ctx.writer); - if (ctx.args_owned) { - Py_DECREF(ctx.args); - } - return NULL; -} - static PyObject * unicode_subtype_new(PyTypeObject *type, PyObject *unicode); diff --git a/PCbuild/_freeze_module.vcxproj b/PCbuild/_freeze_module.vcxproj index c4a11fa9b24..02b6f35798f 100644 --- a/PCbuild/_freeze_module.vcxproj +++ b/PCbuild/_freeze_module.vcxproj @@ -164,6 +164,7 @@ + diff --git a/PCbuild/_freeze_module.vcxproj.filters b/PCbuild/_freeze_module.vcxproj.filters index 7bbbec2c988..39462a6380c 100644 --- a/PCbuild/_freeze_module.vcxproj.filters +++ b/PCbuild/_freeze_module.vcxproj.filters @@ -481,6 +481,9 @@ Source Files + + Source Files + Source Files diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index e2e1e415827..2657ee5c444 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -558,6 +558,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 7e7ed9c2ae6..9c12be6e935 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -1271,6 +1271,9 @@ Objects + + Objects + Objects From d0b18b19fa0ed73ce99e49751604f3e7d409b8ba Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 10 Oct 2025 13:01:06 +0200 Subject: [PATCH 100/373] gh-129813: Fix PyBytesWriter tests (#139892) --- Doc/c-api/bytes.rst | 2 +- Modules/_testcapi/bytes.c | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Doc/c-api/bytes.rst b/Doc/c-api/bytes.rst index 177ebf6c6b3..0a73c4748f4 100644 --- a/Doc/c-api/bytes.rst +++ b/Doc/c-api/bytes.rst @@ -260,7 +260,7 @@ Create, Finish, Discard writer size to *size*. The caller is responsible to write *size* bytes using :c:func:`PyBytesWriter_GetData`. - On error, set an exception and return NULL. + On error, set an exception and return ``NULL``. *size* must be positive or zero. diff --git a/Modules/_testcapi/bytes.c b/Modules/_testcapi/bytes.c index 388e65456c3..f12fc7f5f3a 100644 --- a/Modules/_testcapi/bytes.c +++ b/Modules/_testcapi/bytes.c @@ -79,11 +79,6 @@ writer_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) static int writer_init(PyObject *self_raw, PyObject *args, PyObject *kwargs) { - WriterObject *self = (WriterObject *)self_raw; - if (self->writer) { - PyBytesWriter_Discard(self->writer); - } - if (kwargs && PyDict_GET_SIZE(kwargs)) { PyErr_Format(PyExc_TypeError, "PyBytesWriter() takes exactly no keyword arguments"); @@ -99,6 +94,10 @@ writer_init(PyObject *self_raw, PyObject *args, PyObject *kwargs) return -1; } + WriterObject *self = (WriterObject *)self_raw; + if (self->writer) { + PyBytesWriter_Discard(self->writer); + } if (use_bytearray) { self->writer = _PyBytesWriter_CreateByteArray(alloc); } From 5c942f11cdf5f9d7313200983fa0c58b3bc670a2 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 10 Oct 2025 15:51:19 +0300 Subject: [PATCH 101/373] gh-63161: Fix PEP 263 support (GH-139481) * Support non-UTF-8 shebang and comments if non-UTF-8 encoding is specified. * Detect decoding error in comments for UTF-8 encoding. * Include the decoding error position for default encoding in SyntaxError. --- Lib/test/test_exceptions.py | 8 +- Lib/test/test_source_encoding.py | 123 ++++++++++++++++-- ...5-10-01-18-21-19.gh-issue-63161.ef1S6N.rst | 5 + Parser/pegen_errors.c | 8 ++ Parser/tokenizer/file_tokenizer.c | 49 +++++-- Parser/tokenizer/helpers.c | 53 +++++--- Parser/tokenizer/helpers.h | 2 +- Parser/tokenizer/readline_tokenizer.c | 2 +- Parser/tokenizer/string_tokenizer.c | 6 + 9 files changed, 210 insertions(+), 46 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-01-18-21-19.gh-issue-63161.ef1S6N.rst diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 59f77f91d85..323a8c401bd 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -224,6 +224,8 @@ def check(self, src, lineno, offset, end_lineno=None, end_offset=None, encoding= if not isinstance(src, str): src = src.decode(encoding, 'replace') line = src.split('\n')[lineno-1] + if lineno == 1: + line = line.removeprefix('\ufeff') self.assertIn(line, cm.exception.text) def test_error_offset_continuation_characters(self): @@ -239,7 +241,9 @@ def testSyntaxErrorOffset(self): check('Python = "\u1e54\xfd\u0163\u0125\xf2\xf1" +', 1, 20) check(b'# -*- coding: cp1251 -*-\nPython = "\xcf\xb3\xf2\xee\xed" +', 2, 19, encoding='cp1251') - check(b'Python = "\xcf\xb3\xf2\xee\xed" +', 1, 10) + check(b'Python = "\xcf\xb3\xf2\xee\xed" +', 1, 12) + check(b'\n\n\nPython = "\xcf\xb3\xf2\xee\xed" +', 4, 12) + check(b'\xef\xbb\xbfPython = "\xcf\xb3\xf2\xee\xed" +', 1, 12) check('x = "a', 1, 5) check('lambda x: x = 2', 1, 1) check('f{a + b + c}', 1, 2) @@ -287,7 +291,7 @@ def baz(): check("pass\npass\npass\n(1+)\npass\npass\npass", 4, 4) check("(1+)", 1, 4) check("[interesting\nfoo()\n", 1, 1) - check(b"\xef\xbb\xbf#coding: utf8\nprint('\xe6\x88\x91')\n", 0, -1) + check(b"\xef\xbb\xbf#coding: utf8\nprint('\xe6\x88\x91')\n", 1, 0) check("""f''' { (123_a) diff --git a/Lib/test/test_source_encoding.py b/Lib/test/test_source_encoding.py index 5df40782382..46b291192df 100644 --- a/Lib/test/test_source_encoding.py +++ b/Lib/test/test_source_encoding.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- import unittest -from test.support import script_helper, captured_stdout, requires_subprocess, requires_resource +from test import support +from test.support import script_helper from test.support.os_helper import TESTFN, unlink, rmtree from test.support.import_helper import unload import importlib @@ -64,7 +65,7 @@ def test_issue7820(self): # two bytes in common with the UTF-8 BOM self.assertRaises(SyntaxError, eval, b'\xef\xbb\x20') - @requires_subprocess() + @support.requires_subprocess() def test_20731(self): sub = subprocess.Popen([sys.executable, os.path.join(os.path.dirname(__file__), @@ -267,6 +268,17 @@ def test_second_non_utf8_coding_line(self): b'print(ascii("\xc3\xa4"))\n') self.check_script_output(src, br"'\xc3\u20ac'") + def test_first_utf8_coding_line_error(self): + src = (b'#coding:ascii \xc3\xa4\n' + b'raise RuntimeError\n') + self.check_script_error(src, br"(\(unicode error\) )?'ascii' codec can't decode byte") + + def test_second_utf8_coding_line_error(self): + src = (b'#!/usr/bin/python\n' + b'#coding:ascii \xc3\xa4\n' + b'raise RuntimeError\n') + self.check_script_error(src, br"(\(unicode error\) )?'ascii' codec can't decode byte") + def test_utf8_bom(self): src = (b'\xef\xbb\xbfprint(ascii("\xc3\xa4"))\n') self.check_script_output(src, br"'\xe4'") @@ -282,10 +294,80 @@ def test_utf8_bom_and_utf8_coding_line(self): b'print(ascii("\xc3\xa4"))\n') self.check_script_output(src, br"'\xe4'") - def test_utf8_non_utf8_comment_line_error(self): + def test_utf8_bom_and_non_utf8_first_coding_line(self): + src = (b'\xef\xbb\xbf#coding:iso-8859-15\n' + b'raise RuntimeError\n') + self.check_script_error(src, + br"encoding problem: iso-8859-15 with BOM", + lineno=1) + + def test_utf8_bom_and_non_utf8_second_coding_line(self): + src = (b'\xef\xbb\xbf#first\n' + b'#coding:iso-8859-15\n' + b'raise RuntimeError\n') + self.check_script_error(src, + br"encoding problem: iso-8859-15 with BOM", + lineno=2) + + def test_non_utf8_shebang(self): + src = (b'#!/home/\xa4/bin/python\n' + b'#coding:iso-8859-15\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\u20ac'") + + def test_utf8_shebang_error(self): + src = (b'#!/home/\xc3\xa4/bin/python\n' + b'#coding:ascii\n' + b'raise RuntimeError\n') + self.check_script_error(src, br"(\(unicode error\) )?'ascii' codec can't decode byte") + + def test_non_utf8_shebang_error(self): + src = (b'#!/home/\xa4/bin/python\n' + b'raise RuntimeError\n') + self.check_script_error(src, br"Non-UTF-8 code starting with .* on line 1", + lineno=1) + + def test_non_utf8_second_line_error(self): + src = (b'#first\n' + b'#second\xa4\n' + b'raise RuntimeError\n') + self.check_script_error(src, + br"Non-UTF-8 code starting with .* on line 2", + lineno=2) + + def test_non_utf8_third_line_error(self): + src = (b'#first\n' + b'#second\n' + b'#third\xa4\n' + b'raise RuntimeError\n') + self.check_script_error(src, + br"Non-UTF-8 code starting with .* on line 3", + lineno=3) + + def test_utf8_bom_non_utf8_third_line_error(self): + src = (b'\xef\xbb\xbf#first\n' + b'#second\n' + b'#third\xa4\n' + b'raise RuntimeError\n') + self.check_script_error(src, + br"Non-UTF-8 code starting with .* on line 3|" + br"'utf-8' codec can't decode byte", + lineno=3) + + def test_utf_8_non_utf8_third_line_error(self): + src = (b'#coding: utf-8\n' + b'#second\n' + b'#third\xa4\n' + b'raise RuntimeError\n') + self.check_script_error(src, + br"Non-UTF-8 code starting with .* on line 3|" + br"'utf-8' codec can't decode byte", + lineno=3) + + def test_utf8_non_utf8_third_line_error(self): src = (b'#coding: utf8\n' - b'#\n' - b'#\xa4\n' + b'#second\n' + b'#third\xa4\n' b'raise RuntimeError\n') self.check_script_error(src, br"'utf-8' codec can't decode byte|" @@ -326,7 +408,7 @@ def test_nul_in_second_coding_line(self): class UTF8ValidatorTest(unittest.TestCase): @unittest.skipIf(not sys.platform.startswith("linux"), "Too slow to run on non-Linux platforms") - @requires_resource('cpu') + @support.requires_resource('cpu') def test_invalid_utf8(self): # This is a port of test_utf8_decode_invalid_sequences in # test_unicode.py to exercise the separate utf8 validator in @@ -392,19 +474,29 @@ def check(content): check(b'\xF4'+cb+b'\xBF\xBF') +@support.force_not_colorized_test_class class BytesSourceEncodingTest(AbstractSourceEncodingTest, unittest.TestCase): def check_script_output(self, src, expected): - with captured_stdout() as stdout: + with support.captured_stdout() as stdout: exec(src) out = stdout.getvalue().encode('latin1') self.assertEqual(out.rstrip(), expected) - def check_script_error(self, src, expected): - with self.assertRaisesRegex(SyntaxError, expected.decode()) as cm: + def check_script_error(self, src, expected, lineno=...): + with self.assertRaises(SyntaxError) as cm: exec(src) + exc = cm.exception + self.assertRegex(str(exc), expected.decode()) + if lineno is not ...: + self.assertEqual(exc.lineno, lineno) + line = src.splitlines()[lineno-1].decode(errors='replace') + if lineno == 1: + line = line.removeprefix('\ufeff') + self.assertEqual(line, exc.text) +@support.force_not_colorized_test_class class FileSourceEncodingTest(AbstractSourceEncodingTest, unittest.TestCase): def check_script_output(self, src, expected): @@ -415,13 +507,22 @@ def check_script_output(self, src, expected): res = script_helper.assert_python_ok(fn) self.assertEqual(res.out.rstrip(), expected) - def check_script_error(self, src, expected): + def check_script_error(self, src, expected, lineno=...): with tempfile.TemporaryDirectory() as tmpd: fn = os.path.join(tmpd, 'test.py') with open(fn, 'wb') as fp: fp.write(src) res = script_helper.assert_python_failure(fn) - self.assertRegex(res.err.rstrip().splitlines()[-1], b'SyntaxError.*?' + expected) + err = res.err.rstrip() + self.assertRegex(err.splitlines()[-1], b'SyntaxError: ' + expected) + if lineno is not ...: + self.assertIn(f', line {lineno}\n'.encode(), + err.replace(os.linesep.encode(), b'\n')) + line = src.splitlines()[lineno-1].decode(errors='replace') + if lineno == 1: + line = line.removeprefix('\ufeff') + self.assertIn(line.encode(), err) + if __name__ == "__main__": diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-01-18-21-19.gh-issue-63161.ef1S6N.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-01-18-21-19.gh-issue-63161.ef1S6N.rst new file mode 100644 index 00000000000..5eafe0813dc --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-01-18-21-19.gh-issue-63161.ef1S6N.rst @@ -0,0 +1,5 @@ +Support non-UTF-8 shebang and comments in Python source files if non-UTF-8 +encoding is specified. Detect decoding error in comments for default (UTF-8) +encoding. Show the line and position of decoding error for default encoding +in a traceback. Show the line containing the coding cookie when it conflicts +with the BOM in a traceback. diff --git a/Parser/pegen_errors.c b/Parser/pegen_errors.c index f62b8695995..0639a4e4243 100644 --- a/Parser/pegen_errors.c +++ b/Parser/pegen_errors.c @@ -2,6 +2,7 @@ #include #include "pycore_pyerrors.h" // _PyErr_ProgramDecodedTextObject() +#include "pycore_runtime.h" // _Py_ID() #include "lexer/state.h" #include "lexer/lexer.h" #include "pegen.h" @@ -23,6 +24,13 @@ _PyPegen_raise_tokenizer_init_error(PyObject *filename) PyObject *value; PyObject *tback; PyErr_Fetch(&type, &value, &tback); + if (PyErr_GivenExceptionMatches(value, PyExc_SyntaxError)) { + if (PyObject_SetAttr(value, &_Py_ID(filename), filename)) { + goto error; + } + PyErr_Restore(type, value, tback); + return; + } errstr = PyObject_Str(value); if (!errstr) { goto error; diff --git a/Parser/tokenizer/file_tokenizer.c b/Parser/tokenizer/file_tokenizer.c index 01e473f58a0..8c836a3f725 100644 --- a/Parser/tokenizer/file_tokenizer.c +++ b/Parser/tokenizer/file_tokenizer.c @@ -282,10 +282,8 @@ tok_underflow_interactive(struct tok_state *tok) { } static int -tok_underflow_file(struct tok_state *tok) { - if (tok->start == NULL && !INSIDE_FSTRING(tok)) { - tok->cur = tok->inp = tok->buf; - } +tok_underflow_file(struct tok_state *tok) +{ if (tok->decoding_state == STATE_INIT) { /* We have not yet determined the encoding. If an encoding is found, use the file-pointer @@ -296,8 +294,16 @@ tok_underflow_file(struct tok_state *tok) { } assert(tok->decoding_state != STATE_INIT); } + int raw = tok->decoding_readline == NULL; + if (raw && tok->decoding_state != STATE_NORMAL) { + /* Keep the first line in the buffer to validate it later if + * the encoding has not yet been determined. */ + } + else if (tok->start == NULL && !INSIDE_FSTRING(tok)) { + tok->cur = tok->inp = tok->buf; + } /* Read until '\n' or EOF */ - if (tok->decoding_readline != NULL) { + if (!raw) { /* We already have a codec associated with this input. */ if (!tok_readline_recode(tok)) { return 0; @@ -328,20 +334,35 @@ tok_underflow_file(struct tok_state *tok) { ADVANCE_LINENO(); if (tok->decoding_state != STATE_NORMAL) { - if (tok->lineno > 2) { - tok->decoding_state = STATE_NORMAL; - } - else if (!_PyTokenizer_check_coding_spec(tok->cur, strlen(tok->cur), + if (!_PyTokenizer_check_coding_spec(tok->cur, strlen(tok->cur), tok, fp_setreadl)) { return 0; } + if (tok->lineno >= 2) { + tok->decoding_state = STATE_NORMAL; + } } - /* The default encoding is UTF-8, so make sure we don't have any - non-UTF-8 sequences in it. */ - if (!tok->encoding && !_PyTokenizer_ensure_utf8(tok->cur, tok)) { - _PyTokenizer_error_ret(tok); - return 0; + if (raw && tok->decoding_state == STATE_NORMAL) { + const char *line = tok->lineno <= 2 ? tok->buf : tok->cur; + int lineno = tok->lineno <= 2 ? 1 : tok->lineno; + if (!tok->encoding) { + /* The default encoding is UTF-8, so make sure we don't have any + non-UTF-8 sequences in it. */ + if (!_PyTokenizer_ensure_utf8(line, tok, lineno)) { + _PyTokenizer_error_ret(tok); + return 0; + } + } + else { + PyObject *tmp = PyUnicode_Decode(line, strlen(line), + tok->encoding, NULL); + if (tmp == NULL) { + _PyTokenizer_error_ret(tok); + return 0; + } + Py_DECREF(tmp); + } } assert(tok->done == E_OK); return tok->done == E_OK; diff --git a/Parser/tokenizer/helpers.c b/Parser/tokenizer/helpers.c index 5a416adb875..e5e2eed2d34 100644 --- a/Parser/tokenizer/helpers.c +++ b/Parser/tokenizer/helpers.c @@ -47,8 +47,10 @@ _syntaxerror_range(struct tok_state *tok, const char *format, goto error; } - args = Py_BuildValue("(O(OiiNii))", errmsg, tok->filename, tok->lineno, - col_offset, errtext, tok->lineno, end_col_offset); + args = Py_BuildValue("(O(OiiNii))", errmsg, + tok->filename ? tok->filename : Py_None, + tok->lineno, col_offset, errtext, + tok->lineno, end_col_offset); if (args) { PyErr_SetObject(PyExc_SyntaxError, args); Py_DECREF(args); @@ -422,10 +424,13 @@ _PyTokenizer_check_coding_spec(const char* line, Py_ssize_t size, struct tok_sta tok->encoding = cs; } else { /* then, compare cs with BOM */ if (strcmp(tok->encoding, cs) != 0) { - _PyTokenizer_error_ret(tok); - PyErr_Format(PyExc_SyntaxError, - "encoding problem: %s with BOM", cs); + tok->line_start = line; + tok->cur = (char *)line; + assert(size <= INT_MAX); + _PyTokenizer_syntaxerror_known_range(tok, 0, (int)size, + "encoding problem: %s with BOM", cs); PyMem_Free(cs); + _PyTokenizer_error_ret(tok); return 0; } PyMem_Free(cs); @@ -496,24 +501,38 @@ valid_utf8(const unsigned char* s) } int -_PyTokenizer_ensure_utf8(char *line, struct tok_state *tok) +_PyTokenizer_ensure_utf8(const char *line, struct tok_state *tok, int lineno) { - int badchar = 0; - unsigned char *c; + const char *badchar = NULL; + const char *c; int length; - for (c = (unsigned char *)line; *c; c += length) { - if (!(length = valid_utf8(c))) { - badchar = *c; + int col_offset = 0; + const char *line_start = line; + for (c = line; *c; c += length) { + if (!(length = valid_utf8((const unsigned char *)c))) { + badchar = c; break; } + col_offset++; + if (*c == '\n') { + lineno++; + col_offset = 0; + line_start = c + 1; + } } if (badchar) { - PyErr_Format(PyExc_SyntaxError, - "Non-UTF-8 code starting with '\\x%.2x' " - "in file %U on line %i, " - "but no encoding declared; " - "see https://peps.python.org/pep-0263/ for details", - badchar, tok->filename, tok->lineno); + tok->lineno = lineno; + tok->line_start = line_start; + tok->cur = (char *)badchar; + _PyTokenizer_syntaxerror_known_range(tok, + col_offset + 1, col_offset + 1, + "Non-UTF-8 code starting with '\\x%.2x'" + "%s%V on line %i, " + "but no encoding declared; " + "see https://peps.python.org/pep-0263/ for details", + (unsigned char)*badchar, + tok->filename ? " in file " : "", tok->filename, "", + lineno); return 0; } return 1; diff --git a/Parser/tokenizer/helpers.h b/Parser/tokenizer/helpers.h index 42ea13cd1f8..98f6445d5a3 100644 --- a/Parser/tokenizer/helpers.h +++ b/Parser/tokenizer/helpers.h @@ -26,7 +26,7 @@ int _PyTokenizer_check_bom(int get_char(struct tok_state *), struct tok_state *tok); int _PyTokenizer_check_coding_spec(const char* line, Py_ssize_t size, struct tok_state *tok, int set_readline(struct tok_state *, const char *)); -int _PyTokenizer_ensure_utf8(char *line, struct tok_state *tok); +int _PyTokenizer_ensure_utf8(const char *line, struct tok_state *tok, int lineno); #ifdef Py_DEBUG void _PyTokenizer_print_escape(FILE *f, const char *s, Py_ssize_t size); diff --git a/Parser/tokenizer/readline_tokenizer.c b/Parser/tokenizer/readline_tokenizer.c index 22f84c77a12..0f7769aeb8f 100644 --- a/Parser/tokenizer/readline_tokenizer.c +++ b/Parser/tokenizer/readline_tokenizer.c @@ -97,7 +97,7 @@ tok_underflow_readline(struct tok_state* tok) { ADVANCE_LINENO(); /* The default encoding is UTF-8, so make sure we don't have any non-UTF-8 sequences in it. */ - if (!tok->encoding && !_PyTokenizer_ensure_utf8(tok->cur, tok)) { + if (!tok->encoding && !_PyTokenizer_ensure_utf8(tok->cur, tok, tok->lineno)) { _PyTokenizer_error_ret(tok); return 0; } diff --git a/Parser/tokenizer/string_tokenizer.c b/Parser/tokenizer/string_tokenizer.c index 0c26d5df8d4..7299ecf483c 100644 --- a/Parser/tokenizer/string_tokenizer.c +++ b/Parser/tokenizer/string_tokenizer.c @@ -86,15 +86,18 @@ decode_str(const char *input, int single, struct tok_state *tok, int preserve_cr /* need to check line 1 and 2 separately since check_coding_spec assumes a single line as input */ if (newl[0]) { + tok->lineno = 1; if (!_PyTokenizer_check_coding_spec(str, newl[0] - str, tok, buf_setreadl)) { return NULL; } if (tok->enc == NULL && tok->decoding_state != STATE_NORMAL && newl[1]) { + tok->lineno = 2; if (!_PyTokenizer_check_coding_spec(newl[0]+1, newl[1] - newl[0], tok, buf_setreadl)) return NULL; } } + tok->lineno = 0; if (tok->enc != NULL) { assert(utf8 == NULL); utf8 = _PyTokenizer_translate_into_utf8(str, tok->enc); @@ -102,6 +105,9 @@ decode_str(const char *input, int single, struct tok_state *tok, int preserve_cr return _PyTokenizer_error_ret(tok); str = PyBytes_AS_STRING(utf8); } + else if (!_PyTokenizer_ensure_utf8(str, tok, 1)) { + return _PyTokenizer_error_ret(tok); + } assert(tok->decoding_buffer == NULL); tok->decoding_buffer = utf8; /* CAUTION */ return str; From 1c598e04361dbfc9cf465f3a02f83715c11b028c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 10 Oct 2025 16:29:18 +0300 Subject: [PATCH 102/373] gh-139065: Fix trailing space before long word in textwrap (GH-139070) Fix trailing space before a wrapped long word if the line length with a space is exactly "width". --- Lib/test/test_textwrap.py | 4 ++-- Lib/textwrap.py | 2 +- .../Library/2025-09-17-19-08-34.gh-issue-139065.Hu8fM5.rst | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-09-17-19-08-34.gh-issue-139065.Hu8fM5.rst diff --git a/Lib/test/test_textwrap.py b/Lib/test/test_textwrap.py index cbd383ea4e2..aca1f427656 100644 --- a/Lib/test/test_textwrap.py +++ b/Lib/test/test_textwrap.py @@ -605,7 +605,7 @@ def test_break_long(self): # bug 1146. Prevent a long word to be wrongly wrapped when the # preceding word is exactly one character shorter than the width self.check_wrap(self.text, 12, - ['Did you say ', + ['Did you say', '"supercalifr', 'agilisticexp', 'ialidocious?', @@ -633,7 +633,7 @@ def test_nobreak_long(self): def test_max_lines_long(self): self.check_wrap(self.text, 12, - ['Did you say ', + ['Did you say', '"supercalifr', 'agilisticexp', '[...]'], diff --git a/Lib/textwrap.py b/Lib/textwrap.py index 5ae439f5cd3..41366fbf443 100644 --- a/Lib/textwrap.py +++ b/Lib/textwrap.py @@ -211,7 +211,7 @@ def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width): # If we're allowed to break long words, then do so: put as much # of the next chunk onto the current line as will fit. - if self.break_long_words: + if self.break_long_words and space_left > 0: end = space_left chunk = reversed_chunks[-1] if self.break_on_hyphens and len(chunk) > space_left: diff --git a/Misc/NEWS.d/next/Library/2025-09-17-19-08-34.gh-issue-139065.Hu8fM5.rst b/Misc/NEWS.d/next/Library/2025-09-17-19-08-34.gh-issue-139065.Hu8fM5.rst new file mode 100644 index 00000000000..20c00603959 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-17-19-08-34.gh-issue-139065.Hu8fM5.rst @@ -0,0 +1,2 @@ +Fix trailing space before a wrapped long word if the line length is exactly +*width* in :mod:`textwrap`. From 302f19d1f1880dd29deb5ad02fa2c21639319559 Mon Sep 17 00:00:00 2001 From: Weilin Du <108666168+LamentXU123@users.noreply.github.com> Date: Fri, 10 Oct 2025 23:28:38 +0800 Subject: [PATCH 103/373] gh-139843: Document signals (SIGSTOP, SIGVTALRM, SIGPROF) to fix sphinx references (GH-139896) --- Doc/library/signal.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Doc/library/signal.rst b/Doc/library/signal.rst index 0c681c795c1..ccdf5027ff9 100644 --- a/Doc/library/signal.rst +++ b/Doc/library/signal.rst @@ -205,6 +205,12 @@ The variables defined in the :mod:`signal` module are: .. availability:: Unix. +.. data:: SIGPROF + + Profiling timer expired. + + .. availability:: Unix. + .. data:: SIGQUIT Terminal quit signal. @@ -215,6 +221,10 @@ The variables defined in the :mod:`signal` module are: Segmentation fault: invalid memory reference. +.. data:: SIGSTOP + + Stop executing (cannot be caught or ignored). + .. data:: SIGSTKFLT Stack fault on coprocessor. The Linux kernel does not raise this signal: it @@ -243,6 +253,12 @@ The variables defined in the :mod:`signal` module are: .. availability:: Unix. +.. data:: SIGVTALRM + + Virtual timer expired. + + .. availability:: Unix. + .. data:: SIGWINCH Window resize signal. From c7f1da97eb4639a17fb01ef122155bab2f262a34 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Fri, 10 Oct 2025 16:32:44 +0100 Subject: [PATCH 104/373] gh-101100: Document `zlib` public constants to fix reference warnings (#139835) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Victor Stinner --- Doc/library/zlib.rst | 136 ++++++++++++++++++++++++++++++++++++++++++- Doc/tools/.nitignore | 1 - 2 files changed, 133 insertions(+), 4 deletions(-) diff --git a/Doc/library/zlib.rst b/Doc/library/zlib.rst index 7c5e9b086e1..d4727f366d0 100644 --- a/Doc/library/zlib.rst +++ b/Doc/library/zlib.rst @@ -16,7 +16,7 @@ earlier than 1.1.3; 1.1.3 has a `security vulnerability `_ for authoritative information. For reading and writing ``.gz`` files see the :mod:`gzip` module. @@ -340,6 +340,136 @@ Decompression objects support the following methods and attributes: objects. +The following constants are available to configure compression and decompression +behavior: + +.. data:: DEFLATED + + The deflate compression method. + + +.. data:: MAX_WBITS + + The maximum window size, expressed as a power of 2. + For example, if :const:`!MAX_WBITS` is ``15`` it results in a window size + of ``32 KiB``. + + +.. data:: DEF_MEM_LEVEL + + The default memory level for compression objects. + + +.. data:: DEF_BUF_SIZE + + The default buffer size for decompression operations. + + +.. data:: Z_NO_COMPRESSION + + Compression level ``0``. + + .. versionadded:: 3.6 + + +.. data:: Z_BEST_SPEED + + Compression level ``1``. + + +.. data:: Z_BEST_COMPRESSION + + Compression level ``9``. + + +.. data:: Z_DEFAULT_COMPRESSION + + Default compression level (``-1``). + + +.. data:: Z_DEFAULT_STRATEGY + + Default compression strategy, for normal data. + + +.. data:: Z_FILTERED + + Compression strategy for data produced by a filter (or predictor). + + +.. data:: Z_HUFFMAN_ONLY + + Compression strategy that forces Huffman coding only. + + +.. data:: Z_RLE + + Compression strategy that limits match distances to one (run-length encoding). + + This constant is only available if Python was compiled with zlib + 1.2.0.1 or greater. + + .. versionadded:: 3.6 + + +.. data:: Z_FIXED + + Compression strategy that prevents the use of dynamic Huffman codes. + + This constant is only available if Python was compiled with zlib + 1.2.2.2 or greater. + + .. versionadded:: 3.6 + + +.. data:: Z_NO_FLUSH + + Flush mode ``0``. No special flushing behavior. + + .. versionadded:: 3.6 + + +.. data:: Z_PARTIAL_FLUSH + + Flush mode ``1``. Flush as much output as possible. + + +.. data:: Z_SYNC_FLUSH + + Flush mode ``2``. All output is flushed and the output is aligned to a byte boundary. + + +.. data:: Z_FULL_FLUSH + + Flush mode ``3``. All output is flushed and the compression state is reset. + + +.. data:: Z_FINISH + + Flush mode ``4``. All pending input is processed, no more input is expected. + + +.. data:: Z_BLOCK + + Flush mode ``5``. A deflate block is completed and emitted. + + This constant is only available if Python was compiled with zlib + 1.2.2.2 or greater. + + .. versionadded:: 3.6 + + +.. data:: Z_TREES + + Flush mode ``6``, for inflate operations. Instructs inflate to return when + it gets to the next deflate block boundary. + + This constant is only available if Python was compiled with zlib + 1.2.3.4 or greater. + + .. versionadded:: 3.6 + + Information about the version of the zlib library in use is available through the following constants: @@ -375,10 +505,10 @@ the following constants: Module :mod:`gzip` Reading and writing :program:`gzip`\ -format files. - http://www.zlib.net + https://www.zlib.net The zlib library home page. - http://www.zlib.net/manual.html + https://www.zlib.net/manual.html The zlib manual explains the semantics and usage of the library's many functions. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 827c5808fa5..657d720e766 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -51,7 +51,6 @@ Doc/library/xml.sax.reader.rst Doc/library/xml.sax.rst Doc/library/xmlrpc.client.rst Doc/library/xmlrpc.server.rst -Doc/library/zlib.rst Doc/whatsnew/2.4.rst Doc/whatsnew/2.5.rst Doc/whatsnew/2.6.rst From b881df47ff1adca515d1de04f689160ddae72142 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 10 Oct 2025 21:58:23 +0530 Subject: [PATCH 105/373] gh-139894: fix incorrect sharing of current task while forking in `asyncio` (#139897) Fix incorrect sharing of current task with the forked child process by clearing thread state's current task and current loop in `PyOS_AfterFork_Child`. --- Lib/test/test_asyncio/test_unix_events.py | 86 +++++++++++++------ ...-10-10-11-22-50.gh-issue-139894.ECAXqj.rst | 1 + Modules/posixmodule.c | 10 +++ 3 files changed, 72 insertions(+), 25 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-10-11-22-50.gh-issue-139894.ECAXqj.rst diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py index a69a5e32b1b..d2b3de3b9a4 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -1180,32 +1180,68 @@ async def runner(): @support.requires_fork() -class TestFork(unittest.IsolatedAsyncioTestCase): +class TestFork(unittest.TestCase): - async def test_fork_not_share_event_loop(self): - with warnings_helper.ignore_fork_in_thread_deprecation_warnings(): - # The forked process should not share the event loop with the parent - loop = asyncio.get_running_loop() - r, w = os.pipe() - self.addCleanup(os.close, r) - self.addCleanup(os.close, w) - pid = os.fork() - if pid == 0: - # child - try: - loop = asyncio.get_event_loop() - os.write(w, b'LOOP:' + str(id(loop)).encode()) - except RuntimeError: - os.write(w, b'NO LOOP') - except BaseException as e: - os.write(w, b'ERROR:' + ascii(e).encode()) - finally: - os._exit(0) - else: - # parent - result = os.read(r, 100) - self.assertEqual(result, b'NO LOOP') - wait_process(pid, exitcode=0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() + def test_fork_not_share_current_task(self): + loop = object() + task = object() + asyncio._set_running_loop(loop) + self.addCleanup(asyncio._set_running_loop, None) + asyncio.tasks._enter_task(loop, task) + self.addCleanup(asyncio.tasks._leave_task, loop, task) + self.assertIs(asyncio.current_task(), task) + r, w = os.pipe() + self.addCleanup(os.close, r) + self.addCleanup(os.close, w) + pid = os.fork() + if pid == 0: + # child + try: + asyncio._set_running_loop(loop) + current_task = asyncio.current_task() + if current_task is None: + os.write(w, b'NO TASK') + else: + os.write(w, b'TASK:' + str(id(current_task)).encode()) + except BaseException as e: + os.write(w, b'ERROR:' + ascii(e).encode()) + finally: + asyncio._set_running_loop(None) + os._exit(0) + else: + # parent + result = os.read(r, 100) + self.assertEqual(result, b'NO TASK') + wait_process(pid, exitcode=0) + + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() + def test_fork_not_share_event_loop(self): + # The forked process should not share the event loop with the parent + loop = object() + asyncio._set_running_loop(loop) + self.assertIs(asyncio.get_running_loop(), loop) + self.addCleanup(asyncio._set_running_loop, None) + r, w = os.pipe() + self.addCleanup(os.close, r) + self.addCleanup(os.close, w) + pid = os.fork() + if pid == 0: + # child + try: + loop = asyncio.get_event_loop() + os.write(w, b'LOOP:' + str(id(loop)).encode()) + except RuntimeError: + os.write(w, b'NO LOOP') + except BaseException as e: + os.write(w, b'ERROR:' + ascii(e).encode()) + finally: + os._exit(0) + else: + # parent + result = os.read(r, 100) + self.assertEqual(result, b'NO LOOP') + wait_process(pid, exitcode=0) @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @hashlib_helper.requires_hashdigest('md5') diff --git a/Misc/NEWS.d/next/Library/2025-10-10-11-22-50.gh-issue-139894.ECAXqj.rst b/Misc/NEWS.d/next/Library/2025-10-10-11-22-50.gh-issue-139894.ECAXqj.rst new file mode 100644 index 00000000000..05a977ad119 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-10-11-22-50.gh-issue-139894.ECAXqj.rst @@ -0,0 +1 @@ +Fix incorrect sharing of current task with the child process while forking in :mod:`asyncio`. Patch by Kumar Aditya. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 7a2e36bf294..8278902cbeb 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -689,6 +689,14 @@ reset_remotedebug_data(PyThreadState *tstate) _Py_MAX_SCRIPT_PATH_SIZE); } +static void +reset_asyncio_state(_PyThreadStateImpl *tstate) +{ + llist_init(&tstate->asyncio_tasks_head); + tstate->asyncio_running_loop = NULL; + tstate->asyncio_running_task = NULL; +} + void PyOS_AfterFork_Child(void) @@ -725,6 +733,8 @@ PyOS_AfterFork_Child(void) reset_remotedebug_data(tstate); + reset_asyncio_state((_PyThreadStateImpl *)tstate); + // Remove the dead thread states. We "start the world" once we are the only // thread state left to undo the stop the world call in `PyOS_BeforeFork`. // That needs to happen before `_PyThreadState_DeleteList`, because that From 49aaee7978c54211967392678072accc403d15f2 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Fri, 10 Oct 2025 19:08:55 +0100 Subject: [PATCH 106/373] pathlib ABCs: restore `relative_to()` and `is_relative_to()` (#138853) Restore `JoinablePath.[is_]relative_to()`, which were deleted in ef63cca494571f50906baae1d176469a3dcf8838. These methods are too useful to forgo. Restore old tests, and add new tests covering path classes with non-overridden `__eq__()` and `__hash__()`. Slightly simplify `PurePath.relative_to()` while we're in the area. No change to public APIs, because the pathlib ABCs are still private. --- Lib/pathlib/__init__.py | 7 ++-- Lib/pathlib/types.py | 27 +++++++++++++++ Lib/test/test_pathlib/test_join.py | 55 ++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 2 deletions(-) diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py index 8a892102cc0..6c07cd9ab01 100644 --- a/Lib/pathlib/__init__.py +++ b/Lib/pathlib/__init__.py @@ -490,16 +490,19 @@ def relative_to(self, other, *, walk_up=False): """ if not hasattr(other, 'with_segments'): other = self.with_segments(other) - for step, path in enumerate(chain([other], other.parents)): + parts = [] + for path in chain([other], other.parents): if path == self or path in self.parents: break elif not walk_up: raise ValueError(f"{str(self)!r} is not in the subpath of {str(other)!r}") elif path.name == '..': raise ValueError(f"'..' segment in {str(other)!r} cannot be walked") + else: + parts.append('..') else: raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors") - parts = ['..'] * step + self._tail[len(path._tail):] + parts.extend(self._tail[len(path._tail):]) return self._from_parsed_parts('', '', parts) def is_relative_to(self, other): diff --git a/Lib/pathlib/types.py b/Lib/pathlib/types.py index fea0dd305fe..f21ce077454 100644 --- a/Lib/pathlib/types.py +++ b/Lib/pathlib/types.py @@ -234,6 +234,33 @@ def parents(self): parent = split(path)[0] return tuple(parents) + def relative_to(self, other, *, walk_up=False): + """Return the relative path to another path identified by the passed + arguments. If the operation is not possible (because this is not + related to the other path), raise ValueError. + + The *walk_up* parameter controls whether `..` may be used to resolve + the path. + """ + parts = [] + for path in (other,) + other.parents: + if self.is_relative_to(path): + break + elif not walk_up: + raise ValueError(f"{self!r} is not in the subpath of {other!r}") + elif path.name == '..': + raise ValueError(f"'..' segment in {other!r} cannot be walked") + else: + parts.append('..') + else: + raise ValueError(f"{self!r} and {other!r} have different anchors") + return self.with_segments(*parts, *self.parts[len(path.parts):]) + + def is_relative_to(self, other): + """Return True if the path is relative to another path or False. + """ + return other == self or other in self.parents + def full_match(self, pattern): """ Return True if this path matches the given glob-style pattern. The diff --git a/Lib/test/test_pathlib/test_join.py b/Lib/test/test_pathlib/test_join.py index f1a24204b4c..2f4e79345f3 100644 --- a/Lib/test/test_pathlib/test_join.py +++ b/Lib/test/test_pathlib/test_join.py @@ -354,6 +354,61 @@ def test_with_suffix(self): self.assertRaises(ValueError, P('a/b').with_suffix, '.d/.') self.assertRaises(TypeError, P('a/b').with_suffix, None) + def test_relative_to(self): + P = self.cls + p = P('a/b') + self.assertEqual(p.relative_to(P('')), P('a', 'b')) + self.assertEqual(p.relative_to(P('a')), P('b')) + self.assertEqual(p.relative_to(P('a/b')), P('')) + self.assertEqual(p.relative_to(P(''), walk_up=True), P('a', 'b')) + self.assertEqual(p.relative_to(P('a'), walk_up=True), P('b')) + self.assertEqual(p.relative_to(P('a/b'), walk_up=True), P('')) + self.assertEqual(p.relative_to(P('a/c'), walk_up=True), P('..', 'b')) + self.assertEqual(p.relative_to(P('a/b/c'), walk_up=True), P('..')) + self.assertEqual(p.relative_to(P('c'), walk_up=True), P('..', 'a', 'b')) + self.assertRaises(ValueError, p.relative_to, P('c')) + self.assertRaises(ValueError, p.relative_to, P('a/b/c')) + self.assertRaises(ValueError, p.relative_to, P('a/c')) + self.assertRaises(ValueError, p.relative_to, P('/a')) + self.assertRaises(ValueError, p.relative_to, P('../a')) + self.assertRaises(ValueError, p.relative_to, P('a/..')) + self.assertRaises(ValueError, p.relative_to, P('/a/..')) + self.assertRaises(ValueError, p.relative_to, P('/'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('/a'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('../a'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('a/..'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('/a/..'), walk_up=True) + class Q(self.cls): + __eq__ = object.__eq__ + __hash__ = object.__hash__ + q = Q('a/b') + self.assertTrue(q.relative_to(q)) + self.assertRaises(ValueError, q.relative_to, Q('')) + self.assertRaises(ValueError, q.relative_to, Q('a')) + self.assertRaises(ValueError, q.relative_to, Q('a'), walk_up=True) + self.assertRaises(ValueError, q.relative_to, Q('a/b')) + self.assertRaises(ValueError, q.relative_to, Q('c')) + + def test_is_relative_to(self): + P = self.cls + p = P('a/b') + self.assertTrue(p.is_relative_to(P(''))) + self.assertTrue(p.is_relative_to(P('a'))) + self.assertTrue(p.is_relative_to(P('a/b'))) + self.assertFalse(p.is_relative_to(P('c'))) + self.assertFalse(p.is_relative_to(P('a/b/c'))) + self.assertFalse(p.is_relative_to(P('a/c'))) + self.assertFalse(p.is_relative_to(P('/a'))) + class Q(self.cls): + __eq__ = object.__eq__ + __hash__ = object.__hash__ + q = Q('a/b') + self.assertTrue(q.is_relative_to(q)) + self.assertFalse(q.is_relative_to(Q(''))) + self.assertFalse(q.is_relative_to(Q('a'))) + self.assertFalse(q.is_relative_to(Q('a/b'))) + self.assertFalse(q.is_relative_to(Q('c'))) + class LexicalPathJoinTest(JoinTestBase, unittest.TestCase): cls = LexicalPath From aa840f500ca901deea29669df68992193a273a62 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Fri, 10 Oct 2025 20:22:34 +0100 Subject: [PATCH 107/373] gh-138843: Removing "Unpacking" section from Download page (GH-139918) --- Doc/tools/templates/download.html | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Doc/tools/templates/download.html b/Doc/tools/templates/download.html index 523b505f596..f914ad86211 100644 --- a/Doc/tools/templates/download.html +++ b/Doc/tools/templates/download.html @@ -75,16 +75,6 @@

{% trans %}Download Python {{ dl_version }} documentation{% endtrans %}

See the directory listing for file sizes.{% endtrans %}

-

{% trans %}Unpacking{% endtrans %}

- -

{% trans %}Unix users should download the .tar.bz2 archives; these are bzipped tar -archives and can be handled in the usual way using tar and the bzip2 -program. The Info-ZIP unzip program can be -used to handle the ZIP archives if desired. The .tar.bz2 archives provide the -best compression and fastest download times.{% endtrans %}

- -

{% trans %}Windows users can use the ZIP archives since those are customary on that -platform. These are created on Unix using the Info-ZIP zip program.{% endtrans %}

{% trans %}Problems{% endtrans %}

{% set bugs = pathto('bugs') %} From d9b4eef71e7904fbe3a3786a908e493be7debbff Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Fri, 10 Oct 2025 17:20:18 -0400 Subject: [PATCH 108/373] gh-139001: Fix thread-safety issue in `pathlib.Path` (gh-139066) Don't cache the joined path in `_raw_path` because the caching isn't thread safe. --- Lib/pathlib/__init__.py | 7 +----- Lib/test/test_pathlib/test_join.py | 22 +++++++++++++++++++ ...-09-17-12-07-21.gh-issue-139001.O6tseN.rst | 2 ++ 3 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-09-17-12-07-21.gh-issue-139001.O6tseN.rst diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py index 6c07cd9ab01..51359cec8b0 100644 --- a/Lib/pathlib/__init__.py +++ b/Lib/pathlib/__init__.py @@ -336,13 +336,8 @@ def _raw_path(self): return paths[0] elif paths: # Join path segments from the initializer. - path = self.parser.join(*paths) - # Cache the joined path. - paths.clear() - paths.append(path) - return path + return self.parser.join(*paths) else: - paths.append('') return '' @property diff --git a/Lib/test/test_pathlib/test_join.py b/Lib/test/test_pathlib/test_join.py index 2f4e79345f3..4630210e492 100644 --- a/Lib/test/test_pathlib/test_join.py +++ b/Lib/test/test_pathlib/test_join.py @@ -3,6 +3,8 @@ """ import unittest +import threading +from test.support import threading_helper from .support import is_pypi from .support.lexical_path import LexicalPath @@ -158,6 +160,26 @@ def test_parts(self): parts = p.parts self.assertEqual(parts, (sep, 'a', 'b')) + @threading_helper.requires_working_threading() + def test_parts_multithreaded(self): + P = self.cls + + NUM_THREADS = 10 + NUM_ITERS = 10 + + for _ in range(NUM_ITERS): + b = threading.Barrier(NUM_THREADS) + path = P('a') / 'b' / 'c' / 'd' / 'e' + expected = ('a', 'b', 'c', 'd', 'e') + + def check_parts(): + b.wait() + self.assertEqual(path.parts, expected) + + threads = [threading.Thread(target=check_parts) for _ in range(NUM_THREADS)] + with threading_helper.start_threads(threads): + pass + def test_parent(self): # Relative P = self.cls diff --git a/Misc/NEWS.d/next/Library/2025-09-17-12-07-21.gh-issue-139001.O6tseN.rst b/Misc/NEWS.d/next/Library/2025-09-17-12-07-21.gh-issue-139001.O6tseN.rst new file mode 100644 index 00000000000..3ad5a1272df --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-17-12-07-21.gh-issue-139001.O6tseN.rst @@ -0,0 +1,2 @@ +Fix race condition in :class:`pathlib.Path` on the internal ``_raw_paths`` +field. From ff7bb565d836162eed0851c36afa325a107a5a56 Mon Sep 17 00:00:00 2001 From: Dino Viehland Date: Fri, 10 Oct 2025 15:25:38 -0700 Subject: [PATCH 109/373] gh-139924: Add PyFunction_PYFUNC_EVENT_MODIFY_QUALNAME event for function watchers (#139925) Add PyFunction_PYFUNC_EVENT_MODIFY_QUALNAME event for function watchers --- Doc/c-api/function.rst | 3 +++ Include/cpython/funcobject.h | 3 ++- Lib/test/test_capi/test_watchers.py | 4 ++++ .../next/C_API/2025-10-10-20-59-07.gh-issue-139924.ALByCb.rst | 1 + Objects/funcobject.c | 2 ++ 5 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/C_API/2025-10-10-20-59-07.gh-issue-139924.ALByCb.rst diff --git a/Doc/c-api/function.rst b/Doc/c-api/function.rst index 5fb8567ef8c..764b2ac610b 100644 --- a/Doc/c-api/function.rst +++ b/Doc/c-api/function.rst @@ -175,6 +175,9 @@ There are a few functions specific to Python functions. .. versionadded:: 3.12 + - ``PyFunction_PYFUNC_EVENT_MODIFY_QUALNAME`` + + .. versionadded:: 3.15 .. c:type:: int (*PyFunction_WatchCallback)(PyFunction_WatchEvent event, PyFunctionObject *func, PyObject *new_value) diff --git a/Include/cpython/funcobject.h b/Include/cpython/funcobject.h index 598cd330bc9..9e1599a7648 100644 --- a/Include/cpython/funcobject.h +++ b/Include/cpython/funcobject.h @@ -134,7 +134,8 @@ PyAPI_FUNC(PyObject *) PyStaticMethod_New(PyObject *); V(DESTROY) \ V(MODIFY_CODE) \ V(MODIFY_DEFAULTS) \ - V(MODIFY_KWDEFAULTS) + V(MODIFY_KWDEFAULTS) \ + V(MODIFY_QUALNAME) typedef enum { #define PY_DEF_EVENT(EVENT) PyFunction_EVENT_##EVENT, diff --git a/Lib/test/test_capi/test_watchers.py b/Lib/test/test_capi/test_watchers.py index 8644479d83d..bef72032513 100644 --- a/Lib/test/test_capi/test_watchers.py +++ b/Lib/test/test_capi/test_watchers.py @@ -514,6 +514,10 @@ def myfunc(): _testcapi.set_func_kwdefaults_via_capi(myfunc, new_kwdefaults) self.assertIn((_testcapi.PYFUNC_EVENT_MODIFY_KWDEFAULTS, myfunc, new_kwdefaults), events) + new_qualname = "foo.bar" + myfunc.__qualname__ = new_qualname + self.assertIn((_testcapi.PYFUNC_EVENT_MODIFY_QUALNAME, myfunc, new_qualname), events) + # Clear events reference to func events = [] del myfunc diff --git a/Misc/NEWS.d/next/C_API/2025-10-10-20-59-07.gh-issue-139924.ALByCb.rst b/Misc/NEWS.d/next/C_API/2025-10-10-20-59-07.gh-issue-139924.ALByCb.rst new file mode 100644 index 00000000000..a53d5d068d7 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2025-10-10-20-59-07.gh-issue-139924.ALByCb.rst @@ -0,0 +1 @@ +Function watchers can now receive a PyFunction_PYFUNC_EVENT_MODIFY_QUALNAME event when a watched functions qualname is changed. diff --git a/Objects/funcobject.c b/Objects/funcobject.c index d8a10075578..43198aaf8a7 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -62,6 +62,7 @@ handle_func_event(PyFunction_WatchEvent event, PyFunctionObject *func, case PyFunction_EVENT_MODIFY_CODE: case PyFunction_EVENT_MODIFY_DEFAULTS: case PyFunction_EVENT_MODIFY_KWDEFAULTS: + case PyFunction_EVENT_MODIFY_QUALNAME: RARE_EVENT_INTERP_INC(interp, func_modification); break; default: @@ -747,6 +748,7 @@ func_set_qualname(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) "__qualname__ must be set to a string object"); return -1; } + handle_func_event(PyFunction_EVENT_MODIFY_QUALNAME, (PyFunctionObject *) op, value); Py_XSETREF(op->func_qualname, Py_NewRef(value)); return 0; } From d4e5802588db3459f04d4b8013cc571a8988e203 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Sat, 11 Oct 2025 02:37:48 +0100 Subject: [PATCH 110/373] gh-96491: Deduplicate version in IDLE shell title (#139841) Saving to a file added both the filename and repeated the version. --------- Co-authored-by: Terry Jan Reedy --- Lib/idlelib/CREDITS.txt | 1 + Lib/idlelib/editor.py | 7 +++++-- Lib/idlelib/idle_test/test_outwin.py | 3 ++- Lib/idlelib/pyshell.py | 3 +-- .../IDLE/2025-10-09-12-53-47.gh-issue-96491.4YKxvy.rst | 1 + 5 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/IDLE/2025-10-09-12-53-47.gh-issue-96491.4YKxvy.rst diff --git a/Lib/idlelib/CREDITS.txt b/Lib/idlelib/CREDITS.txt index bea3ba7c20d..1b853e8cc1c 100644 --- a/Lib/idlelib/CREDITS.txt +++ b/Lib/idlelib/CREDITS.txt @@ -37,6 +37,7 @@ Major contributors since 2005: - 2014: Saimadhav Heblikar - 2015: Mark Roseman - 2017: Louie Lu, Cheryl Sabella, and Serhiy Storchaka +- 2025: Stan Ulbrych For additional details refer to NEWS.txt and Changelog. diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index b4d6d25871b..83112d85575 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -33,7 +33,6 @@ # The default tab setting for a Text widget, in average-width characters. TK_TABWIDTH_DEFAULT = 8 -_py_version = ' (%s)' % platform.python_version() darwin = sys.platform == 'darwin' def _sphinx_version(): @@ -1008,12 +1007,16 @@ def open_recent_file(fn_closure=file_name): def saved_change_hook(self): short = self.short_title() long = self.long_title() + _py_version = ' (%s)' % platform.python_version() if short and long and not macosx.isCocoaTk(): # Don't use both values on macOS because # that doesn't match platform conventions. title = short + " - " + long + _py_version elif short: - title = short + if short == "IDLE Shell": + title = short + " " + platform.python_version() + else: + title = short + _py_version elif long: title = long else: diff --git a/Lib/idlelib/idle_test/test_outwin.py b/Lib/idlelib/idle_test/test_outwin.py index 81f4aad7e95..0f13363f84f 100644 --- a/Lib/idlelib/idle_test/test_outwin.py +++ b/Lib/idlelib/idle_test/test_outwin.py @@ -1,6 +1,7 @@ "Test outwin, coverage 76%." from idlelib import outwin +import platform import sys import unittest from test.support import requires @@ -41,7 +42,7 @@ def test_ispythonsource(self): self.assertFalse(w.ispythonsource(__file__)) def test_window_title(self): - self.assertEqual(self.window.top.title(), 'Output') + self.assertEqual(self.window.top.title(), 'Output' + ' (%s)' % platform.python_version()) def test_maybesave(self): w = self.window diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 74a0e03994f..1b7c2af1a92 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -22,7 +22,6 @@ import linecache import os import os.path -from platform import python_version import re import socket import subprocess @@ -841,7 +840,7 @@ def display_executing_dialog(self): class PyShell(OutputWindow): from idlelib.squeezer import Squeezer - shell_title = "IDLE Shell " + python_version() + shell_title = "IDLE Shell" # Override classes ColorDelegator = ModifiedColorDelegator diff --git a/Misc/NEWS.d/next/IDLE/2025-10-09-12-53-47.gh-issue-96491.4YKxvy.rst b/Misc/NEWS.d/next/IDLE/2025-10-09-12-53-47.gh-issue-96491.4YKxvy.rst new file mode 100644 index 00000000000..beb6ef5ade5 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2025-10-09-12-53-47.gh-issue-96491.4YKxvy.rst @@ -0,0 +1 @@ +Deduplicate version number in IDLE shell title bar after saving to a file. From 897a36baddb3611865a839c6345315da689c52a1 Mon Sep 17 00:00:00 2001 From: yihong Date: Sat, 11 Oct 2025 20:32:57 +0800 Subject: [PATCH 111/373] gh-139935: fix `test_os.test_getlogin` on some platforms (#139936) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This amends 4e7e2dd043c1da85b0c157d3ed24866b77e83a4f to catch errors that `os.getlogin` can raise as specified by POSIX and Linux/glibc [1]. [1]: https://man7.org/linux/man-pages/man3/getlogin.3.html#ERRORS --------- Signed-off-by: yihong0618 Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/test/test_os/test_os.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py index 371771087aa..95b175db6fc 100644 --- a/Lib/test/test_os/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -3203,7 +3203,14 @@ def test_getlogin(self): try: user_name = os.getlogin() except OSError as exc: - if exc.errno in (errno.ENOTTY, errno.ENXIO): + # See https://man7.org/linux/man-pages/man3/getlogin.3.html#ERRORS. + allowed_errors = ( + # defined by POSIX + errno.EMFILE, errno.ENFILE, errno.ENXIO, errno.ERANGE, + # defined by Linux/glibc + errno.ENOENT, errno.ENOMEM, errno.ENOTTY, + ) + if exc.errno in allowed_errors: self.skipTest(str(exc)) else: raise From 2eb32add92d553b320850975442fa2e55addc377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 11 Oct 2025 16:31:34 +0200 Subject: [PATCH 112/373] gh-139935: do not skip test on real errors in `os.getlogin` (#139953) --- Lib/test/test_os/test_os.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py index 95b175db6fc..e074858fe2a 100644 --- a/Lib/test/test_os/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -3204,13 +3204,7 @@ def test_getlogin(self): user_name = os.getlogin() except OSError as exc: # See https://man7.org/linux/man-pages/man3/getlogin.3.html#ERRORS. - allowed_errors = ( - # defined by POSIX - errno.EMFILE, errno.ENFILE, errno.ENXIO, errno.ERANGE, - # defined by Linux/glibc - errno.ENOENT, errno.ENOMEM, errno.ENOTTY, - ) - if exc.errno in allowed_errors: + if exc.errno in (errno.ENXIO, errno.ENOENT, errno.ENOTTY): self.skipTest(str(exc)) else: raise From 5776d0d2e08f4d93dcd62d875bae8c1396a04ba4 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Sat, 11 Oct 2025 16:14:29 +0100 Subject: [PATCH 113/373] gh-139905: Provide suggestion in error message if `Generic.__init_subclass__` was not called (#139943) --- Lib/test/test_typing.py | 28 +++++++++++++++++++ Lib/typing.py | 16 +++++++++-- ...-10-11-10-02-56.gh-issue-139905.UyJIR_.rst | 3 ++ 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-11-10-02-56.gh-issue-139905.UyJIR_.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 428089d88cc..bc7f14f90a7 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4722,6 +4722,34 @@ class D(Generic[T]): pass with self.assertRaises(TypeError): D[()] + def test_generic_init_subclass_not_called_error(self): + notes = ["Note: this exception may have been caused by " + r"'GenericTests.test_generic_init_subclass_not_called_error..Base.__init_subclass__' " + "(or the '__init_subclass__' method on a superclass) not calling 'super().__init_subclass__()'"] + + class Base: + def __init_subclass__(cls) -> None: + # Oops, I forgot super().__init_subclass__()! + pass + + with self.subTest(): + class Sub(Base, Generic[T]): + pass + + with self.assertRaises(AttributeError) as cm: + Sub[int] + + self.assertEqual(cm.exception.__notes__, notes) + + with self.subTest(): + class Sub[U](Base): + pass + + with self.assertRaises(AttributeError) as cm: + Sub[int] + + self.assertEqual(cm.exception.__notes__, notes) + def test_generic_subclass_checks(self): for typ in [list[int], List[int], tuple[int, str], Tuple[int, str], diff --git a/Lib/typing.py b/Lib/typing.py index 4311a77b8db..03d5357d4cf 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1158,14 +1158,26 @@ def _generic_class_getitem(cls, args): f"Parameters to {cls.__name__}[...] must all be unique") else: # Subscripting a regular Generic subclass. - for param in cls.__parameters__: + try: + parameters = cls.__parameters__ + except AttributeError as e: + init_subclass = getattr(cls, '__init_subclass__', None) + if init_subclass not in {None, Generic.__init_subclass__}: + e.add_note( + f"Note: this exception may have been caused by " + f"{init_subclass.__qualname__!r} (or the " + f"'__init_subclass__' method on a superclass) not " + f"calling 'super().__init_subclass__()'" + ) + raise + for param in parameters: prepare = getattr(param, '__typing_prepare_subst__', None) if prepare is not None: args = prepare(cls, args) _check_generic_specialization(cls, args) new_args = [] - for param, new_arg in zip(cls.__parameters__, args): + for param, new_arg in zip(parameters, args): if isinstance(param, TypeVarTuple): new_args.extend(new_arg) else: diff --git a/Misc/NEWS.d/next/Library/2025-10-11-10-02-56.gh-issue-139905.UyJIR_.rst b/Misc/NEWS.d/next/Library/2025-10-11-10-02-56.gh-issue-139905.UyJIR_.rst new file mode 100644 index 00000000000..a6876ca2df8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-11-10-02-56.gh-issue-139905.UyJIR_.rst @@ -0,0 +1,3 @@ +Add suggestion to error message for :class:`typing.Generic` subclasses when +``cls.__parameters__`` is missing due to a parent class failing to call +:meth:`super().__init_subclass__() ` in its ``__init_subclass__``. From cdd3eee7fc26538c8365dcbf2dd844ec7cdf7fb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 11 Oct 2025 19:34:08 +0200 Subject: [PATCH 114/373] gh-139929: fix incorrect OpenSSL version-based guard in `_ssl.c` (GH-139945) fix OpenSSL version-based guards --- Modules/_ssl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 1fa44ef1de4..4b75e455f40 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -937,7 +937,7 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, } /* bpo43522 and OpenSSL < 1.1.1l: copy hostflags manually */ -#if OPENSSL_VERSION < 0x101010cf +#if OPENSSL_VERSION_NUMBER < 0x101010cf X509_VERIFY_PARAM *ssl_verification_params = SSL_get0_param(self->ssl); X509_VERIFY_PARAM *ssl_ctx_verification_params = SSL_CTX_get0_param(ctx); From 447c7a89fb41b7fa84b9b26f111aedd649bc5400 Mon Sep 17 00:00:00 2001 From: Weilin Du <108666168+LamentXU123@users.noreply.github.com> Date: Sun, 12 Oct 2025 02:26:48 +0800 Subject: [PATCH 115/373] gh-101100: Fix Sphinx warnings in `Doc/library/signal.rst` (GH-139930) --- Doc/library/signal.rst | 7 +++---- Doc/tools/.nitignore | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Doc/library/signal.rst b/Doc/library/signal.rst index ccdf5027ff9..47f824488f5 100644 --- a/Doc/library/signal.rst +++ b/Doc/library/signal.rst @@ -270,7 +270,7 @@ The variables defined in the :mod:`signal` module are: All the signal numbers are defined symbolically. For example, the hangup signal is defined as :const:`signal.SIGHUP`; the variable names are identical to the names used in C programs, as found in ````. The Unix man page for - ':c:func:`signal`' lists the existing signals (on some systems this is + '``signal``' lists the existing signals (on some systems this is :manpage:`signal(2)`, on others the list is in :manpage:`signal(7)`). Note that not all systems define the same set of signal names; only those names defined by the system are defined by this module. @@ -666,9 +666,8 @@ The :mod:`signal` module defines the following functions: *sigset*. The return value is an object representing the data contained in the - :c:type:`siginfo_t` structure, namely: :attr:`si_signo`, :attr:`si_code`, - :attr:`si_errno`, :attr:`si_pid`, :attr:`si_uid`, :attr:`si_status`, - :attr:`si_band`. + ``siginfo_t`` structure, namely: ``si_signo``, ``si_code``, + ``si_errno``, ``si_pid``, ``si_uid``, ``si_status``, ``si_band``. .. availability:: Unix. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 657d720e766..60a80fe2c72 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -29,7 +29,6 @@ Doc/library/profile.rst Doc/library/pyexpat.rst Doc/library/resource.rst Doc/library/select.rst -Doc/library/signal.rst Doc/library/smtplib.rst Doc/library/socket.rst Doc/library/ssl.rst From 166cdaa6fb06c0ec802fd6910c117d809f818ede Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 11 Oct 2025 22:58:14 +0200 Subject: [PATCH 116/373] gh-111489: Remove _PyTuple_FromArray() alias (#139973) Replace _PyTuple_FromArray() with PyTuple_FromArray(). Remove pycore_tuple.h includes. --- Include/internal/pycore_tuple.h | 3 --- Lib/test/clinic.test.c | 16 ++++++------- Modules/_testclinic.c | 6 ++--- Modules/clinic/_testclinic.c.h | 34 ++++++++++++++-------------- Modules/clinic/_testclinic_depr.c.h | 4 ++-- Modules/clinic/_testclinic_kwds.c.h | 4 ++-- Modules/clinic/gcmodule.c.h | 7 +++--- Modules/gcmodule.c | 1 - Modules/itertoolsmodule.c | 8 +++---- Objects/call.c | 2 +- Objects/descrobject.c | 4 ++-- Objects/exceptions.c | 3 +-- Objects/listobject.c | 10 ++++---- Objects/structseq.c | 4 ++-- Objects/tupleobject.c | 2 +- Objects/unicodeobject.c | 3 +-- Python/bltinmodule.c | 6 ++--- Python/ceval.c | 2 +- Python/clinic/sysmodule.c.h | 5 ++-- Python/intrinsics.c | 3 +-- Python/optimizer.c | 2 +- Python/optimizer_symbols.c | 3 +-- Tools/clinic/libclinic/converters.py | 5 ++-- 23 files changed, 63 insertions(+), 74 deletions(-) diff --git a/Include/internal/pycore_tuple.h b/Include/internal/pycore_tuple.h index be1961cbf77..46db02593ad 100644 --- a/Include/internal/pycore_tuple.h +++ b/Include/internal/pycore_tuple.h @@ -23,9 +23,6 @@ extern PyStatus _PyTuple_InitGlobalObjects(PyInterpreterState *); #define _PyTuple_ITEMS(op) _Py_RVALUE(_PyTuple_CAST(op)->ob_item) -// Alias for backward compatibility -#define _PyTuple_FromArray PyTuple_FromArray - PyAPI_FUNC(PyObject *)_PyTuple_FromStackRefStealOnSuccess(const union _PyStackRef *, Py_ssize_t); PyAPI_FUNC(PyObject *)_PyTuple_FromArraySteal(PyObject *const *, Py_ssize_t); diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index ed9058899c0..4729708efd3 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -4341,7 +4341,7 @@ test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t narg goto exit; } a = args[0]; - __clinic_args = _PyTuple_FromArray(args + 1, nargs - 1); + __clinic_args = PyTuple_FromArray(args + 1, nargs - 1); if (__clinic_args == NULL) { goto exit; } @@ -4356,7 +4356,7 @@ test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t narg static PyObject * test_vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=0c11c475e240869e input=2c49a482f68545c0]*/ +/*[clinic end generated code: output=83cbe9554d04add2 input=2c49a482f68545c0]*/ /*[clinic input] test_vararg @@ -4421,7 +4421,7 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject } a = fastargs[0]; __clinic_args = nargs > 1 - ? _PyTuple_FromArray(args + 1, nargs - 1) + ? PyTuple_FromArray(args + 1, nargs - 1) : PyTuple_New(0); if (__clinic_args == NULL) { goto exit; @@ -4437,7 +4437,7 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject static PyObject * test_vararg_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=17ba625cdd0369c1 input=7448995636d9186a]*/ +/*[clinic end generated code: output=d773f7b54e61f73a input=7448995636d9186a]*/ /*[clinic input] test_vararg_with_default @@ -4514,7 +4514,7 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar } skip_optional_kwonly: __clinic_args = nargs > 1 - ? _PyTuple_FromArray(args + 1, nargs - 1) + ? PyTuple_FromArray(args + 1, nargs - 1) : PyTuple_New(0); if (__clinic_args == NULL) { goto exit; @@ -4531,7 +4531,7 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar static PyObject * test_vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args, int b) -/*[clinic end generated code: output=3f2b06ab08d5d0be input=3a0f9f557ce1f712]*/ +/*[clinic end generated code: output=d25e56802c197344 input=3a0f9f557ce1f712]*/ /*[clinic input] test_vararg_with_only_defaults @@ -4612,7 +4612,7 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize } c = fastargs[1]; skip_optional_kwonly: - __clinic_args = _PyTuple_FromArray(args, nargs); + __clinic_args = PyTuple_FromArray(args, nargs); if (__clinic_args == NULL) { goto exit; } @@ -4628,7 +4628,7 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize static PyObject * test_vararg_with_only_defaults_impl(PyObject *module, PyObject *args, int b, PyObject *c) -/*[clinic end generated code: output=f46666f0b1bf86b9 input=6983e66817f82924]*/ +/*[clinic end generated code: output=7366943a7df42e05 input=6983e66817f82924]*/ /*[clinic input] test_paramname_module diff --git a/Modules/_testclinic.c b/Modules/_testclinic.c index 5c196c0dd0f..890f2201b46 100644 --- a/Modules/_testclinic.c +++ b/Modules/_testclinic.c @@ -63,7 +63,7 @@ pack_arguments_2pos_varpos(PyObject *a, PyObject *b, PyObject * const *args, Py_ssize_t args_length) /*[clinic end generated code: output=267032f41bd039cc input=86ee3064b7853e86]*/ { - PyObject *tuple = _PyTuple_FromArray(args, args_length); + PyObject *tuple = PyTuple_FromArray(args, args_length); if (tuple == NULL) { return NULL; } @@ -1174,7 +1174,7 @@ varpos_array_impl(PyObject *module, PyObject * const *args, Py_ssize_t args_length) /*[clinic end generated code: output=a25f42f39c9b13ad input=97b8bdcf87e019c7]*/ { - return _PyTuple_FromArray(args, args_length); + return PyTuple_FromArray(args, args_length); } @@ -1610,7 +1610,7 @@ _testclinic_TestClass_varpos_array_no_fastcall_impl(PyTypeObject *type, Py_ssize_t args_length) /*[clinic end generated code: output=27c9da663e942617 input=9ba5ae1f1eb58777]*/ { - return _PyTuple_FromArray(args, args_length); + return PyTuple_FromArray(args, args_length); } diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h index 7e971f7ad73..9bcd0eeb008 100644 --- a/Modules/clinic/_testclinic.c.h +++ b/Modules/clinic/_testclinic.c.h @@ -9,7 +9,7 @@ preserve #include "pycore_long.h" // _PyLong_UnsignedShort_Converter() #include "pycore_modsupport.h" // _PyArg_CheckPositional() #include "pycore_runtime.h" // _Py_ID() -#include "pycore_tuple.h" // _PyTuple_FromArray() +#include "pycore_tuple.h" // _PyTuple_ITEMS() PyDoc_STRVAR(test_empty_function__doc__, "test_empty_function($module, /)\n" @@ -2764,7 +2764,7 @@ varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *return_value = NULL; PyObject *__clinic_args = NULL; - __clinic_args = _PyTuple_FromArray(args, nargs); + __clinic_args = PyTuple_FromArray(args, nargs); if (__clinic_args == NULL) { goto exit; } @@ -2802,7 +2802,7 @@ posonly_varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs) } a = args[0]; b = args[1]; - __clinic_args = _PyTuple_FromArray(args + 2, nargs - 2); + __clinic_args = PyTuple_FromArray(args + 2, nargs - 2); if (__clinic_args == NULL) { goto exit; } @@ -2845,7 +2845,7 @@ posonly_req_opt_varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs b = args[1]; skip_optional: __clinic_args = nargs > 2 - ? _PyTuple_FromArray(args + 2, nargs - 2) + ? PyTuple_FromArray(args + 2, nargs - 2) : PyTuple_New(0); if (__clinic_args == NULL) { goto exit; @@ -2916,7 +2916,7 @@ posonly_poskw_varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs, a = fastargs[0]; b = fastargs[1]; __clinic_args = nargs > 2 - ? _PyTuple_FromArray(args + 2, nargs - 2) + ? PyTuple_FromArray(args + 2, nargs - 2) : PyTuple_New(0); if (__clinic_args == NULL) { goto exit; @@ -2984,7 +2984,7 @@ poskw_varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject } a = fastargs[0]; __clinic_args = nargs > 1 - ? _PyTuple_FromArray(args + 1, nargs - 1) + ? PyTuple_FromArray(args + 1, nargs - 1) : PyTuple_New(0); if (__clinic_args == NULL) { goto exit; @@ -3063,7 +3063,7 @@ poskw_varpos_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t narg } skip_optional_kwonly: __clinic_args = nargs > 1 - ? _PyTuple_FromArray(args + 1, nargs - 1) + ? PyTuple_FromArray(args + 1, nargs - 1) : PyTuple_New(0); if (__clinic_args == NULL) { goto exit; @@ -3146,7 +3146,7 @@ poskw_varpos_kwonly_opt2(PyObject *module, PyObject *const *args, Py_ssize_t nar c = fastargs[2]; skip_optional_kwonly: __clinic_args = nargs > 1 - ? _PyTuple_FromArray(args + 1, nargs - 1) + ? PyTuple_FromArray(args + 1, nargs - 1) : PyTuple_New(0); if (__clinic_args == NULL) { goto exit; @@ -3218,7 +3218,7 @@ varpos_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO } b = fastargs[0]; skip_optional_kwonly: - __clinic_args = _PyTuple_FromArray(args, nargs); + __clinic_args = PyTuple_FromArray(args, nargs); if (__clinic_args == NULL) { goto exit; } @@ -3299,7 +3299,7 @@ varpos_kwonly_req_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, } c = fastargs[2]; skip_optional_kwonly: - __clinic_args = _PyTuple_FromArray(args, nargs); + __clinic_args = PyTuple_FromArray(args, nargs); if (__clinic_args == NULL) { goto exit; } @@ -3549,7 +3549,7 @@ gh_32092_oob(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject kw2 = fastargs[3]; skip_optional_kwonly: varargs = nargs > 2 - ? _PyTuple_FromArray(args + 2, nargs - 2) + ? PyTuple_FromArray(args + 2, nargs - 2) : PyTuple_New(0); if (varargs == NULL) { goto exit; @@ -3626,7 +3626,7 @@ gh_32092_kw_pass(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb kw = fastargs[1]; skip_optional_kwonly: __clinic_args = nargs > 1 - ? _PyTuple_FromArray(args + 1, nargs - 1) + ? PyTuple_FromArray(args + 1, nargs - 1) : PyTuple_New(0); if (__clinic_args == NULL) { goto exit; @@ -3658,7 +3658,7 @@ gh_99233_refcount(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *return_value = NULL; PyObject *__clinic_args = NULL; - __clinic_args = _PyTuple_FromArray(args, nargs); + __clinic_args = PyTuple_FromArray(args, nargs); if (__clinic_args == NULL) { goto exit; } @@ -3771,7 +3771,7 @@ null_or_tuple_for_varargs(PyObject *module, PyObject *const *args, Py_ssize_t na } skip_optional_kwonly: constraints = nargs > 1 - ? _PyTuple_FromArray(args + 1, nargs - 1) + ? PyTuple_FromArray(args + 1, nargs - 1) : PyTuple_New(0); if (constraints == NULL) { goto exit; @@ -4174,7 +4174,7 @@ _testclinic_TestClass_defclass_varpos(PyObject *self, PyTypeObject *cls, PyObjec if (!fastargs) { goto exit; } - __clinic_args = _PyTuple_FromArray(args, nargs); + __clinic_args = PyTuple_FromArray(args, nargs); if (__clinic_args == NULL) { goto exit; } @@ -4231,7 +4231,7 @@ _testclinic_TestClass_defclass_posonly_varpos(PyObject *self, PyTypeObject *cls, } a = fastargs[0]; b = fastargs[1]; - __clinic_args = _PyTuple_FromArray(args + 2, nargs - 2); + __clinic_args = PyTuple_FromArray(args + 2, nargs - 2); if (__clinic_args == NULL) { goto exit; } @@ -4600,4 +4600,4 @@ _testclinic_TestClass_posonly_poskw_varpos_array_no_fastcall(PyObject *type, PyO exit: return return_value; } -/*[clinic end generated code: output=0764e6f8c9d94057 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=290d2e346ea7bfa1 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_testclinic_depr.c.h b/Modules/clinic/_testclinic_depr.c.h index a46d238801b..e2db4fd87ed 100644 --- a/Modules/clinic/_testclinic_depr.c.h +++ b/Modules/clinic/_testclinic_depr.c.h @@ -9,7 +9,7 @@ preserve #include "pycore_long.h" // _PyLong_UnsignedShort_Converter() #include "pycore_modsupport.h" // _PyArg_CheckPositional() #include "pycore_runtime.h" // _Py_ID() -#include "pycore_tuple.h" // _PyTuple_FromArray() +#include "pycore_tuple.h" // _PyTuple_ITEMS() PyDoc_STRVAR(depr_star_new__doc__, "DeprStarNew(a=None)\n" @@ -2474,4 +2474,4 @@ depr_multi(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * exit: return return_value; } -/*[clinic end generated code: output=4e60af44fd6b7b94 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2231bec0ed196830 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_testclinic_kwds.c.h b/Modules/clinic/_testclinic_kwds.c.h index e2fd4d9f3b4..86cad50c56c 100644 --- a/Modules/clinic/_testclinic_kwds.c.h +++ b/Modules/clinic/_testclinic_kwds.c.h @@ -9,7 +9,7 @@ preserve #include "pycore_long.h" // _PyLong_UnsignedShort_Converter() #include "pycore_modsupport.h" // _PyArg_CheckPositional() #include "pycore_runtime.h" // _Py_ID() -#include "pycore_tuple.h" // _PyTuple_FromArray() +#include "pycore_tuple.h" // _PyTuple_ITEMS() PyDoc_STRVAR(lone_kwds__doc__, "lone_kwds($module, /, **kwds)\n" @@ -181,4 +181,4 @@ exit: return return_value; } -/*[clinic end generated code: output=e4dea1070e003f5d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3e5251b10aa44382 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/gcmodule.c.h b/Modules/clinic/gcmodule.c.h index 53ff9e4faf8..08275e35413 100644 --- a/Modules/clinic/gcmodule.c.h +++ b/Modules/clinic/gcmodule.c.h @@ -8,7 +8,6 @@ preserve #endif #include "pycore_abstract.h" // _Py_convert_optional_to_ssize_t() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() -#include "pycore_tuple.h" // _PyTuple_FromArray() PyDoc_STRVAR(gc_enable__doc__, "enable($module, /)\n" @@ -324,7 +323,7 @@ gc_get_referrers(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *return_value = NULL; PyObject *objs = NULL; - objs = _PyTuple_FromArray(args, nargs); + objs = PyTuple_FromArray(args, nargs); if (objs == NULL) { goto exit; } @@ -355,7 +354,7 @@ gc_get_referents(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *return_value = NULL; PyObject *objs = NULL; - objs = _PyTuple_FromArray(args, nargs); + objs = PyTuple_FromArray(args, nargs); if (objs == NULL) { goto exit; } @@ -584,4 +583,4 @@ gc_get_freeze_count(PyObject *module, PyObject *Py_UNUSED(ignored)) exit: return return_value; } -/*[clinic end generated code: output=96d057eac558e6ca input=a9049054013a1b77]*/ +/*[clinic end generated code: output=19738854607938db input=a9049054013a1b77]*/ diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 19b3f2a2eb6..8a8c7284283 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -8,7 +8,6 @@ #include "pycore_gc.h" #include "pycore_object.h" // _PyObject_IS_GC() #include "pycore_pystate.h" // _PyInterpreterState_GET() -#include "pycore_tuple.h" // _PyTuple_FromArray() typedef struct _gc_runtime_state GCState; diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 5d2506f48e3..60ef6f9ff4c 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -2131,7 +2131,7 @@ product_next_lock_held(PyObject *op) /* Copy the previous result tuple or re-use it if available */ if (Py_REFCNT(result) > 1) { PyObject *old_result = result; - result = _PyTuple_FromArray(_PyTuple_ITEMS(old_result), npools); + result = PyTuple_FromArray(_PyTuple_ITEMS(old_result), npools); if (result == NULL) goto empty; lz->result = result; @@ -2366,7 +2366,7 @@ combinations_next_lock_held(PyObject *op) /* Copy the previous result tuple or re-use it if available */ if (Py_REFCNT(result) > 1) { PyObject *old_result = result; - result = _PyTuple_FromArray(_PyTuple_ITEMS(old_result), r); + result = PyTuple_FromArray(_PyTuple_ITEMS(old_result), r); if (result == NULL) goto empty; co->result = result; @@ -2620,7 +2620,7 @@ cwr_next(PyObject *op) /* Copy the previous result tuple or re-use it if available */ if (Py_REFCNT(result) > 1) { PyObject *old_result = result; - result = _PyTuple_FromArray(_PyTuple_ITEMS(old_result), r); + result = PyTuple_FromArray(_PyTuple_ITEMS(old_result), r); if (result == NULL) goto empty; co->result = result; @@ -2881,7 +2881,7 @@ permutations_next(PyObject *op) /* Copy the previous result tuple or re-use it if available */ if (Py_REFCNT(result) > 1) { PyObject *old_result = result; - result = _PyTuple_FromArray(_PyTuple_ITEMS(old_result), r); + result = PyTuple_FromArray(_PyTuple_ITEMS(old_result), r); if (result == NULL) goto empty; po->result = result; diff --git a/Objects/call.c b/Objects/call.c index c9a18bcc3da..bd8617825b5 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -213,7 +213,7 @@ _PyObject_MakeTpCall(PyThreadState *tstate, PyObject *callable, return NULL; } - PyObject *argstuple = _PyTuple_FromArray(args, nargs); + PyObject *argstuple = PyTuple_FromArray(args, nargs); if (argstuple == NULL) { return NULL; } diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 06a81a4fdbd..5ac4fbd8129 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -313,7 +313,7 @@ method_vectorcall_VARARGS( if (method_check_args(func, args, nargs, kwnames)) { return NULL; } - PyObject *argstuple = _PyTuple_FromArray(args+1, nargs-1); + PyObject *argstuple = PyTuple_FromArray(args+1, nargs-1); if (argstuple == NULL) { return NULL; } @@ -338,7 +338,7 @@ method_vectorcall_VARARGS_KEYWORDS( if (method_check_args(func, args, nargs, NULL)) { return NULL; } - PyObject *argstuple = _PyTuple_FromArray(args+1, nargs-1); + PyObject *argstuple = PyTuple_FromArray(args+1, nargs-1); if (argstuple == NULL) { return NULL; } diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 531ee48eaf8..244d8f39e2b 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -13,7 +13,6 @@ #include "pycore_modsupport.h" // _PyArg_NoKeywords() #include "pycore_object.h" #include "pycore_pyerrors.h" // struct _PyErr_SetRaisedException -#include "pycore_tuple.h" // _PyTuple_FromArray() #include "osdefs.h" // SEP #include "clinic/exceptions.c.h" @@ -119,7 +118,7 @@ BaseException_vectorcall(PyObject *type_obj, PyObject * const*args, self->context = NULL; self->suppress_context = 0; - self->args = _PyTuple_FromArray(args, PyVectorcall_NARGS(nargsf)); + self->args = PyTuple_FromArray(args, PyVectorcall_NARGS(nargsf)); if (!self->args) { Py_DECREF(self); return NULL; diff --git a/Objects/listobject.c b/Objects/listobject.c index 5905a6d335b..b2903e5c93e 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -6,16 +6,16 @@ #include "pycore_critical_section.h" // _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED() #include "pycore_dict.h" // _PyDictViewObject #include "pycore_freelist.h" // _Py_FREELIST_FREE(), _Py_FREELIST_POP() -#include "pycore_pyatomic_ft_wrappers.h" #include "pycore_interp.h" // PyInterpreterState.list #include "pycore_list.h" // struct _Py_list_freelist, _PyListIterObject #include "pycore_long.h" // _PyLong_DigitCount #include "pycore_modsupport.h" // _PyArg_NoKwnames() #include "pycore_object.h" // _PyObject_GC_TRACK(), _PyDebugAllocatorStats() -#include "pycore_stackref.h" // _Py_TryIncrefCompareStackRef() -#include "pycore_tuple.h" // _PyTuple_FromArray() -#include "pycore_typeobject.h" // _Py_TYPE_VERSION_LIST +#include "pycore_pyatomic_ft_wrappers.h" #include "pycore_setobject.h" // _PySet_NextEntry() +#include "pycore_stackref.h" // _Py_TryIncrefCompareStackRef() +#include "pycore_tuple.h" // _PyTuple_FromArraySteal() +#include "pycore_typeobject.h" // _Py_TYPE_VERSION_LIST #include /*[clinic input] @@ -3221,7 +3221,7 @@ PyList_AsTuple(PyObject *v) PyObject *ret; PyListObject *self = (PyListObject *)v; Py_BEGIN_CRITICAL_SECTION(self); - ret = _PyTuple_FromArray(self->ob_item, Py_SIZE(v)); + ret = PyTuple_FromArray(self->ob_item, Py_SIZE(v)); Py_END_CRITICAL_SECTION(); return ret; } diff --git a/Objects/structseq.c b/Objects/structseq.c index c05bcde24c4..7a159338b9b 100644 --- a/Objects/structseq.c +++ b/Objects/structseq.c @@ -12,7 +12,7 @@ #include "pycore_modsupport.h" // _PyArg_NoPositional() #include "pycore_object.h" // _PyObject_GC_TRACK() #include "pycore_structseq.h" // PyStructSequence_InitType() -#include "pycore_tuple.h" // _PyTuple_FromArray() +#include "pycore_tuple.h" // _PyTuple_RESET_HASH_CACHE() #include "pycore_typeobject.h" // _PyStaticType_FiniBuiltin() static const char visible_length_key[] = "n_sequence_fields"; @@ -353,7 +353,7 @@ structseq_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) if (n_unnamed_fields < 0) { return NULL; } - tup = _PyTuple_FromArray(self->ob_item, n_visible_fields); + tup = PyTuple_FromArray(self->ob_item, n_visible_fields); if (!tup) goto error; diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 1fa4bae638a..94b7ae7e642 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -438,7 +438,7 @@ tuple_slice(PyTupleObject *a, Py_ssize_t ilow, if (ilow == 0 && ihigh == Py_SIZE(a) && PyTuple_CheckExact(a)) { return Py_NewRef(a); } - return _PyTuple_FromArray(a->ob_item + ilow, ihigh - ilow); + return PyTuple_FromArray(a->ob_item + ilow, ihigh - ilow); } PyObject * diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index a67bf9b1c53..d4549b70d4d 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -56,7 +56,6 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "pycore_pyhash.h" // _Py_HashSecret_t #include "pycore_pylifecycle.h" // _Py_SetFileSystemEncoding() #include "pycore_pystate.h" // _PyInterpreterState_GET() -#include "pycore_tuple.h" // _PyTuple_FromArray() #include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI #include "pycore_unicodeobject.h" // struct _Py_unicode_state #include "pycore_unicodeobject_generated.h" // _PyUnicode_InitStaticStrings() @@ -14494,7 +14493,7 @@ unicode_vectorcall(PyObject *type, PyObject *const *args, Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); if (kwnames != NULL && PyTuple_GET_SIZE(kwnames) != 0) { // Fallback to unicode_new() - PyObject *tuple = _PyTuple_FromArray(args, nargs); + PyObject *tuple = PyTuple_FromArray(args, nargs); if (tuple == NULL) { return NULL; } diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 2551c2c961b..64249177eec 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -3,6 +3,7 @@ #include "Python.h" #include "pycore_ast.h" // _PyAST_Validate() #include "pycore_call.h" // _PyObject_CallNoArgs() +#include "pycore_cell.h" // PyCell_GetRef() #include "pycore_ceval.h" // _PyEval_Vector() #include "pycore_compile.h" // _PyAST_Compile() #include "pycore_fileutils.h" // _PyFile_Flush @@ -14,8 +15,7 @@ #include "pycore_pyerrors.h" // _PyErr_NoMemory() #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_pythonrun.h" // _Py_SourceAsString() -#include "pycore_tuple.h" // _PyTuple_FromArray() -#include "pycore_cell.h" // PyCell_GetRef() +#include "pycore_tuple.h" // _PyTuple_Recycle() #include "clinic/bltinmodule.c.h" @@ -123,7 +123,7 @@ builtin___build_class__(PyObject *self, PyObject *const *args, Py_ssize_t nargs, "__build_class__: name is not a string"); return NULL; } - orig_bases = _PyTuple_FromArray(args + 2, nargs - 2); + orig_bases = PyTuple_FromArray(args + 2, nargs - 2); if (orig_bases == NULL) return NULL; diff --git a/Python/ceval.c b/Python/ceval.c index 1b52128c858..f48f412fab8 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2011,7 +2011,7 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, { PyThreadState *tstate = _PyThreadState_GET(); PyObject *res = NULL; - PyObject *defaults = _PyTuple_FromArray(defs, defcount); + PyObject *defaults = PyTuple_FromArray(defs, defcount); if (defaults == NULL) { return NULL; } diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index a47e4d11b54..4c4a86de2f9 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -7,7 +7,6 @@ preserve # include "pycore_runtime.h" // _Py_ID() #endif #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() -#include "pycore_tuple.h" // _PyTuple_FromArray() PyDoc_STRVAR(sys_addaudithook__doc__, "addaudithook($module, /, hook)\n" @@ -102,7 +101,7 @@ sys_audit(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyErr_SetString(PyExc_ValueError, "embedded null character"); goto exit; } - __clinic_args = _PyTuple_FromArray(args + 1, nargs - 1); + __clinic_args = PyTuple_FromArray(args + 1, nargs - 1); if (__clinic_args == NULL) { goto exit; } @@ -1948,4 +1947,4 @@ exit: #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=449d16326e69dcf6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5f7d84c5bf00d557 input=a9049054013a1b77]*/ diff --git a/Python/intrinsics.c b/Python/intrinsics.c index 8ea920e690c..9cfc285c6a5 100644 --- a/Python/intrinsics.c +++ b/Python/intrinsics.c @@ -9,7 +9,6 @@ #include "pycore_intrinsics.h" // INTRINSIC_PRINT #include "pycore_pyerrors.h" // _PyErr_SetString() #include "pycore_runtime.h" // _Py_ID() -#include "pycore_tuple.h" // _PyTuple_FromArray() #include "pycore_typevarobject.h" // _Py_make_typevar() #include "pycore_unicodeobject.h" // _PyUnicode_FromASCII() @@ -192,7 +191,7 @@ static PyObject * list_to_tuple(PyThreadState* unused, PyObject *v) { assert(PyList_Check(v)); - return _PyTuple_FromArray(((PyListObject *)v)->ob_item, Py_SIZE(v)); + return PyTuple_FromArray(((PyListObject *)v)->ob_item, Py_SIZE(v)); } static PyObject * diff --git a/Python/optimizer.c b/Python/optimizer.c index 7b76cddeabf..83b0b1a5deb 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -14,7 +14,7 @@ #include "pycore_opcode_utils.h" // MAX_REAL_OPCODE #include "pycore_optimizer.h" // _Py_uop_analyze_and_optimize() #include "pycore_pystate.h" // _PyInterpreterState_GET() -#include "pycore_tuple.h" // _PyTuple_FromArraySteal +#include "pycore_tuple.h" // _PyTuple_FromArraySteal #include "pycore_unicodeobject.h" // _PyUnicode_FromASCII #include "pycore_uop_ids.h" #include "pycore_jit.h" diff --git a/Python/optimizer_symbols.c b/Python/optimizer_symbols.c index 0e6884b9923..01cff0b014c 100644 --- a/Python/optimizer_symbols.c +++ b/Python/optimizer_symbols.c @@ -7,7 +7,6 @@ #include "pycore_long.h" #include "pycore_optimizer.h" #include "pycore_stats.h" -#include "pycore_tuple.h" // _PyTuple_FromArray() #include #include @@ -1044,7 +1043,7 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) "tuple item does not match value used to create tuple" ); PyObject *pair[2] = { val_42, val_43 }; - tuple = _PyTuple_FromArray(pair, 2); + tuple = PyTuple_FromArray(pair, 2); ref = _Py_uop_sym_new_const(ctx, tuple); TEST_PREDICATE( _Py_uop_sym_get_const(ctx, _Py_uop_sym_tuple_getitem(ctx, ref, 1)) == val_43, diff --git a/Tools/clinic/libclinic/converters.py b/Tools/clinic/libclinic/converters.py index 201125b9165..bc21ae84e1c 100644 --- a/Tools/clinic/libclinic/converters.py +++ b/Tools/clinic/libclinic/converters.py @@ -1266,13 +1266,12 @@ def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int, }}}} """ else: - self.add_include('pycore_tuple.h', '_PyTuple_FromArray()') start = f'args + {max_pos}' if max_pos else 'args' size = f'nargs - {max_pos}' if max_pos else 'nargs' if min(pos_only, min_pos) < max_pos: return f""" {paramname} = nargs > {max_pos} - ? _PyTuple_FromArray({start}, {size}) + ? PyTuple_FromArray({start}, {size}) : PyTuple_New(0); if ({paramname} == NULL) {{{{ goto exit; @@ -1280,7 +1279,7 @@ def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int, """ else: return f""" - {paramname} = _PyTuple_FromArray({start}, {size}); + {paramname} = PyTuple_FromArray({start}, {size}); if ({paramname} == NULL) {{{{ goto exit; }}}} From 35e9d41a9cc3999672ba7440847b16ec71bd9ddd Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 11 Oct 2025 22:58:43 +0200 Subject: [PATCH 117/373] gh-139482: Add `posix._clearenv()` function (#139965) --- Lib/os.py | 11 ++++++++ Lib/test/test_os/test_os.py | 8 ++++++ ...-10-11-20-03-13.gh-issue-139482.du2Stg.rst | 3 +++ Modules/clinic/posixmodule.c.h | 27 ++++++++++++++++++- Modules/posixmodule.c | 20 ++++++++++++++ configure | 6 +++++ configure.ac | 5 ++-- pyconfig.h.in | 3 +++ 8 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-11-20-03-13.gh-issue-139482.du2Stg.rst diff --git a/Lib/os.py b/Lib/os.py index 710d6f8cfcd..328d13c303b 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -58,6 +58,11 @@ def _get_exports_list(module): __all__.append('_exit') except ImportError: pass + try: + from posix import _clearenv + __all__.append('_clearenv') + except ImportError: + pass import posixpath as path try: @@ -768,6 +773,12 @@ def __ror__(self, other): new.update(self) return new + if _exists("_clearenv"): + def clear(self): + _clearenv() + self._data.clear() + + def _create_environ_mapping(): if name == 'nt': # Where Env Var Names Must Be UPPERCASE diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py index e074858fe2a..86880a6d281 100644 --- a/Lib/test/test_os/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -1494,6 +1494,14 @@ def test_reload_environ(self): self.assertNotIn(b'test_env', os.environb) self.assertNotIn('test_env', os.environ) + def test_clearenv(self): + os.environ['REMOVEME'] = '1' + os.environ.clear() + self.assertEqual(os.environ, {}) + + self.assertRaises(TypeError, os.environ.clear, None) + + class WalkTests(unittest.TestCase): """Tests for os.walk().""" is_fwalk = False diff --git a/Misc/NEWS.d/next/Library/2025-10-11-20-03-13.gh-issue-139482.du2Stg.rst b/Misc/NEWS.d/next/Library/2025-10-11-20-03-13.gh-issue-139482.du2Stg.rst new file mode 100644 index 00000000000..4edd3d238bf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-11-20-03-13.gh-issue-139482.du2Stg.rst @@ -0,0 +1,3 @@ +Optimize :data:`os.environ.clear() ` by calling +:manpage:`clearenv(3)` when this function is available. +Patch by Victor Stinner. diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 3d9863ad179..71f87ac8ec7 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -9539,6 +9539,27 @@ exit: #endif /* !defined(MS_WINDOWS) */ +#if defined(HAVE_CLEARENV) + +PyDoc_STRVAR(os__clearenv__doc__, +"_clearenv($module, /)\n" +"--\n" +"\n"); + +#define OS__CLEARENV_METHODDEF \ + {"_clearenv", (PyCFunction)os__clearenv, METH_NOARGS, os__clearenv__doc__}, + +static PyObject * +os__clearenv_impl(PyObject *module); + +static PyObject * +os__clearenv(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return os__clearenv_impl(module); +} + +#endif /* defined(HAVE_CLEARENV) */ + PyDoc_STRVAR(os_strerror__doc__, "strerror($module, code, /)\n" "--\n" @@ -13292,6 +13313,10 @@ exit: #define OS_UNSETENV_METHODDEF #endif /* !defined(OS_UNSETENV_METHODDEF) */ +#ifndef OS__CLEARENV_METHODDEF + #define OS__CLEARENV_METHODDEF +#endif /* !defined(OS__CLEARENV_METHODDEF) */ + #ifndef OS_WCOREDUMP_METHODDEF #define OS_WCOREDUMP_METHODDEF #endif /* !defined(OS_WCOREDUMP_METHODDEF) */ @@ -13447,4 +13472,4 @@ exit: #ifndef OS__EMSCRIPTEN_LOG_METHODDEF #define OS__EMSCRIPTEN_LOG_METHODDEF #endif /* !defined(OS__EMSCRIPTEN_LOG_METHODDEF) */ -/*[clinic end generated code: output=47ace1528820858b input=a9049054013a1b77]*/ +/*[clinic end generated code: output=67f0df7cd5a7de20 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 8278902cbeb..38ddc3ec4ff 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -13201,6 +13201,25 @@ os_unsetenv_impl(PyObject *module, PyObject *name) #endif /* !MS_WINDOWS */ +#ifdef HAVE_CLEARENV +/*[clinic input] +os._clearenv +[clinic start generated code]*/ + +static PyObject * +os__clearenv_impl(PyObject *module) +/*[clinic end generated code: output=2d6705d62c014b51 input=47d2fa7f323c43ca]*/ +{ + errno = 0; + int err = clearenv(); + if (err) { + return posix_error(); + } + Py_RETURN_NONE; +} +#endif + + /*[clinic input] os.strerror @@ -17167,6 +17186,7 @@ static PyMethodDef posix_methods[] = { OS_POSIX_FADVISE_METHODDEF OS_PUTENV_METHODDEF OS_UNSETENV_METHODDEF + OS__CLEARENV_METHODDEF OS_STRERROR_METHODDEF OS_FCHDIR_METHODDEF OS_FSYNC_METHODDEF diff --git a/configure b/configure index d80340e3015..211f8439906 100755 --- a/configure +++ b/configure @@ -19225,6 +19225,12 @@ if test "x$ac_cv_func_chown" = xyes then : printf "%s\n" "#define HAVE_CHOWN 1" >>confdefs.h +fi +ac_fn_c_check_func "$LINENO" "clearenv" "ac_cv_func_clearenv" +if test "x$ac_cv_func_clearenv" = xyes +then : + printf "%s\n" "#define HAVE_CLEARENV 1" >>confdefs.h + fi ac_fn_c_check_func "$LINENO" "clock" "ac_cv_func_clock" if test "x$ac_cv_func_clock" = xyes diff --git a/configure.ac b/configure.ac index 1e0c0f71b7c..35bf153a898 100644 --- a/configure.ac +++ b/configure.ac @@ -5226,7 +5226,8 @@ fi # checks for library functions AC_CHECK_FUNCS([ \ - accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ + accept4 alarm bind_textdomain_codeset chmod chown clearenv \ + clock closefrom close_range confstr \ copy_file_range ctermid dladdr dup dup3 execv explicit_bzero explicit_memset \ faccessat fchmod fchmodat fchown fchownat fdopendir fdwalk fexecve \ fork fork1 fpathconf fstatat ftime ftruncate futimens futimes futimesat \ @@ -8173,7 +8174,7 @@ PY_STDLIB_MOD([xxlimited_35], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_d # Determine JIT stencils header files based on target platform JIT_STENCILS_H="" -AS_VAR_IF([enable_experimental_jit], [no], +AS_VAR_IF([enable_experimental_jit], [no], [], [case "$host" in aarch64-apple-darwin*) diff --git a/pyconfig.h.in b/pyconfig.h.in index 60bff4a9f26..72870411bc0 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -141,6 +141,9 @@ /* Define if you have the 'chroot' function. */ #undef HAVE_CHROOT +/* Define to 1 if you have the 'clearenv' function. */ +#undef HAVE_CLEARENV + /* Define to 1 if you have the 'clock' function. */ #undef HAVE_CLOCK From d6dd64ac654898b4ce713c16c49eadb0c6e9d7dc Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 12 Oct 2025 01:52:01 +0300 Subject: [PATCH 118/373] gh-138044: Fix `importlib.resources.files` deprecation docs (#139632) --- Doc/library/importlib.resources.rst | 8 ++++++-- Doc/whatsnew/3.15.rst | 8 ++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Doc/library/importlib.resources.rst b/Doc/library/importlib.resources.rst index 8cb43f0625f..20297f9fe30 100644 --- a/Doc/library/importlib.resources.rst +++ b/Doc/library/importlib.resources.rst @@ -72,8 +72,12 @@ for example, a package and its resources can be imported from a zip file using .. versionadded:: 3.9 - .. deprecated-removed:: 3.12 3.15 - *package* parameter was renamed to *anchor*. *anchor* can now be a + .. versionchanged:: 3.12 + *package* parameter was renamed to *anchor*. + *package* was still accepted, but deprecated. + + .. versionchanged:: 3.15 + *package* parameter was fully removed. *anchor* can now be a non-package module and if omitted will default to the caller's module. *package* is no longer accepted since Python 3.15. Consider passing the anchor positionally or using ``importlib_resources >= 5.10`` for a diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 40286d4fe85..a6be2716296 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -696,6 +696,14 @@ http.server (Contributed by Bénédikt Tran in :gh:`133810`.) +importlib.resources +------------------- + +* Removed deprecated ``package`` parameter + from :func:`importlib.resources.files` function. + (Contributed by Semyon Moroz in :gh:`138044`) + + pathlib ------- From a18843dbfba32387be4236379260f7c15a002e0d Mon Sep 17 00:00:00 2001 From: Alper Date: Sun, 12 Oct 2025 00:42:10 -0700 Subject: [PATCH 119/373] gh-116738: test `dbm.gnu` module on FT Python build (#138467) --- Lib/test/test_free_threading/test_dbm_gnu.py | 79 ++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 Lib/test/test_free_threading/test_dbm_gnu.py diff --git a/Lib/test/test_free_threading/test_dbm_gnu.py b/Lib/test/test_free_threading/test_dbm_gnu.py new file mode 100644 index 00000000000..d2d7b78be71 --- /dev/null +++ b/Lib/test/test_free_threading/test_dbm_gnu.py @@ -0,0 +1,79 @@ +import unittest + +from test.support import import_helper, os_helper, threading_helper +from test.support.threading_helper import run_concurrently + +import threading + +gdbm = import_helper.import_module("dbm.gnu") + +NTHREADS = 10 +KEY_PER_THREAD = 1000 + +gdbm_filename = "test_gdbm_file" + + +@threading_helper.requires_working_threading() +class TestGdbm(unittest.TestCase): + def test_racing_dbm_gnu(self): + def gdbm_multi_op_worker(db): + # Each thread sets, gets, and iterates + tid = threading.get_ident() + + # Insert keys + for i in range(KEY_PER_THREAD): + db[f"key_{tid}_{i}"] = f"value_{tid}_{i}" + + for i in range(KEY_PER_THREAD): + # Keys and values are stored as bytes; encode values for + # comparison + key = f"key_{tid}_{i}" + value = f"value_{tid}_{i}".encode() + self.assertIn(key, db) + self.assertEqual(db[key], value) + self.assertEqual(db.get(key), value) + self.assertIsNone(db.get("not_exist")) + with self.assertRaises(KeyError): + db["not_exist"] + + # Iterate over the database keys and verify only those belonging + # to this thread. Other threads may concurrently delete their keys. + key_prefix = f"key_{tid}".encode() + key = db.firstkey() + key_count = 0 + while key: + if key.startswith(key_prefix): + self.assertIn(key, db) + key_count += 1 + key = db.nextkey(key) + + # Can't assert key_count == KEY_PER_THREAD because concurrent + # threads may insert or delete keys during iteration. This can + # cause keys to be skipped or counted multiple times, making the + # count unreliable. + # See: https://www.gnu.org.ua/software/gdbm/manual/Sequential.html + # self.assertEqual(key_count, KEY_PER_THREAD) + + # Delete this thread's keys + for i in range(KEY_PER_THREAD): + key = f"key_{tid}_{i}" + del db[key] + self.assertNotIn(key, db) + with self.assertRaises(KeyError): + del db["not_exist"] + + # Re-insert keys + for i in range(KEY_PER_THREAD): + db[f"key_{tid}_{i}"] = f"value_{tid}_{i}" + + with os_helper.temp_dir() as tmpdirname: + db = gdbm.open(f"{tmpdirname}/{gdbm_filename}", "c") + run_concurrently( + worker_func=gdbm_multi_op_worker, nthreads=NTHREADS, args=(db,) + ) + self.assertEqual(len(db), NTHREADS * KEY_PER_THREAD) + db.close() + + +if __name__ == "__main__": + unittest.main() From 6710156bd27dd48493d15f515506a0ead5d0328f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 12 Oct 2025 12:51:44 +0200 Subject: [PATCH 120/373] gh-139988: fix a leak when failing to create a Union type (#139990) --- .../2025-10-12-11-00-06.gh-issue-139988.4wi51t.rst | 2 ++ Objects/unionobject.c | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-12-11-00-06.gh-issue-139988.4wi51t.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-12-11-00-06.gh-issue-139988.4wi51t.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-12-11-00-06.gh-issue-139988.4wi51t.rst new file mode 100644 index 00000000000..60fa3b1d339 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-12-11-00-06.gh-issue-139988.4wi51t.rst @@ -0,0 +1,2 @@ +Fix a memory leak when failing to create a :class:`~typing.Union` type. +Patch by Bénédikt Tran. diff --git a/Objects/unionobject.c b/Objects/unionobject.c index 2206ed80ef0..c4ece0fe09f 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -474,11 +474,13 @@ _Py_union_from_tuple(PyObject *args) } if (PyTuple_CheckExact(args)) { if (!unionbuilder_add_tuple(&ub, args)) { + unionbuilder_finalize(&ub); return NULL; } } else { if (!unionbuilder_add_single(&ub, args)) { + unionbuilder_finalize(&ub); return NULL; } } From 68622be935b36f0ca75729157992972dbf426603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20S=C5=82awecki?= Date: Sun, 12 Oct 2025 18:48:22 +0200 Subject: [PATCH 121/373] gh-139155: Remove "dictionaries are sorted by key" note in `pprint` docs (GH-139159) --- Doc/library/pprint.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/Doc/library/pprint.rst b/Doc/library/pprint.rst index 2985f31bacb..f5189245079 100644 --- a/Doc/library/pprint.rst +++ b/Doc/library/pprint.rst @@ -22,8 +22,6 @@ The formatted representation keeps objects on a single line if it can, and breaks them onto multiple lines if they don't fit within the allowed width, adjustable by the *width* parameter defaulting to 80 characters. -Dictionaries are sorted by key before the display is computed. - .. versionchanged:: 3.9 Added support for pretty-printing :class:`types.SimpleNamespace`. From 0fc5c56815b963e4b45469b2b6a5610ea2f2d181 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Sun, 12 Oct 2025 09:51:13 -0700 Subject: [PATCH 122/373] GH-139979: Add @force_not_colorized_test_class to TestOptionalHelpVersionActions (#139980) --- Lib/test/test_argparse.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 6a07f190285..e5d642cde66 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -5831,6 +5831,7 @@ def test_subparser_conflict(self): # Help and Version option tests # ============================= +@force_not_colorized_test_class class TestOptionalsHelpVersionActions(TestCase): """Test the help and version actions""" From 5f91d5d9a4187c4bfa2ddd653b819b12eb3ad477 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 12 Oct 2025 19:32:10 +0200 Subject: [PATCH 123/373] gh-112075: Remove _PyObject_SetManagedDict() function (#139737) Move it to the internal C API and no longer export it. --- Include/cpython/object.h | 1 - Include/internal/pycore_object.h | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Include/cpython/object.h b/Include/cpython/object.h index e2f87524c21..d64298232e7 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -442,7 +442,6 @@ PyAPI_FUNC(int) _Py_ReachedRecursionLimitWithMargin(PyThreadState *tstate, int m PyAPI_FUNC(void *) PyObject_GetItemData(PyObject *obj); PyAPI_FUNC(int) PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg); -PyAPI_FUNC(int) _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict); PyAPI_FUNC(void) PyObject_ClearManagedDict(PyObject *obj); diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 77560e5da66..980d6d7764b 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -1031,7 +1031,8 @@ enum _PyAnnotateFormat { _Py_ANNOTATE_FORMAT_STRING = 4, }; -int _PyObject_SetDict(PyObject *obj, PyObject *value); +extern int _PyObject_SetDict(PyObject *obj, PyObject *value); +extern int _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict); #ifndef Py_GIL_DISABLED static inline Py_ALWAYS_INLINE void _Py_INCREF_MORTAL(PyObject *op) From bb85af343f331b104e3bca685e8f96e60bba8bc0 Mon Sep 17 00:00:00 2001 From: Arseniy Krupchik <96835169+Grivvus@users.noreply.github.com> Date: Mon, 13 Oct 2025 02:17:41 +0400 Subject: [PATCH 124/373] gh-136438: Make sure test.test_pydoc.test_pydoc pass with all optimization levels (#136479) test_pydoc.test_pydoc now passes with -OO --- Lib/test/test_pydoc/test_pydoc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py index 5aa8d92057e..c640416327e 100644 --- a/Lib/test/test_pydoc/test_pydoc.py +++ b/Lib/test/test_pydoc/test_pydoc.py @@ -1413,7 +1413,7 @@ def test_special_form(self): self.assertIn('NoReturn = typing.NoReturn', doc) self.assertIn(typing.NoReturn.__doc__.strip().splitlines()[0], doc) else: - self.assertIn('NoReturn = class _SpecialForm(_Final)', doc) + self.assertIn('NoReturn = class _SpecialForm(_Final, _NotIterable)', doc) def test_typing_pydoc(self): def foo(data: typing.List[typing.Any], From 0344db8d60621705af88daa7ff65bde063271f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20S=C5=82awecki?= Date: Mon, 13 Oct 2025 10:40:39 +0200 Subject: [PATCH 125/373] gh-131189: Remove `curses` mention from `PYTHON_BASIC_REPL` docs (#140022) The `curses` dependency for the default REPL has been removed in 09dfb50f1b7c23bc48d86bd579671761bb8ca48b. --- Doc/using/cmdline.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index 74c18c2a6ed..773fdabe99e 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -1277,9 +1277,8 @@ conflict. .. envvar:: PYTHON_BASIC_REPL If this variable is set to any value, the interpreter will not attempt to - load the Python-based :term:`REPL` that requires :mod:`curses` and - :mod:`readline`, and will instead use the traditional parser-based - :term:`REPL`. + load the Python-based :term:`REPL` that requires :mod:`readline`, and will + instead use the traditional parser-based :term:`REPL`. .. versionadded:: 3.13 From e6aa5152964647e548929a10d2d0fe2a7c5d875a Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 13 Oct 2025 12:28:25 +0200 Subject: [PATCH 126/373] gh-140009: Improve performance of `list_extend_dictitems` by using `PyTuple_FromArray` (#140010) --- .../2025-10-12-18-54-06.gh-issue-140009.-MbFh_.rst | 1 + Objects/listobject.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-12-18-54-06.gh-issue-140009.-MbFh_.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-12-18-54-06.gh-issue-140009.-MbFh_.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-12-18-54-06.gh-issue-140009.-MbFh_.rst new file mode 100644 index 00000000000..97b35c88e17 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-12-18-54-06.gh-issue-140009.-MbFh_.rst @@ -0,0 +1 @@ +Improve performance of list extension by dictionary items. diff --git a/Objects/listobject.c b/Objects/listobject.c index b2903e5c93e..1722ea60cdc 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -1382,9 +1382,9 @@ list_extend_dictitems(PyListObject *self, PyDictObject *dict) PyObject **dest = self->ob_item + m; Py_ssize_t pos = 0; Py_ssize_t i = 0; - PyObject *key, *value; - while (_PyDict_Next((PyObject *)dict, &pos, &key, &value, NULL)) { - PyObject *item = PyTuple_Pack(2, key, value); + PyObject *key_value[2]; + while (_PyDict_Next((PyObject *)dict, &pos, &key_value[0], &key_value[1], NULL)) { + PyObject *item = PyTuple_FromArray(key_value, 2); if (item == NULL) { Py_SET_SIZE(self, m + i); return -1; From a00655b8775e00fb27644ac37c4e109d42dce5cd Mon Sep 17 00:00:00 2001 From: mdehoon Date: Mon, 13 Oct 2025 19:29:42 +0900 Subject: [PATCH 127/373] gh-140001: Remove obsolete TCL_WIN_SOCKET macro (from Tcl 7.x) from _tkinter.c (GH-139998) Co-authored-by: Michiel Jan Laurens de Hoon --- Modules/_tkinter.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index f0882191d3c..c0ed8977d8f 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -82,33 +82,10 @@ typedef int Tcl_Size; #ifdef HAVE_CREATEFILEHANDLER -/* This bit is to ensure that TCL_UNIX_FD is defined and doesn't interfere - with the proper calculation of FHANDLETYPE == TCL_UNIX_FD below. */ -#ifndef TCL_UNIX_FD -# ifdef TCL_WIN_SOCKET -# define TCL_UNIX_FD (! TCL_WIN_SOCKET) -# else -# define TCL_UNIX_FD 1 -# endif -#endif - -/* Tcl_CreateFileHandler() changed several times; these macros deal with the - messiness. In Tcl 8.0 and later, it is not available on Windows (and on - Unix, only because Jack added it back); when available on Windows, it only - applies to sockets. */ - -#ifdef MS_WINDOWS -#define FHANDLETYPE TCL_WIN_SOCKET -#else -#define FHANDLETYPE TCL_UNIX_FD -#endif - /* If Tcl can wait for a Unix file descriptor, define the EventHook() routine which uses this to handle Tcl events while the user is typing commands. */ -#if FHANDLETYPE == TCL_UNIX_FD #define WAIT_FOR_STDIN -#endif #endif /* HAVE_CREATEFILEHANDLER */ From 52996aaa78706e0f2a70953feaf97b474eb438e7 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Mon, 13 Oct 2025 14:23:09 +0300 Subject: [PATCH 128/373] gh-107073: fix relevant typo in `PyObject_ClearManagedDict` (#140032) --- Doc/c-api/object.rst | 2 +- Doc/c-api/typeobj.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 78599e704b1..56857929a5a 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -600,7 +600,7 @@ Object Protocol Clear the managed dictionary of *obj*. - This function must only be called in a traverse function of the type which + This function must only be called in a clear function of the type which has the :c:macro:`Py_TPFLAGS_MANAGED_DICT` flag set. .. versionadded:: 3.13 diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 8bd3144f88a..5b7cf0c4502 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -1704,7 +1704,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) :c:func:`Py_CLEAR` macro performs the operations in a safe order. If the :c:macro:`Py_TPFLAGS_MANAGED_DICT` bit is set in the - :c:member:`~PyTypeObject.tp_flags` field, the traverse function must call + :c:member:`~PyTypeObject.tp_flags` field, the clear function must call :c:func:`PyObject_ClearManagedDict` like this:: PyObject_ClearManagedDict((PyObject*)self); From 1a82568568a302d372690b07c7d9c45719e89bd4 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Mon, 13 Oct 2025 15:01:06 +0100 Subject: [PATCH 129/373] gh-139823: Check if `zlib` is available in `ensurepip` (GH-139954) --- Lib/ensurepip/__init__.py | 9 +++++++++ Lib/test/test_ensurepip.py | 15 +++++++++++++++ ...2025-10-11-14-37-42.gh-issue-139823.uGF4oh.rst | 2 ++ 3 files changed, 26 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-10-11-14-37-42.gh-issue-139823.uGF4oh.rst diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index 4bd85990e86..0552cf55db1 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -130,6 +130,15 @@ def _bootstrap(*, root=None, upgrade=False, user=False, Note that calling this function will alter both sys.path and os.environ. """ + + try: + import zlib + except ImportError: + raise ModuleNotFoundError( + "ensurepip requires the standard library module 'zlib' " + "to install pip." + ) from None + if altinstall and default_pip: raise ValueError("Cannot use altinstall and default_pip together") diff --git a/Lib/test/test_ensurepip.py b/Lib/test/test_ensurepip.py index 6d3c91b0b6d..f6743d57ca2 100644 --- a/Lib/test/test_ensurepip.py +++ b/Lib/test/test_ensurepip.py @@ -60,6 +60,11 @@ def setUp(self): self.run_pip.return_value = 0 self.addCleanup(run_pip_patch.stop) + # Allow testing on zlib-less platforms by avoiding the check for zlib in _bootstrap() + zlib_patch = unittest.mock.patch.dict('sys.modules', {'zlib': unittest.mock.MagicMock()}) + zlib_patch.start() + self.addCleanup(zlib_patch.stop) + # Avoid side effects on the actual os module real_devnull = os.devnull os_patch = unittest.mock.patch("ensurepip.os") @@ -185,6 +190,16 @@ def test_pip_config_file_disabled(self): ensurepip.bootstrap() self.assertEqual(self.os_environ["PIP_CONFIG_FILE"], os.devnull) + def test_missing_zlib(self): + with unittest.mock.patch.dict('sys.modules', {'zlib': None}): + with self.assertRaises(ModuleNotFoundError) as cm: + ensurepip.bootstrap() + + error_msg = str(cm.exception) + self.assertIn("ensurepip requires the standard library module 'zlib'", error_msg) + + self.assertFalse(self.run_pip.called) + @contextlib.contextmanager def fake_pip(version=ensurepip.version()): if version is None: diff --git a/Misc/NEWS.d/next/Library/2025-10-11-14-37-42.gh-issue-139823.uGF4oh.rst b/Misc/NEWS.d/next/Library/2025-10-11-14-37-42.gh-issue-139823.uGF4oh.rst new file mode 100644 index 00000000000..43fe05d5699 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-11-14-37-42.gh-issue-139823.uGF4oh.rst @@ -0,0 +1,2 @@ +:mod:`ensurepip` now fails with a nicer error message when the :mod:`zlib` +module is not available. From 88fc0a0fdcbda334eda8b44af2080e87274879f5 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 13 Oct 2025 17:18:45 +0300 Subject: [PATCH 130/373] Using Python on Windows: Free-threaded is supported from 3.14 (#139992) --- Doc/using/windows.rst | 61 ++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 38 deletions(-) diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index 2cfdeac10f6..76624862633 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -60,7 +60,7 @@ packages. .. _windows-path-mod: .. _launcher: -Python Install Manager +Python install manager ====================== Installation @@ -103,7 +103,7 @@ Windows Server 2019, please see :ref:`pymanager-advancedinstall` below for more information. -Basic Use +Basic use --------- The recommended command for launching Python is ``python``, which will either @@ -193,7 +193,7 @@ installed if automatic installation is configured (most likely by setting ``pymanager exec`` forms of the command were used. -Command Help +Command help ------------ The ``py help`` command will display the full list of supported commands, along @@ -218,7 +218,7 @@ override multiple settings at once. See :ref:`pymanager-config` below for more information about these files. -Listing Runtimes +Listing runtimes ---------------- .. code:: @@ -259,7 +259,7 @@ For compatibility with the old launcher, the ``--list``, ``--list-paths``, additional options, and will produce legacy formatted output. -Installing Runtimes +Installing runtimes ------------------- .. code:: @@ -298,7 +298,7 @@ useful for embedding runtimes into larger applications. .. _pymanager-offline: -Offline Installs +Offline installs ---------------- To perform offline installs of Python, you will need to first create an offline @@ -330,7 +330,7 @@ In this way, Python runtimes can be installed and managed on a machine without access to the internet. -Uninstalling Runtimes +Uninstalling runtimes --------------------- .. code:: @@ -541,7 +541,7 @@ configuration option. .. _pymanager-advancedinstall: -Advanced Installation +Advanced installation --------------------- For situations where an MSIX cannot be installed, such as some older @@ -635,7 +635,7 @@ the Store package in this way. .. _pymanager-admin-config: -Administrative Configuration +Administrative configuration ---------------------------- There are a number of options that may be useful for administrators to override @@ -726,17 +726,12 @@ directory containing the configuration file that specified them. .. _install-freethreaded-windows: -Installing Free-threaded Binaries +Installing free-threaded binaries --------------------------------- -.. versionadded:: 3.13 (Experimental) +.. versionadded:: 3.13 -.. note:: - - Everything described in this section is considered experimental, - and should be expected to change in future releases. - -Pre-built distributions of the experimental free-threaded build are available +Pre-built distributions of the free-threaded build are available by installing tags with the ``t`` suffix. .. code:: @@ -885,7 +880,7 @@ versions before providing updates to users. The two recommended use cases for this distribution are described below. -Python Application +Python application ------------------ An application written in Python does not necessarily require users to be aware @@ -989,12 +984,7 @@ for the 64-bit version, `www.nuget.org/packages/pythonx86 Free-threaded packages ---------------------- -.. versionadded:: 3.13 (Experimental) - -.. note:: - - Everything described in this section is considered experimental, - and should be expected to change in future releases. +.. versionadded:: 3.13 Packages containing free-threaded binaries are named `python-freethreaded `_ @@ -1046,7 +1036,7 @@ please install Python 3.12. .. _max-path: -Removing the MAX_PATH Limitation +Removing the MAX_PATH limitation ================================ Windows historically has limited path lengths to 260 characters. This meant that @@ -1332,7 +1322,7 @@ installation". In this case: * Shortcuts are available for all users -Removing the MAX_PATH Limitation +Removing the MAX_PATH limitation -------------------------------- Windows historically has limited path lengths to 260 characters. This meant that @@ -1355,7 +1345,7 @@ After changing the above option, no further configuration is required. .. _install-quiet-option: -Installing Without UI +Installing without UI --------------------- All of the options available in the installer UI can also be specified from the @@ -1504,7 +1494,7 @@ example file sets the same options as the previous example: .. _install-layout-option: -Installing Without Downloading +Installing without downloading ------------------------------ As some features of Python are not included in the initial installer download, @@ -1545,15 +1535,10 @@ settings and replace any that have been removed or modified. :ref:`launcher`, which has its own entry in Programs and Features. -Installing Free-threaded Binaries +Installing free-threaded binaries --------------------------------- -.. versionadded:: 3.13 (Experimental) - -.. note:: - - Everything described in this section is considered experimental, - and should be expected to change in future releases. +.. versionadded:: 3.13 To install pre-built binaries with free-threading enabled (see :pep:`703`), you should select "Customize installation". The second page of options includes the @@ -1585,7 +1570,7 @@ builds. Free-threaded binaries are also available :ref:`on nuget.org `. -Python Launcher for Windows (Deprecated) +Python launcher for Windows (deprecated) ======================================== .. deprecated:: 3.14 @@ -1737,7 +1722,7 @@ have the script specify the version which should be used. The key benefit of this is that a single launcher can support multiple Python versions at the same time depending on the contents of the first line. -Shebang Lines +Shebang lines ------------- If the first line of a script file starts with ``#!``, it is known as a @@ -1949,7 +1934,7 @@ should allow you to see what versions of Python were located, why a particular version was chosen and the exact command-line used to execute the target Python. It is primarily intended for testing and debugging. -Dry Run +Dry run ------- If an environment variable :envvar:`!PYLAUNCHER_DRYRUN` is set (to any value), From 525dcfe5236ee21b234cad16d2a3d5769e77e0ec Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 13 Oct 2025 18:32:16 +0300 Subject: [PATCH 131/373] gh-138772: Fix and improve documentation for turtle color functions (GH-139325) Use multiple signatures for clarity. Explain different forms of bgcolor() in details. Fix outdated docstrings. --- Doc/library/turtle.rst | 52 +++++++++++++++---- Lib/turtle.py | 115 ++++++++++++++++++++++++----------------- 2 files changed, 110 insertions(+), 57 deletions(-) diff --git a/Doc/library/turtle.rst b/Doc/library/turtle.rst index fea6b57edf0..b687231bd48 100644 --- a/Doc/library/turtle.rst +++ b/Doc/library/turtle.rst @@ -777,13 +777,17 @@ Turtle motion 180.0 -.. function:: dot(size=None, *color) +.. function:: dot() + dot(size) + dot(color, /) + dot(size, color, /) + dot(size, r, g, b, /) :param size: an integer >= 1 (if given) :param color: a colorstring or a numeric color tuple Draw a circular dot with diameter *size*, using *color*. If *size* is - not given, the maximum of pensize+4 and 2*pensize is used. + not given, the maximum of ``pensize+4`` and ``2*pensize`` is used. .. doctest:: @@ -1152,7 +1156,9 @@ Drawing state Color control ~~~~~~~~~~~~~ -.. function:: pencolor(*args) +.. function:: pencolor() + pencolor(color, /) + pencolor(r, g, b, /) Return or set the pencolor. @@ -1161,7 +1167,7 @@ Color control ``pencolor()`` Return the current pencolor as color specification string or as a tuple (see example). May be used as input to another - color/pencolor/fillcolor call. + color/pencolor/fillcolor/bgcolor call. ``pencolor(colorstring)`` Set pencolor to *colorstring*, which is a Tk color specification string, @@ -1201,7 +1207,9 @@ Color control (50.0, 193.0, 143.0) -.. function:: fillcolor(*args) +.. function:: fillcolor() + fillcolor(color, /) + fillcolor(r, g, b, /) Return or set the fillcolor. @@ -1210,7 +1218,7 @@ Color control ``fillcolor()`` Return the current fillcolor as color specification string, possibly in tuple format (see example). May be used as input to another - color/pencolor/fillcolor call. + color/pencolor/fillcolor/bgcolor call. ``fillcolor(colorstring)`` Set fillcolor to *colorstring*, which is a Tk color specification string, @@ -1244,7 +1252,10 @@ Color control (255.0, 255.0, 255.0) -.. function:: color(*args) +.. function:: color() + color(color, /) + color(r, g, b, /) + color(pencolor, fillcolor, /) Return or set pencolor and fillcolor. @@ -1870,13 +1881,32 @@ Most of the examples in this section refer to a TurtleScreen instance called Window control -------------- -.. function:: bgcolor(*args) +.. function:: bgcolor() + bgcolor(color, /) + bgcolor(r, g, b, /) - :param args: a color string or three numbers in the range 0..colormode or a - 3-tuple of such numbers + Return or set the background color of the TurtleScreen. + Four input formats are allowed: - Set or return background color of the TurtleScreen. + ``bgcolor()`` + Return the current background color as color specification string or + as a tuple (see example). May be used as input to another + color/pencolor/fillcolor/bgcolor call. + + ``bgcolor(colorstring)`` + Set the background color to *colorstring*, which is a Tk color + specification string, such as ``"red"``, ``"yellow"``, or ``"#33cc8c"``. + + ``bgcolor((r, g, b))`` + Set the background color to the RGB color represented by the tuple of + *r*, *g*, and *b*. + Each of *r*, *g*, and *b* must be in the range 0..colormode, where + colormode is either 1.0 or 255 (see :func:`colormode`). + + ``bgcolor(r, g, b)`` + Set the background color to the RGB color represented by *r*, *g*, and *b*. Each of + *r*, *g*, and *b* must be in the range 0..colormode. .. doctest:: :skipif: _tkinter is None diff --git a/Lib/turtle.py b/Lib/turtle.py index e88981d298a..e5ce2c0a03c 100644 --- a/Lib/turtle.py +++ b/Lib/turtle.py @@ -1214,16 +1214,32 @@ def turtles(self): def bgcolor(self, *args): """Set or return backgroundcolor of the TurtleScreen. - Arguments (if given): a color string or three numbers - in the range 0..colormode or a 3-tuple of such numbers. + Four input formats are allowed: + - bgcolor() + Return the current background color as color specification + string or as a tuple (see example). May be used as input + to another color/pencolor/fillcolor/bgcolor call. + - bgcolor(colorstring) + Set the background color to colorstring, which is a Tk color + specification string, such as "red", "yellow", or "#33cc8c". + - bgcolor((r, g, b)) + Set the background color to the RGB color represented by + the tuple of r, g, and b. Each of r, g, and b must be in + the range 0..colormode, where colormode is either 1.0 or 255 + (see colormode()). + - bgcolor(r, g, b) + Set the background color to the RGB color represented by + r, g, and b. Each of r, g, and b must be in the range + 0..colormode. Example (for a TurtleScreen instance named screen): >>> screen.bgcolor("orange") >>> screen.bgcolor() 'orange' - >>> screen.bgcolor(0.5,0,0.5) + >>> colormode(255) + >>> screen.bgcolor('#800080') >>> screen.bgcolor() - '#800080' + (128.0, 0.0, 128.0) """ if args: color = self._colorstr(args) @@ -1678,7 +1694,7 @@ def forward(self, distance): Example (for a Turtle instance named turtle): >>> turtle.position() - (0.00, 0.00) + (0.00,0.00) >>> turtle.forward(25) >>> turtle.position() (25.00,0.00) @@ -1701,10 +1717,10 @@ def back(self, distance): Example (for a Turtle instance named turtle): >>> turtle.position() - (0.00, 0.00) + (0.00,0.00) >>> turtle.backward(30) >>> turtle.position() - (-30.00, 0.00) + (-30.00,0.00) """ self._go(-distance) @@ -1811,7 +1827,7 @@ def goto(self, x, y=None): Example (for a Turtle instance named turtle): >>> tp = turtle.pos() >>> tp - (0.00, 0.00) + (0.00,0.00) >>> turtle.setpos(60,30) >>> turtle.pos() (60.00,30.00) @@ -1891,7 +1907,7 @@ def distance(self, x, y=None): Example (for a Turtle instance named turtle): >>> turtle.pos() - (0.00, 0.00) + (0.00,0.00) >>> turtle.distance(30,40) 50.0 >>> pen = Turtle() @@ -2230,19 +2246,17 @@ def color(self, *args): Arguments: Several input formats are allowed. - They use 0, 1, 2, or 3 arguments as follows: - - color() - Return the current pencolor and the current fillcolor - as a pair of color specification strings as are returned - by pencolor and fillcolor. - color(colorstring), color((r,g,b)), color(r,g,b) - inputs as in pencolor, set both, fillcolor and pencolor, + They use 0 to 3 arguments as follows: + - color() + Return the current pencolor and the current fillcolor as + a pair of color specification strings or tuples as returned + by pencolor() and fillcolor(). + - color(colorstring), color((r,g,b)), color(r,g,b) + Inputs as in pencolor(), set both, fillcolor and pencolor, to the given value. - color(colorstring1, colorstring2), - color((r1,g1,b1), (r2,g2,b2)) - equivalent to pencolor(colorstring1) and fillcolor(colorstring2) - and analogously, if the other input format is used. + - color(colorstring1, colorstring2), color((r1,g1,b1), (r2,g2,b2)) + Equivalent to pencolor(colorstring1) and fillcolor(colorstring2) + and analogously if the other input format is used. If turtleshape is a polygon, outline and interior of that polygon is drawn with the newly set colors. @@ -2253,9 +2267,9 @@ def color(self, *args): >>> turtle.color() ('red', 'green') >>> colormode(255) - >>> color((40, 80, 120), (160, 200, 240)) + >>> color(('#285078', '#a0c8f0')) >>> color() - ('#285078', '#a0c8f0') + ((40.0, 80.0, 120.0), (160.0, 200.0, 240.0)) """ if args: l = len(args) @@ -2277,28 +2291,32 @@ def pencolor(self, *args): Arguments: Four input formats are allowed: - pencolor() - Return the current pencolor as color specification string, - possibly in hex-number format (see example). - May be used as input to another color/pencolor/fillcolor call. + Return the current pencolor as color specification string or + as a tuple (see example). May be used as input to another + color/pencolor/fillcolor/bgcolor call. - pencolor(colorstring) - s is a Tk color specification string, such as "red" or "yellow" + Set pencolor to colorstring, which is a Tk color + specification string, such as "red", "yellow", or "#33cc8c". - pencolor((r, g, b)) - *a tuple* of r, g, and b, which represent, an RGB color, - and each of r, g, and b are in the range 0..colormode, - where colormode is either 1.0 or 255 + Set pencolor to the RGB color represented by the tuple of + r, g, and b. Each of r, g, and b must be in the range + 0..colormode, where colormode is either 1.0 or 255 (see + colormode()). - pencolor(r, g, b) - r, g, and b represent an RGB color, and each of r, g, and b - are in the range 0..colormode + Set pencolor to the RGB color represented by r, g, and b. + Each of r, g, and b must be in the range 0..colormode. If turtleshape is a polygon, the outline of that polygon is drawn with the newly set pencolor. Example (for a Turtle instance named turtle): >>> turtle.pencolor('brown') - >>> tup = (0.2, 0.8, 0.55) - >>> turtle.pencolor(tup) >>> turtle.pencolor() - '#33cc8c' + 'brown' + >>> colormode(255) + >>> turtle.pencolor('#32c18f') + >>> turtle.pencolor() + (50.0, 193.0, 143.0) """ if args: color = self._colorstr(args) @@ -2315,26 +2333,31 @@ def fillcolor(self, *args): Four input formats are allowed: - fillcolor() Return the current fillcolor as color specification string, - possibly in hex-number format (see example). - May be used as input to another color/pencolor/fillcolor call. + possibly in tuple format (see example). May be used as + input to another color/pencolor/fillcolor/bgcolor call. - fillcolor(colorstring) - s is a Tk color specification string, such as "red" or "yellow" + Set fillcolor to colorstring, which is a Tk color + specification string, such as "red", "yellow", or "#33cc8c". - fillcolor((r, g, b)) - *a tuple* of r, g, and b, which represent, an RGB color, - and each of r, g, and b are in the range 0..colormode, - where colormode is either 1.0 or 255 + Set fillcolor to the RGB color represented by the tuple of + r, g, and b. Each of r, g, and b must be in the range + 0..colormode, where colormode is either 1.0 or 255 (see + colormode()). - fillcolor(r, g, b) - r, g, and b represent an RGB color, and each of r, g, and b - are in the range 0..colormode + Set fillcolor to the RGB color represented by r, g, and b. + Each of r, g, and b must be in the range 0..colormode. If turtleshape is a polygon, the interior of that polygon is drawn with the newly set fillcolor. Example (for a Turtle instance named turtle): >>> turtle.fillcolor('violet') - >>> col = turtle.pencolor() - >>> turtle.fillcolor(col) - >>> turtle.fillcolor(0, .5, 0) + >>> turtle.fillcolor() + 'violet' + >>> colormode(255) + >>> turtle.fillcolor('#ffffff') + >>> turtle.fillcolor() + (255.0, 255.0, 255.0) """ if args: color = self._colorstr(args) From 6481539a6d9692ddf13ab01baad1bc9133409413 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Mon, 13 Oct 2025 22:55:07 +0530 Subject: [PATCH 132/373] gh-125996: fix thread safety of `collections.OrderedDict` (#133734) --- Include/internal/pycore_dict.h | 6 + ...-05-10-17-42-03.gh-issue-125996.vaQp0-.rst | 1 + Objects/clinic/odictobject.c.h | 106 ++++++++- Objects/dictobject.c | 16 +- Objects/odictobject.c | 213 ++++++++++++------ 5 files changed, 266 insertions(+), 76 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-05-10-17-42-03.gh-issue-125996.vaQp0-.rst diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index 92b6b819ef2..b8fe360321d 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -30,6 +30,10 @@ PyAPI_FUNC(int) _PyDict_SetItem_KnownHash(PyObject *mp, PyObject *key, // Export for '_asyncio' shared extension PyAPI_FUNC(int) _PyDict_DelItem_KnownHash(PyObject *mp, PyObject *key, Py_hash_t hash); + +extern int _PyDict_DelItem_KnownHash_LockHeld(PyObject *mp, PyObject *key, + Py_hash_t hash); + extern int _PyDict_Contains_KnownHash(PyObject *, PyObject *, Py_hash_t); // "Id" variants @@ -47,6 +51,8 @@ extern int _PyDict_HasOnlyStringKeys(PyObject *mp); // Export for '_ctypes' shared extension PyAPI_FUNC(Py_ssize_t) _PyDict_SizeOf(PyDictObject *); +extern Py_ssize_t _PyDict_SizeOf_LockHeld(PyDictObject *); + #define _PyDict_HasSplitTable(d) ((d)->ma_values != NULL) /* Like PyDict_Merge, but override can be 0, 1 or 2. If override is 0, diff --git a/Misc/NEWS.d/next/Library/2025-05-10-17-42-03.gh-issue-125996.vaQp0-.rst b/Misc/NEWS.d/next/Library/2025-05-10-17-42-03.gh-issue-125996.vaQp0-.rst new file mode 100644 index 00000000000..4bbfaa3a5e2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-10-17-42-03.gh-issue-125996.vaQp0-.rst @@ -0,0 +1 @@ +Fix thread safety of :class:`collections.OrderedDict`. Patch by Kumar Aditya. diff --git a/Objects/clinic/odictobject.c.h b/Objects/clinic/odictobject.c.h index e71c29a1b26..894e9be91bb 100644 --- a/Objects/clinic/odictobject.c.h +++ b/Objects/clinic/odictobject.c.h @@ -6,6 +6,7 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() PyDoc_STRVAR(OrderedDict_fromkeys__doc__, @@ -73,6 +74,53 @@ exit: return return_value; } +PyDoc_STRVAR(OrderedDict___sizeof____doc__, +"__sizeof__($self, /)\n" +"--\n" +"\n"); + +#define ORDEREDDICT___SIZEOF___METHODDEF \ + {"__sizeof__", (PyCFunction)OrderedDict___sizeof__, METH_NOARGS, OrderedDict___sizeof____doc__}, + +static Py_ssize_t +OrderedDict___sizeof___impl(PyODictObject *self); + +static PyObject * +OrderedDict___sizeof__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + Py_ssize_t _return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + _return_value = OrderedDict___sizeof___impl((PyODictObject *)self); + Py_END_CRITICAL_SECTION(); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyLong_FromSsize_t(_return_value); + +exit: + return return_value; +} + +PyDoc_STRVAR(OrderedDict___reduce____doc__, +"__reduce__($self, /)\n" +"--\n" +"\n" +"Return state information for pickling"); + +#define ORDEREDDICT___REDUCE___METHODDEF \ + {"__reduce__", (PyCFunction)OrderedDict___reduce__, METH_NOARGS, OrderedDict___reduce____doc__}, + +static PyObject * +OrderedDict___reduce___impl(PyODictObject *od); + +static PyObject * +OrderedDict___reduce__(PyObject *od, PyObject *Py_UNUSED(ignored)) +{ + return OrderedDict___reduce___impl((PyODictObject *)od); +} + PyDoc_STRVAR(OrderedDict_setdefault__doc__, "setdefault($self, /, key, default=None)\n" "--\n" @@ -135,7 +183,9 @@ OrderedDict_setdefault(PyObject *self, PyObject *const *args, Py_ssize_t nargs, } default_value = args[1]; skip_optional_pos: + Py_BEGIN_CRITICAL_SECTION(self); return_value = OrderedDict_setdefault_impl((PyODictObject *)self, key, default_value); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -204,7 +254,9 @@ OrderedDict_pop(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObjec } default_value = args[1]; skip_optional_pos: + Py_BEGIN_CRITICAL_SECTION(self); return_value = OrderedDict_pop_impl((PyODictObject *)self, key, default_value); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -272,12 +324,62 @@ OrderedDict_popitem(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyO goto exit; } skip_optional_pos: + Py_BEGIN_CRITICAL_SECTION(self); return_value = OrderedDict_popitem_impl((PyODictObject *)self, last); + Py_END_CRITICAL_SECTION(); exit: return return_value; } +PyDoc_STRVAR(OrderedDict_clear__doc__, +"clear($self, /)\n" +"--\n" +"\n" +"Remove all items from ordered dict."); + +#define ORDEREDDICT_CLEAR_METHODDEF \ + {"clear", (PyCFunction)OrderedDict_clear, METH_NOARGS, OrderedDict_clear__doc__}, + +static PyObject * +OrderedDict_clear_impl(PyODictObject *self); + +static PyObject * +OrderedDict_clear(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = OrderedDict_clear_impl((PyODictObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(OrderedDict_copy__doc__, +"copy($self, /)\n" +"--\n" +"\n" +"A shallow copy of ordered dict."); + +#define ORDEREDDICT_COPY_METHODDEF \ + {"copy", (PyCFunction)OrderedDict_copy, METH_NOARGS, OrderedDict_copy__doc__}, + +static PyObject * +OrderedDict_copy_impl(PyObject *od); + +static PyObject * +OrderedDict_copy(PyObject *od, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(od); + return_value = OrderedDict_copy_impl(od); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(OrderedDict_move_to_end__doc__, "move_to_end($self, /, key, last=True)\n" "--\n" @@ -342,9 +444,11 @@ OrderedDict_move_to_end(PyObject *self, PyObject *const *args, Py_ssize_t nargs, goto exit; } skip_optional_pos: + Py_BEGIN_CRITICAL_SECTION(self); return_value = OrderedDict_move_to_end_impl((PyODictObject *)self, key, last); + Py_END_CRITICAL_SECTION(); exit: return return_value; } -/*[clinic end generated code: output=7d8206823bb1f419 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=7bc997ca7900f06f input=a9049054013a1b77]*/ diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 24188ffe713..ddf9bde63f3 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2819,8 +2819,8 @@ PyDict_DelItem(PyObject *op, PyObject *key) return _PyDict_DelItem_KnownHash(op, key, hash); } -static int -delitem_knownhash_lock_held(PyObject *op, PyObject *key, Py_hash_t hash) +int +_PyDict_DelItem_KnownHash_LockHeld(PyObject *op, PyObject *key, Py_hash_t hash) { Py_ssize_t ix; PyDictObject *mp; @@ -2855,7 +2855,7 @@ _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) { int res; Py_BEGIN_CRITICAL_SECTION(op); - res = delitem_knownhash_lock_held(op, key, hash); + res = _PyDict_DelItem_KnownHash_LockHeld(op, key, hash); Py_END_CRITICAL_SECTION(); return res; } @@ -4703,9 +4703,11 @@ dict_tp_clear(PyObject *op) static PyObject *dictiter_new(PyDictObject *, PyTypeObject *); -static Py_ssize_t -sizeof_lock_held(PyDictObject *mp) +Py_ssize_t +_PyDict_SizeOf_LockHeld(PyDictObject *mp) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(mp); + size_t res = _PyObject_SIZE(Py_TYPE(mp)); if (_PyDict_HasSplitTable(mp)) { res += shared_keys_usable_size(mp->ma_keys) * sizeof(PyObject*); @@ -4724,7 +4726,7 @@ _PyDict_SizeOf(PyDictObject *mp) { Py_ssize_t res; Py_BEGIN_CRITICAL_SECTION(mp); - res = sizeof_lock_held(mp); + res = _PyDict_SizeOf_LockHeld(mp); Py_END_CRITICAL_SECTION(); return res; @@ -6909,7 +6911,7 @@ _PyDict_SetItem_LockHeld(PyDictObject *dict, PyObject *name, PyObject *value) dict_unhashable_type(name); return -1; } - return delitem_knownhash_lock_held((PyObject *)dict, name, hash); + return _PyDict_DelItem_KnownHash_LockHeld((PyObject *)dict, name, hash); } else { return setitem_lock_held(dict, name, value); } diff --git a/Objects/odictobject.c b/Objects/odictobject.c index bcdf94c6c64..45d2ea0203a 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -536,6 +536,7 @@ struct _odictnode { static Py_ssize_t _odict_get_index_raw(PyODictObject *od, PyObject *key, Py_hash_t hash) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); PyObject *value = NULL; PyDictKeysObject *keys = ((PyDictObject *)od)->ma_keys; Py_ssize_t ix; @@ -560,6 +561,7 @@ _odict_get_index_raw(PyODictObject *od, PyObject *key, Py_hash_t hash) static int _odict_resize(PyODictObject *od) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); Py_ssize_t size, i; _ODictNode **fast_nodes, *node; @@ -596,6 +598,7 @@ _odict_resize(PyODictObject *od) static Py_ssize_t _odict_get_index(PyODictObject *od, PyObject *key, Py_hash_t hash) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); PyDictKeysObject *keys; assert(key != NULL); @@ -616,6 +619,7 @@ _odict_get_index(PyODictObject *od, PyObject *key, Py_hash_t hash) static _ODictNode * _odict_find_node_hash(PyODictObject *od, PyObject *key, Py_hash_t hash) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); Py_ssize_t index; if (_odict_EMPTY(od)) @@ -630,6 +634,7 @@ _odict_find_node_hash(PyODictObject *od, PyObject *key, Py_hash_t hash) static _ODictNode * _odict_find_node(PyODictObject *od, PyObject *key) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); Py_ssize_t index; Py_hash_t hash; @@ -648,6 +653,7 @@ _odict_find_node(PyODictObject *od, PyObject *key) static void _odict_add_head(PyODictObject *od, _ODictNode *node) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); _odictnode_PREV(node) = NULL; _odictnode_NEXT(node) = _odict_FIRST(od); if (_odict_FIRST(od) == NULL) @@ -661,6 +667,7 @@ _odict_add_head(PyODictObject *od, _ODictNode *node) static void _odict_add_tail(PyODictObject *od, _ODictNode *node) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); _odictnode_PREV(node) = _odict_LAST(od); _odictnode_NEXT(node) = NULL; if (_odict_LAST(od) == NULL) @@ -675,6 +682,7 @@ _odict_add_tail(PyODictObject *od, _ODictNode *node) static int _odict_add_new_node(PyODictObject *od, PyObject *key, Py_hash_t hash) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); Py_ssize_t i; _ODictNode *node; @@ -719,6 +727,7 @@ _odict_add_new_node(PyODictObject *od, PyObject *key, Py_hash_t hash) static void _odict_remove_node(PyODictObject *od, _ODictNode *node) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); if (_odict_FIRST(od) == node) _odict_FIRST(od) = _odictnode_NEXT(node); else if (_odictnode_PREV(node) != NULL) @@ -754,6 +763,7 @@ static int _odict_clear_node(PyODictObject *od, _ODictNode *node, PyObject *key, Py_hash_t hash) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); Py_ssize_t i; assert(key != NULL); @@ -952,31 +962,34 @@ OrderedDict_fromkeys_impl(PyTypeObject *type, PyObject *seq, PyObject *value) return _PyDict_FromKeys((PyObject *)type, seq, value); } -/* __sizeof__() */ +/*[clinic input] +@critical_section +OrderedDict.__sizeof__ -> Py_ssize_t +[clinic start generated code]*/ -/* OrderedDict.__sizeof__() does not have a docstring. */ -PyDoc_STRVAR(odict_sizeof__doc__, ""); - -static PyObject * -odict_sizeof(PyObject *op, PyObject *Py_UNUSED(ignored)) +static Py_ssize_t +OrderedDict___sizeof___impl(PyODictObject *self) +/*[clinic end generated code: output=1a8560db8cf83ac5 input=655e989ae24daa6a]*/ { - PyODictObject *od = _PyODictObject_CAST(op); - Py_ssize_t res = _PyDict_SizeOf((PyDictObject *)od); - res += sizeof(_ODictNode *) * od->od_fast_nodes_size; /* od_fast_nodes */ - if (!_odict_EMPTY(od)) { - res += sizeof(_ODictNode) * PyODict_SIZE(od); /* linked-list */ + Py_ssize_t res = _PyDict_SizeOf_LockHeld((PyDictObject *)self); + res += sizeof(_ODictNode *) * self->od_fast_nodes_size; /* od_fast_nodes */ + if (!_odict_EMPTY(self)) { + res += sizeof(_ODictNode) * PyODict_SIZE(self); /* linked-list */ } - return PyLong_FromSsize_t(res); + return res; } -/* __reduce__() */ +/*[clinic input] +OrderedDict.__reduce__ + self as od: self(type="PyODictObject *") -PyDoc_STRVAR(odict_reduce__doc__, "Return state information for pickling"); +Return state information for pickling +[clinic start generated code]*/ static PyObject * -odict_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) +OrderedDict___reduce___impl(PyODictObject *od) +/*[clinic end generated code: output=71eeb81f760a6a8e input=b0467c7ec400fe5e]*/ { - register PyODictObject *od = _PyODictObject_CAST(op); PyObject *state, *result = NULL; PyObject *items_iter, *items, *args = NULL; @@ -1011,8 +1024,10 @@ odict_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) /* setdefault(): Skips __missing__() calls. */ +static int PyODict_SetItem_LockHeld(PyObject *self, PyObject *key, PyObject *value); /*[clinic input] +@critical_section OrderedDict.setdefault key: object @@ -1026,7 +1041,7 @@ Return the value for key if key is in the dictionary, else default. static PyObject * OrderedDict_setdefault_impl(PyODictObject *self, PyObject *key, PyObject *default_value) -/*[clinic end generated code: output=97537cb7c28464b6 input=38e098381c1efbc6]*/ +/*[clinic end generated code: output=97537cb7c28464b6 input=d7b93e92734f99b5]*/ { PyObject *result = NULL; @@ -1036,7 +1051,7 @@ OrderedDict_setdefault_impl(PyODictObject *self, PyObject *key, if (PyErr_Occurred()) return NULL; assert(_odict_find_node(self, key) == NULL); - if (PyODict_SetItem((PyObject *)self, key, default_value) >= 0) { + if (PyODict_SetItem_LockHeld((PyObject *)self, key, default_value) >= 0) { result = Py_NewRef(default_value); } } @@ -1066,10 +1081,9 @@ static PyObject * _odict_popkey_hash(PyObject *od, PyObject *key, PyObject *failobj, Py_hash_t hash) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); + PyObject *value = NULL; - - Py_BEGIN_CRITICAL_SECTION(od); - _ODictNode *node = _odict_find_node_hash(_PyODictObject_CAST(od), key, hash); if (node != NULL) { /* Pop the node first to avoid a possible dict resize (due to @@ -1094,7 +1108,6 @@ _odict_popkey_hash(PyObject *od, PyObject *key, PyObject *failobj, PyErr_SetObject(PyExc_KeyError, key); } } - Py_END_CRITICAL_SECTION(); done: return value; @@ -1102,6 +1115,7 @@ _odict_popkey_hash(PyObject *od, PyObject *key, PyObject *failobj, /* Skips __missing__() calls. */ /*[clinic input] +@critical_section @permit_long_summary OrderedDict.pop @@ -1117,7 +1131,7 @@ raise a KeyError. static PyObject * OrderedDict_pop_impl(PyODictObject *self, PyObject *key, PyObject *default_value) -/*[clinic end generated code: output=7a6447d104e7494b input=eebd40ac51666d33]*/ +/*[clinic end generated code: output=7a6447d104e7494b input=0742e3c9bf076a72]*/ { Py_hash_t hash = PyObject_Hash(key); if (hash == -1) @@ -1129,6 +1143,7 @@ OrderedDict_pop_impl(PyODictObject *self, PyObject *key, /* popitem() */ /*[clinic input] +@critical_section OrderedDict.popitem last: bool = True @@ -1140,7 +1155,7 @@ Pairs are returned in LIFO order if last is true or FIFO order if false. static PyObject * OrderedDict_popitem_impl(PyODictObject *self, int last) -/*[clinic end generated code: output=98e7d986690d49eb input=d992ac5ee8305e1a]*/ +/*[clinic end generated code: output=98e7d986690d49eb input=8aafc7433e0a40e7]*/ { PyObject *key, *value, *item = NULL; _ODictNode *node; @@ -1169,6 +1184,9 @@ OrderedDict_popitem_impl(PyODictObject *self, int last) PyDoc_STRVAR(odict_keys__doc__, ""); static PyObject * odictkeys_new(PyObject *od, PyObject *Py_UNUSED(ignored)); /* forward */ +static int +_PyODict_SetItem_KnownHash_LockHeld(PyObject *od, PyObject *key, PyObject *value, + Py_hash_t hash); /* forward */ /* values() */ @@ -1194,32 +1212,36 @@ static PyObject * mutablemapping_update(PyObject *, PyObject *, PyObject *); #define odict_update mutablemapping_update -/* clear() */ +/*[clinic input] +@critical_section +OrderedDict.clear -PyDoc_STRVAR(odict_clear__doc__, - "od.clear() -> None. Remove all items from od."); +Remove all items from ordered dict. +[clinic start generated code]*/ static PyObject * -odict_clear(PyObject *op, PyObject *Py_UNUSED(ignored)) +OrderedDict_clear_impl(PyODictObject *self) +/*[clinic end generated code: output=a1a76d1322f556c5 input=08b12322e74c535c]*/ { - register PyODictObject *od = _PyODictObject_CAST(op); - PyDict_Clear(op); - _odict_clear_nodes(od); + _PyDict_Clear_LockHeld((PyObject *)self); + _odict_clear_nodes(self); Py_RETURN_NONE; } /* copy() */ -/* forward */ -static int _PyODict_SetItem_KnownHash(PyObject *, PyObject *, PyObject *, - Py_hash_t); +/*[clinic input] +@critical_section +OrderedDict.copy + self as od: self -PyDoc_STRVAR(odict_copy__doc__, "od.copy() -> a shallow copy of od"); +A shallow copy of ordered dict. +[clinic start generated code]*/ static PyObject * -odict_copy(PyObject *op, PyObject *Py_UNUSED(ignored)) +OrderedDict_copy_impl(PyObject *od) +/*[clinic end generated code: output=9cdbe7394aecc576 input=e329951ae617ed48]*/ { - register PyODictObject *od = _PyODictObject_CAST(op); _ODictNode *node; PyObject *od_copy; @@ -1239,8 +1261,8 @@ odict_copy(PyObject *op, PyObject *Py_UNUSED(ignored)) PyErr_SetObject(PyExc_KeyError, key); goto fail; } - if (_PyODict_SetItem_KnownHash((PyObject *)od_copy, key, value, - _odictnode_HASH(node)) != 0) + if (_PyODict_SetItem_KnownHash_LockHeld((PyObject *)od_copy, key, value, + _odictnode_HASH(node)) != 0) goto fail; } } @@ -1288,6 +1310,7 @@ odict_reversed(PyObject *op, PyObject *Py_UNUSED(ignored)) /* move_to_end() */ /*[clinic input] +@critical_section OrderedDict.move_to_end key: object @@ -1300,7 +1323,7 @@ Raise KeyError if the element does not exist. static PyObject * OrderedDict_move_to_end_impl(PyODictObject *self, PyObject *key, int last) -/*[clinic end generated code: output=fafa4c5cc9b92f20 input=d6ceff7132a2fcd7]*/ +/*[clinic end generated code: output=fafa4c5cc9b92f20 input=09f8bc7053c0f6d4]*/ { _ODictNode *node; @@ -1341,10 +1364,8 @@ static PyMethodDef odict_methods[] = { /* overridden dict methods */ ORDEREDDICT_FROMKEYS_METHODDEF - {"__sizeof__", odict_sizeof, METH_NOARGS, - odict_sizeof__doc__}, - {"__reduce__", odict_reduce, METH_NOARGS, - odict_reduce__doc__}, + ORDEREDDICT___SIZEOF___METHODDEF + ORDEREDDICT___REDUCE___METHODDEF ORDEREDDICT_SETDEFAULT_METHODDEF ORDEREDDICT_POP_METHODDEF ORDEREDDICT_POPITEM_METHODDEF @@ -1356,11 +1377,8 @@ static PyMethodDef odict_methods[] = { odict_items__doc__}, {"update", _PyCFunction_CAST(odict_update), METH_VARARGS | METH_KEYWORDS, odict_update__doc__}, - {"clear", odict_clear, METH_NOARGS, - odict_clear__doc__}, - {"copy", odict_copy, METH_NOARGS, - odict_copy__doc__}, - + ORDEREDDICT_CLEAR_METHODDEF + ORDEREDDICT_COPY_METHODDEF /* new methods */ {"__reversed__", odict_reversed, METH_NOARGS, odict_reversed__doc__}, @@ -1459,7 +1477,8 @@ odict_tp_clear(PyObject *op) { PyODictObject *od = _PyODictObject_CAST(op); Py_CLEAR(od->od_inst_dict); - PyDict_Clear((PyObject *)od); + // cannot use lock held variant as critical section is not held here + PyDict_Clear(op); _odict_clear_nodes(od); return 0; } @@ -1467,7 +1486,7 @@ odict_tp_clear(PyObject *op) /* tp_richcompare */ static PyObject * -odict_richcompare(PyObject *v, PyObject *w, int op) +odict_richcompare_lock_held(PyObject *v, PyObject *w, int op) { if (!PyODict_Check(v) || !PyDict_Check(w)) { Py_RETURN_NOTIMPLEMENTED; @@ -1500,6 +1519,16 @@ odict_richcompare(PyObject *v, PyObject *w, int op) } } +static PyObject * +odict_richcompare(PyObject *v, PyObject *w, int op) +{ + PyObject *res; + Py_BEGIN_CRITICAL_SECTION2(v, w); + res = odict_richcompare_lock_held(v, w, op); + Py_END_CRITICAL_SECTION2(); + return res; +} + /* tp_iter */ static PyObject * @@ -1590,10 +1619,11 @@ PyODict_New(void) } static int -_PyODict_SetItem_KnownHash(PyObject *od, PyObject *key, PyObject *value, - Py_hash_t hash) +_PyODict_SetItem_KnownHash_LockHeld(PyObject *od, PyObject *key, PyObject *value, + Py_hash_t hash) { - int res = _PyDict_SetItem_KnownHash(od, key, value, hash); + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); + int res = _PyDict_SetItem_KnownHash_LockHeld((PyDictObject *)od, key, value, hash); if (res == 0) { res = _odict_add_new_node(_PyODictObject_CAST(od), key, hash); if (res < 0) { @@ -1606,18 +1636,32 @@ _PyODict_SetItem_KnownHash(PyObject *od, PyObject *key, PyObject *value, return res; } -int -PyODict_SetItem(PyObject *od, PyObject *key, PyObject *value) + +static int +PyODict_SetItem_LockHeld(PyObject *od, PyObject *key, PyObject *value) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); Py_hash_t hash = PyObject_Hash(key); - if (hash == -1) + if (hash == -1) { return -1; - return _PyODict_SetItem_KnownHash(od, key, value, hash); + } + return _PyODict_SetItem_KnownHash_LockHeld(od, key, value, hash); } int -PyODict_DelItem(PyObject *od, PyObject *key) +PyODict_SetItem(PyObject *od, PyObject *key, PyObject *value) { + int res; + Py_BEGIN_CRITICAL_SECTION(od); + res = PyODict_SetItem_LockHeld(od, key, value); + Py_END_CRITICAL_SECTION(); + return res; +} + +int +PyODict_DelItem_LockHeld(PyObject *od, PyObject *key) +{ + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); int res; Py_hash_t hash = PyObject_Hash(key); if (hash == -1) @@ -1625,9 +1669,18 @@ PyODict_DelItem(PyObject *od, PyObject *key) res = _odict_clear_node(_PyODictObject_CAST(od), NULL, key, hash); if (res < 0) return -1; - return _PyDict_DelItem_KnownHash(od, key, hash); + return _PyDict_DelItem_KnownHash_LockHeld(od, key, hash); } +int +PyODict_DelItem(PyObject *od, PyObject *key) +{ + int res; + Py_BEGIN_CRITICAL_SECTION(od); + res = PyODict_DelItem_LockHeld(od, key); + Py_END_CRITICAL_SECTION(); + return res; +} /* ------------------------------------------- * The OrderedDict views (keys/values/items) @@ -1669,14 +1722,14 @@ odictiter_traverse(PyObject *op, visitproc visit, void *arg) /* In order to protect against modifications during iteration, we track * the current key instead of the current node. */ static PyObject * -odictiter_nextkey(odictiterobject *di) +odictiter_nextkey_lock_held(odictiterobject *di) { + assert(di->di_odict != NULL); + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(di->di_odict); PyObject *key = NULL; _ODictNode *node; int reversed = di->kind & _odict_ITER_REVERSED; - if (di->di_odict == NULL) - return NULL; if (di->di_current == NULL) goto done; /* We're already done. */ @@ -1721,8 +1774,23 @@ odictiter_nextkey(odictiterobject *di) return key; } + static PyObject * -odictiter_iternext(PyObject *op) +odictiter_nextkey(odictiterobject *di) +{ + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(di); + if (di->di_odict == NULL) { + return NULL; + } + PyObject *res; + Py_BEGIN_CRITICAL_SECTION(di->di_odict); + res = odictiter_nextkey_lock_held(di); + Py_END_CRITICAL_SECTION(); + return res; +} + +static PyObject * +odictiter_iternext_lock_held(PyObject *op) { odictiterobject *di = (odictiterobject*)op; PyObject *result, *value; @@ -1736,14 +1804,12 @@ odictiter_iternext(PyObject *op) return key; } - value = PyODict_GetItem((PyObject *)di->di_odict, key); /* borrowed */ - if (value == NULL) { + if (PyDict_GetItemRef((PyObject *)di->di_odict, key, &value) != 1) { if (!PyErr_Occurred()) PyErr_SetObject(PyExc_KeyError, key); Py_DECREF(key); goto done; } - Py_INCREF(value); /* Handle the values case. */ if (!(di->kind & _odict_ITER_KEYS)) { @@ -1754,7 +1820,7 @@ odictiter_iternext(PyObject *op) /* Handle the items case. */ result = di->di_result; - if (Py_REFCNT(result) == 1) { + if (_PyObject_IsUniquelyReferenced(result)) { /* not in use so we can reuse it * (the common case during iteration) */ Py_INCREF(result); @@ -1783,6 +1849,17 @@ odictiter_iternext(PyObject *op) return NULL; } +static PyObject * +odictiter_iternext(PyObject *op) +{ + PyObject *res; + Py_BEGIN_CRITICAL_SECTION(op); + res = odictiter_iternext_lock_held(op); + Py_END_CRITICAL_SECTION(); + return res; +} + + /* No need for tp_clear because odictiterobject is not mutable. */ PyDoc_STRVAR(reduce_doc, "Return state information for pickling"); From c46265d94a2d2c5bcaabbbc312f8f6ac9162cd5f Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 13 Oct 2025 20:43:14 +0100 Subject: [PATCH 133/373] gh-139810: Always generate -dev tags for PyManager packages (GH-139814) --- ...-10-08-22-54-38.gh-issue-139810.LAaemi.rst | 2 ++ PC/layout/support/pymanager.py | 22 ++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2025-10-08-22-54-38.gh-issue-139810.LAaemi.rst diff --git a/Misc/NEWS.d/next/Windows/2025-10-08-22-54-38.gh-issue-139810.LAaemi.rst b/Misc/NEWS.d/next/Windows/2025-10-08-22-54-38.gh-issue-139810.LAaemi.rst new file mode 100644 index 00000000000..55252f288b0 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2025-10-08-22-54-38.gh-issue-139810.LAaemi.rst @@ -0,0 +1,2 @@ +Installing with ``py install 3[.x]-dev`` will now select final versions as +well as prereleases. diff --git a/PC/layout/support/pymanager.py b/PC/layout/support/pymanager.py index 667c89cdd2c..831d49ea3f9 100644 --- a/PC/layout/support/pymanager.py +++ b/PC/layout/support/pymanager.py @@ -80,7 +80,9 @@ def calculate_install_json(ns, *, for_embed=False, for_test=False): # Tag used in runtime ID (for side-by-side install/updates) ID_TAG = XY_ARCH_TAG - # Tag shown in 'py list' output + # Tag shown in 'py list' output. + # gh-139810: We only include '-dev' here for prereleases, even though it + # works for final releases too. DISPLAY_TAG = f"{XY_TAG}-dev{TAG_ARCH}" if VER_SUFFIX else XY_ARCH_TAG # Tag used for PEP 514 registration SYS_WINVER = XY_TAG + (TAG_ARCH if TAG_ARCH != '-64' else '') @@ -102,9 +104,10 @@ def calculate_install_json(ns, *, for_embed=False, for_test=False): FULL_ARCH_TAG, XY_ARCH_TAG, X_ARCH_TAG, - # X_TAG and XY_TAG doesn't include VER_SUFFIX, so create -dev versions - f"{XY_TAG}-dev{TAG_ARCH}" if XY_TAG and VER_SUFFIX else "", - f"{X_TAG}-dev{TAG_ARCH}" if X_TAG and VER_SUFFIX else "", + # gh-139810: The -dev tags are always included so that the latest + # release is chosen, no matter whether it's prerelease or final. + f"{XY_TAG}-dev{TAG_ARCH}" if XY_TAG else "", + f"{X_TAG}-dev{TAG_ARCH}" if X_TAG else "", ] # Generate run-for entries for each target. @@ -115,16 +118,15 @@ def calculate_install_json(ns, *, for_embed=False, for_test=False): ]: if not base["target"]: continue - STD_RUN_FOR.append({**base, "tag": FULL_ARCH_TAG}) + STD_RUN_FOR.extend([ + {**base, "tag": FULL_ARCH_TAG}, + {**base, "tag": f"{XY_TAG}-dev{TAG_ARCH}" if XY_TAG else ""}, + {**base, "tag": f"{X_TAG}-dev{TAG_ARCH}" if X_TAG else ""}, + ]) if XY_TAG: STD_RUN_FOR.append({**base, "tag": XY_ARCH_TAG}) if X_TAG: STD_RUN_FOR.append({**base, "tag": X_ARCH_TAG}) - if VER_SUFFIX: - STD_RUN_FOR.extend([ - {**base, "tag": f"{XY_TAG}-dev{TAG_ARCH}" if XY_TAG else ""}, - {**base, "tag": f"{X_TAG}-dev{TAG_ARCH}" if X_TAG else ""}, - ]) # Generate alias entries for each target. We need both arch and non-arch # versions as well as windowed/non-windowed versions to make sure that all From be60e4b4f34a097d999d337446786ae14e35c964 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Mon, 13 Oct 2025 23:10:39 +0300 Subject: [PATCH 134/373] gh-140000: Traverse `name` attribute for `TypeVar`, `TypeVarTuple`, `TypeAliasType`, `ParamSpec` (#140016) --- ...025-10-13-17-56-23.gh-issue-140000.tLhn3e.rst | 4 ++++ Objects/typevarobject.c | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-13-17-56-23.gh-issue-140000.tLhn3e.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-13-17-56-23.gh-issue-140000.tLhn3e.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-13-17-56-23.gh-issue-140000.tLhn3e.rst new file mode 100644 index 00000000000..2c6259635ea --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-13-17-56-23.gh-issue-140000.tLhn3e.rst @@ -0,0 +1,4 @@ +Fix potential memory leak when a reference cycle exists between an instance +of :class:`typing.TypeAliasType`, :class:`typing.TypeVar`, +:class:`typing.ParamSpec`, or :class:`typing.TypeVarTuple` and its +``__name__`` attribute. Patch by Mikhail Efimov. diff --git a/Objects/typevarobject.c b/Objects/typevarobject.c index 522e9fd9c95..4fe46e9fccb 100644 --- a/Objects/typevarobject.c +++ b/Objects/typevarobject.c @@ -472,7 +472,7 @@ typevar_dealloc(PyObject *self) _PyObject_GC_UNTRACK(self); - Py_DECREF(tv->name); + Py_XDECREF(tv->name); Py_XDECREF(tv->bound); Py_XDECREF(tv->evaluate_bound); Py_XDECREF(tv->constraints); @@ -491,6 +491,7 @@ typevar_traverse(PyObject *self, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(self)); typevarobject *tv = typevarobject_CAST(self); + Py_VISIT(tv->name); Py_VISIT(tv->bound); Py_VISIT(tv->evaluate_bound); Py_VISIT(tv->constraints); @@ -505,6 +506,7 @@ static int typevar_clear(PyObject *op) { typevarobject *self = typevarobject_CAST(op); + Py_CLEAR(self->name); Py_CLEAR(self->bound); Py_CLEAR(self->evaluate_bound); Py_CLEAR(self->constraints); @@ -1171,7 +1173,7 @@ paramspec_dealloc(PyObject *self) _PyObject_GC_UNTRACK(self); - Py_DECREF(ps->name); + Py_XDECREF(ps->name); Py_XDECREF(ps->bound); Py_XDECREF(ps->default_value); Py_XDECREF(ps->evaluate_default); @@ -1187,6 +1189,7 @@ paramspec_traverse(PyObject *self, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(self)); paramspecobject *ps = paramspecobject_CAST(self); + Py_VISIT(ps->name); Py_VISIT(ps->bound); Py_VISIT(ps->default_value); Py_VISIT(ps->evaluate_default); @@ -1198,6 +1201,7 @@ static int paramspec_clear(PyObject *op) { paramspecobject *self = paramspecobject_CAST(op); + Py_CLEAR(self->name); Py_CLEAR(self->bound); Py_CLEAR(self->default_value); Py_CLEAR(self->evaluate_default); @@ -1519,7 +1523,7 @@ typevartuple_dealloc(PyObject *self) _PyObject_GC_UNTRACK(self); typevartupleobject *tvt = typevartupleobject_CAST(self); - Py_DECREF(tvt->name); + Py_XDECREF(tvt->name); Py_XDECREF(tvt->default_value); Py_XDECREF(tvt->evaluate_default); PyObject_ClearManagedDict(self); @@ -1683,6 +1687,7 @@ typevartuple_traverse(PyObject *self, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(self)); typevartupleobject *tvt = typevartupleobject_CAST(self); + Py_VISIT(tvt->name); Py_VISIT(tvt->default_value); Py_VISIT(tvt->evaluate_default); PyObject_VisitManagedDict(self, visit, arg); @@ -1693,6 +1698,7 @@ static int typevartuple_clear(PyObject *self) { typevartupleobject *tvt = typevartupleobject_CAST(self); + Py_CLEAR(tvt->name); Py_CLEAR(tvt->default_value); Py_CLEAR(tvt->evaluate_default); PyObject_ClearManagedDict(self); @@ -1851,7 +1857,7 @@ typealias_dealloc(PyObject *self) PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); typealiasobject *ta = typealiasobject_CAST(self); - Py_DECREF(ta->name); + Py_XDECREF(ta->name); Py_XDECREF(ta->type_params); Py_XDECREF(ta->compute_value); Py_XDECREF(ta->value); @@ -2032,6 +2038,7 @@ static int typealias_traverse(PyObject *op, visitproc visit, void *arg) { typealiasobject *self = typealiasobject_CAST(op); + Py_VISIT(self->name); Py_VISIT(self->type_params); Py_VISIT(self->compute_value); Py_VISIT(self->value); @@ -2043,6 +2050,7 @@ static int typealias_clear(PyObject *op) { typealiasobject *self = typealiasobject_CAST(op); + Py_CLEAR(self->name); Py_CLEAR(self->type_params); Py_CLEAR(self->compute_value); Py_CLEAR(self->value); From 9c1279d5de573926acd72ca70c085d016470fd07 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 14 Oct 2025 07:00:58 +0300 Subject: [PATCH 135/373] gh-124111: test macOS CI with Tk 9 (#137424) Co-authored-by: Ned Deily --- .github/workflows/reusable-macos.yml | 4 +- Lib/test/test_tkinter/test_widgets.py | 45 ++++++++++++++------ Lib/test/test_tkinter/widget_tests.py | 59 +++++++++++++++++++++------ 3 files changed, 82 insertions(+), 26 deletions(-) diff --git a/.github/workflows/reusable-macos.yml b/.github/workflows/reusable-macos.yml index 87bcd5786e7..3d310ae695b 100644 --- a/.github/workflows/reusable-macos.yml +++ b/.github/workflows/reusable-macos.yml @@ -43,9 +43,9 @@ jobs: key: ${{ github.job }}-${{ env.IMAGE_OS_VERSION }}-${{ inputs.config_hash }} - name: Install Homebrew dependencies run: | - brew install pkg-config openssl@3.0 xz gdbm tcl-tk@8 make + brew install pkg-config openssl@3.0 xz gdbm tcl-tk@9 make # Because alternate versions are not symlinked into place by default: - brew link --overwrite tcl-tk@8 + brew link --overwrite tcl-tk@9 - name: Configure CPython run: | MACOSX_DEPLOYMENT_TARGET=10.15 \ diff --git a/Lib/test/test_tkinter/test_widgets.py b/Lib/test/test_tkinter/test_widgets.py index ff3f92e9b5e..20e385ad0b6 100644 --- a/Lib/test/test_tkinter/test_widgets.py +++ b/Lib/test/test_tkinter/test_widgets.py @@ -315,7 +315,10 @@ def test_configure_direction(self): def test_configure_height(self): widget = self.create() - self.checkIntegerParam(widget, 'height', 100, -100, 0, conv=str) + if tk_version < (9, 0): + self.checkIntegerParam(widget, 'height', 100, -100, 0, conv=str) + else: + self.checkIntegerParam(widget, 'height', 0, -100, 0) def test_configure_image(self): widget = self.create() @@ -342,7 +345,10 @@ def test_configure_menu(self): def test_configure_width(self): widget = self.create() - self.checkIntegerParam(widget, 'width', 402, -402, 0, conv=str) + if tk_version < (9, 0): + self.checkIntegerParam(widget, 'width', 402, -402, 0, conv=str) + else: + self.checkIntegerParam(widget, 'width', 402, 0, 0) class OptionMenuTest(MenubuttonTest, unittest.TestCase): @@ -391,8 +397,12 @@ def test_configure_disabledbackground(self): def test_configure_insertborderwidth(self): widget = self.create(insertwidth=100) - self.checkPixelsParam(widget, 'insertborderwidth', - 0, 1.3, 2.6, 6, '10p') + if tk_version < (9, 0): + self.checkPixelsParam(widget, 'insertborderwidth', + 0, 1.3, 2.6, 6, '10p') + else: + self.checkPixelsParam(widget, 'insertborderwidth', + 0, 1.3, 3, 6, '10p') self.checkParam(widget, 'insertborderwidth', -2) # insertborderwidth is bounded above by a half of insertwidth. expected = 100 // 2 if tk_version < (9, 0) else 60 @@ -551,11 +561,22 @@ def test_configure_values(self): # XXX widget = self.create() self.assertEqual(widget['values'], '') - self.checkParam(widget, 'values', 'mon tue wed thur') + if tk_version < (9, 0): + expected = 'mon tue wed thur' + else: + expected = ('mon', 'tue', 'wed', 'thur') + self.checkParam(widget, 'values', 'mon tue wed thur', + expected=expected) self.checkParam(widget, 'values', ('mon', 'tue', 'wed', 'thur'), - expected='mon tue wed thur') + expected=expected) + + if tk_version < (9, 0): + expected = '42 3.14 {} {any string}' + else: + expected = (42, 3.14, '', 'any string') self.checkParam(widget, 'values', (42, 3.14, '', 'any string'), - expected='42 3.14 {} {any string}') + expected=expected) + self.checkParam(widget, 'values', '') def test_configure_wrap(self): @@ -649,10 +670,9 @@ def test_configure_endline(self): def test_configure_height(self): widget = self.create() self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, '3c') - self.checkParam(widget, 'height', -100, - expected=1 if tk_version < (9, 0) else -100) - self.checkParam(widget, 'height', 0, - expected=1 if tk_version < (9, 0) else 0 ) + expected = 1 if tk_version < (9, 0) else 0 + self.checkParam(widget, 'height', -100, expected=expected) + self.checkParam(widget, 'height', 0, expected=expected) def test_configure_maxundo(self): widget = self.create() @@ -670,8 +690,9 @@ def test_configure_insertunfocussed(self): def test_configure_selectborderwidth(self): widget = self.create() + value = -2 if tk_version < (9, 0) else 0 self.checkPixelsParam(widget, 'selectborderwidth', - 1.3, 2.6, -2, '10p', conv=False) + 1.3, 2.6, value, '10p', conv=False) def test_configure_spacing1(self): widget = self.create() diff --git a/Lib/test/test_tkinter/widget_tests.py b/Lib/test/test_tkinter/widget_tests.py index f518925e994..dd2d7c4da45 100644 --- a/Lib/test/test_tkinter/widget_tests.py +++ b/Lib/test/test_tkinter/widget_tests.py @@ -247,7 +247,11 @@ def test_configure_borderwidth(self): widget = self.create() self.checkPixelsParam(widget, 'borderwidth', 0, 1.3, 2.6, 6, '10p') - self.checkParam(widget, 'borderwidth', -2) + if tk_version < (9, 0): + self.checkParam(widget, 'borderwidth', -2) + else: + self.checkParam(widget, 'borderwidth', 0) + if 'bd' in self.OPTIONS: self.checkPixelsParam(widget, 'bd', 0, 1.3, 2.6, 6, '10p') self.checkParam(widget, 'bd', -2, expected=expected) @@ -260,27 +264,46 @@ def test_configure_highlightthickness(self): def test_configure_insertborderwidth(self): widget = self.create() - self.checkPixelsParam(widget, 'insertborderwidth', - 0, 1.3, 2.6, 6, '10p') - self.checkParam(widget, 'insertborderwidth', -2) + if tk_version < (9, 0): + values = (0, 1.3, 2.6, 6, -2, '10p') + value = -2 + else: + values = (0, 1, 3, 6, 13) + value = 0 + self.checkPixelsParam(widget, 'insertborderwidth', *values) + self.checkParam(widget, 'insertborderwidth', value) def test_configure_insertwidth(self): widget = self.create() - self.checkPixelsParam(widget, 'insertwidth', 1.3, 2.6, -2, '10p') + if tk_version < (9, 0): + self.checkPixelsParam(widget, 'insertwidth', 1.3, 2.6, -2, '10p') + else: + self.checkPixelsParam(widget, 'insertwidth', 1, 3, 0, 13) def test_configure_padx(self): widget = self.create() self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m') - self.checkParam(widget, 'padx', -2) + if tk_version < (9, 0): + self.checkParam(widget, 'padx', -2) + else: + self.checkParam(widget, 'padx', 0) def test_configure_pady(self): widget = self.create() self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m') - self.checkParam(widget, 'pady', -2) + if tk_version < (9, 0): + self.checkParam(widget, 'pady', -2) + else: + self.checkParam(widget, 'pady', 0) def test_configure_selectborderwidth(self): widget = self.create() - self.checkPixelsParam(widget, 'selectborderwidth', 1.3, 2.6, -2, '10p') + if tk_version < (9, 0): + values = (1.3, 2.6, -2, '10p') + else: + values = (1, 3, 0, 13) + self.checkPixelsParam(widget, 'selectborderwidth', *values) + class StandardOptionsTests(PixelOptionsTests): @@ -546,22 +569,34 @@ class IntegerSizeTests: """ Tests widgets which only accept integral width and height.""" def test_configure_height(self): widget = self.create() - self.checkIntegerParam(widget, 'height', 100, -100, 0) + if tk_version < (9, 0): + self.checkIntegerParam(widget, 'height', 100, -100, 0) + else: + self.checkIntegerParam(widget, 'height', 100, 0, 0) def test_configure_width(self): widget = self.create() - self.checkIntegerParam(widget, 'width', 402, -402, 0) + if tk_version < (9, 0): + self.checkIntegerParam(widget, 'width', 402, -402, 0) + else: + self.checkIntegerParam(widget, 'width', 402, 0, 0) class PixelSizeTests: """ Tests widgets which accept screen distances for width and height.""" def test_configure_height(self): widget = self.create() - self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '3c') + value = -100 if tk_version < (9, 0) else 0 + self.checkPixelsParam( + widget, 'height', 100, 101.2, 102.6, value, 0, '3c' + ) def test_configure_width(self): widget = self.create() - self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i') + value = -402 if tk_version < (9, 0) else 0 + self.checkPixelsParam( + widget, 'width', 402, 403.4, 404.6, value, 0, '5i' + ) def add_configure_tests(*source_classes): From 65a6d196a1ec2e20eb630a554739852c758587ef Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 14 Oct 2025 05:28:46 +0100 Subject: [PATCH 136/373] gh-132339: Update macOS installer version of OpenSSL to 3.5.4. (#140075) --- Mac/BuildScript/build-installer.py | 6 +++--- .../macOS/2025-10-04-12-29-31.gh-issue-139573.vVpHaP.rst | 1 - .../macOS/2025-10-13-23-46-12.gh-issue-132339.kAp603.rst | 1 + 3 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 Misc/NEWS.d/next/macOS/2025-10-04-12-29-31.gh-issue-139573.vVpHaP.rst create mode 100644 Misc/NEWS.d/next/macOS/2025-10-13-23-46-12.gh-issue-132339.kAp603.rst diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 4fd8d55f35a..c3d182dbd70 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -246,9 +246,9 @@ def library_recipes(): result.extend([ dict( - name="OpenSSL 3.0.18", - url="https://github.com/openssl/openssl/releases/download/openssl-3.0.18/openssl-3.0.18.tar.gz", - checksum='d80c34f5cf902dccf1f1b5df5ebb86d0392e37049e5d73df1b3abae72e4ffe8b', + name="OpenSSL 3.5.4", + url="https://github.com/openssl/openssl/releases/download/openssl-3.5.4/openssl-3.5.4.tar.gz", + checksum="967311f84955316969bdb1d8d4b983718ef42338639c621ec4c34fddef355e99", buildrecipe=build_universal_openssl, configure=None, install=None, diff --git a/Misc/NEWS.d/next/macOS/2025-10-04-12-29-31.gh-issue-139573.vVpHaP.rst b/Misc/NEWS.d/next/macOS/2025-10-04-12-29-31.gh-issue-139573.vVpHaP.rst deleted file mode 100644 index 02a3bfb41ce..00000000000 --- a/Misc/NEWS.d/next/macOS/2025-10-04-12-29-31.gh-issue-139573.vVpHaP.rst +++ /dev/null @@ -1 +0,0 @@ -Updated bundled version of OpenSSL to 3.0.18. diff --git a/Misc/NEWS.d/next/macOS/2025-10-13-23-46-12.gh-issue-132339.kAp603.rst b/Misc/NEWS.d/next/macOS/2025-10-13-23-46-12.gh-issue-132339.kAp603.rst new file mode 100644 index 00000000000..4526c97fdc0 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2025-10-13-23-46-12.gh-issue-132339.kAp603.rst @@ -0,0 +1 @@ +Update macOS installer version of OpenSSL to 3.5.4. From 4e342ea64247b9bfe08636abfc4b28413a736abd Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 14 Oct 2025 05:42:23 +0100 Subject: [PATCH 137/373] gh-124111: Update macOS installer to use Tcl/Tk 9.0.2. (#140076) --- Mac/BuildScript/build-installer.py | 6 +++--- .../macOS/2025-10-06-23-56-36.gh-issue-124111.KOlBvs.rst | 1 - .../macOS/2025-10-14-00-08-16.gh-issue-124111.7-j-DQ.rst | 1 + 3 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 Misc/NEWS.d/next/macOS/2025-10-06-23-56-36.gh-issue-124111.KOlBvs.rst create mode 100644 Misc/NEWS.d/next/macOS/2025-10-14-00-08-16.gh-issue-124111.7-j-DQ.rst diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index c3d182dbd70..e6048f94bed 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -264,10 +264,10 @@ def library_recipes(): tk_patches = ['backport_gh71383_fix.patch', 'tk868_on_10_8_10_9.patch', 'backport_gh110950_fix.patch'] else: - tcl_tk_ver='8.6.17' - tcl_checksum='a3903371efcce8a405c5c245d029e9f6850258a60fa3761c4d58995610949b31' + tcl_tk_ver='9.0.2' + tcl_checksum='e074c6a8d9ba2cddf914ba97b6677a552d7a52a3ca102924389a05ccb249b520' - tk_checksum='e4982df6f969c08bf9dd858a6891059b4a3f50dc6c87c10abadbbe2fc4838946' + tk_checksum='76fb852b2f167592fe8b41aa6549ce4e486dbf3b259a269646600e3894517c76' tk_patches = [] diff --git a/Misc/NEWS.d/next/macOS/2025-10-06-23-56-36.gh-issue-124111.KOlBvs.rst b/Misc/NEWS.d/next/macOS/2025-10-06-23-56-36.gh-issue-124111.KOlBvs.rst deleted file mode 100644 index 2787e5d37fa..00000000000 --- a/Misc/NEWS.d/next/macOS/2025-10-06-23-56-36.gh-issue-124111.KOlBvs.rst +++ /dev/null @@ -1 +0,0 @@ -Update macOS installer to use Tcl/Tk 8.6.17. diff --git a/Misc/NEWS.d/next/macOS/2025-10-14-00-08-16.gh-issue-124111.7-j-DQ.rst b/Misc/NEWS.d/next/macOS/2025-10-14-00-08-16.gh-issue-124111.7-j-DQ.rst new file mode 100644 index 00000000000..847053463fd --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2025-10-14-00-08-16.gh-issue-124111.7-j-DQ.rst @@ -0,0 +1 @@ +Update macOS installer to use Tcl/Tk 9.0.2. From 8e0bf4fe5d7835ef912758b4eeab6c94794da696 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 14 Oct 2025 05:48:27 +0100 Subject: [PATCH 138/373] gh-115119: Update macOS installer to use libmpdecimal 4.0.1. (#140077) --- Mac/BuildScript/build-installer.py | 6 +++--- .../macOS/2025-10-14-00-17-48.gh-issue-115119.470I1N.rst | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/macOS/2025-10-14-00-17-48.gh-issue-115119.470I1N.rst diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index e6048f94bed..1852397ed6f 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -378,9 +378,9 @@ def library_recipes(): install=f"make && ranlib libsqlite3.a && make install DESTDIR={shellQuote(os.path.join(WORKDIR, 'libraries'))}", ), dict( - name="libmpdec 4.0.0", - url="https://www.bytereef.org/software/mpdecimal/releases/mpdecimal-4.0.0.tar.gz", - checksum="942445c3245b22730fd41a67a7c5c231d11cb1b9936b9c0f76334fb7d0b4468c", + name="libmpdec 4.0.1", + url="https://www.bytereef.org/software/mpdecimal/releases/mpdecimal-4.0.1.tar.gz", + checksum="96d33abb4bb0070c7be0fed4246cd38416188325f820468214471938545b1ac8", configure_pre=[ "--disable-cxx", "MACHINE=universal", diff --git a/Misc/NEWS.d/next/macOS/2025-10-14-00-17-48.gh-issue-115119.470I1N.rst b/Misc/NEWS.d/next/macOS/2025-10-14-00-17-48.gh-issue-115119.470I1N.rst new file mode 100644 index 00000000000..d59da4b87b7 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2025-10-14-00-17-48.gh-issue-115119.470I1N.rst @@ -0,0 +1 @@ +Update macOS installer to use libmpdecimal 4.0.1. From f70082b4777804b69e98192121a61c2048669a16 Mon Sep 17 00:00:00 2001 From: Rafael Fontenelle Date: Tue, 14 Oct 2025 05:05:59 -0300 Subject: [PATCH 139/373] gh-70765: Remove unnecessary extra backtick from Changelog entry (#140071) --- .../next/Library/2025-10-02-17-40-10.gh-issue-70765.zVlLZn.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-10-02-17-40-10.gh-issue-70765.zVlLZn.rst b/Misc/NEWS.d/next/Library/2025-10-02-17-40-10.gh-issue-70765.zVlLZn.rst index e1a9bbe9afe..d20f881eb09 100644 --- a/Misc/NEWS.d/next/Library/2025-10-02-17-40-10.gh-issue-70765.zVlLZn.rst +++ b/Misc/NEWS.d/next/Library/2025-10-02-17-40-10.gh-issue-70765.zVlLZn.rst @@ -1,5 +1,5 @@ :mod:`http.server`: fix default handling of HTTP/0.9 requests in :class:`~http.server.BaseHTTPRequestHandler`. Previously, -:meth:`!BaseHTTPRequestHandler.parse_request`` incorrectly +:meth:`!BaseHTTPRequestHandler.parse_request` incorrectly waited for headers in the request although those are not supported in HTTP/0.9. Patch by Bénédikt Tran. From 6ca91834b8f3ecd8875d9109e5e7d75f17bfe0a3 Mon Sep 17 00:00:00 2001 From: Weilin Du <108666168+LamentXU123@users.noreply.github.com> Date: Tue, 14 Oct 2025 16:12:24 +0800 Subject: [PATCH 140/373] gh-101100: Fix sphinx warnings in `library/smtplib.rst` (#139991) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/smtplib.rst | 62 ++++++++++++++++++++++++++++++----------- Doc/tools/.nitignore | 1 - 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/Doc/library/smtplib.rst b/Doc/library/smtplib.rst index c5f8516f768..c5a3de52090 100644 --- a/Doc/library/smtplib.rst +++ b/Doc/library/smtplib.rst @@ -80,8 +80,8 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions). An :class:`SMTP_SSL` instance behaves exactly the same as instances of :class:`SMTP`. :class:`SMTP_SSL` should be used for situations where SSL is - required from the beginning of the connection and using :meth:`starttls` is - not appropriate. If *host* is not specified, the local host is used. If + required from the beginning of the connection and using :meth:`~SMTP.starttls` + is not appropriate. If *host* is not specified, the local host is used. If *port* is zero, the standard SMTP-over-SSL port (465) is used. The optional arguments *local_hostname*, *timeout* and *source_address* have the same meaning as they do in the :class:`SMTP` class. *context*, also optional, @@ -112,7 +112,7 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions). The LMTP protocol, which is very similar to ESMTP, is heavily based on the standard SMTP client. It's common to use Unix sockets for LMTP, so our - :meth:`connect` method must support that as well as a regular host:port + :meth:`~SMTP.connect` method must support that as well as a regular host:port server. The optional arguments *local_hostname* and *source_address* have the same meaning as they do in the :class:`SMTP` class. To specify a Unix socket, you must use an absolute path for *host*, starting with a '/'. @@ -147,9 +147,15 @@ A nice selection of exceptions is defined as well: .. exception:: SMTPResponseException Base class for all exceptions that include an SMTP error code. These exceptions - are generated in some instances when the SMTP server returns an error code. The - error code is stored in the :attr:`smtp_code` attribute of the error, and the - :attr:`smtp_error` attribute is set to the error message. + are generated in some instances when the SMTP server returns an error code. + + .. attribute:: smtp_code + + The error code. + + .. attribute:: smtp_error + + The error message. .. exception:: SMTPSenderRefused @@ -161,9 +167,13 @@ A nice selection of exceptions is defined as well: .. exception:: SMTPRecipientsRefused - All recipient addresses refused. The errors for each recipient are accessible - through the attribute :attr:`recipients`, which is a dictionary of exactly the - same sort as :meth:`SMTP.sendmail` returns. + All recipient addresses refused. + + .. attribute:: recipients + + A dictionary of exactly the same sort as returned + by :meth:`SMTP.sendmail` containing the errors for + each recipient. .. exception:: SMTPDataError @@ -213,7 +223,6 @@ SMTP Objects An :class:`SMTP` instance has the following methods: - .. method:: SMTP.set_debuglevel(level) Set the debug output level. A value of 1 or ``True`` for *level* results in @@ -417,7 +426,7 @@ An :class:`SMTP` instance has the following methods: .. versionchanged:: 3.4 The method now supports hostname check with - :attr:`SSLContext.check_hostname` and *Server Name Indicator* (see + :attr:`ssl.SSLContext.check_hostname` and *Server Name Indicator* (see :const:`~ssl.HAS_SNI`). .. versionchanged:: 3.5 @@ -435,7 +444,7 @@ An :class:`SMTP` instance has the following methods: ESMTP options (such as ``DSN`` commands) that should be used with all ``RCPT`` commands can be passed as *rcpt_options*. (If you need to use different ESMTP options to different recipients you have to use the low-level methods such as - :meth:`mail`, :meth:`rcpt` and :meth:`data` to send the message.) + :meth:`!mail`, :meth:`!rcpt` and :meth:`!data` to send the message.) .. note:: @@ -467,10 +476,7 @@ An :class:`SMTP` instance has the following methods: This method may raise the following exceptions: :exc:`SMTPRecipientsRefused` - All recipients were refused. Nobody got the mail. The :attr:`recipients` - attribute of the exception object is a dictionary with information about the - refused recipients (like the one returned when at least one recipient was - accepted). + All recipients were refused. Nobody got the mail. :exc:`SMTPHeloError` The server didn't reply properly to the ``HELO`` greeting. @@ -546,6 +552,30 @@ Low-level methods corresponding to the standard SMTP/ESMTP commands ``HELP``, Normally these do not need to be called directly, so they are not documented here. For details, consult the module code. +Additionally, an SMTP instance has the following attributes: + + +.. attribute:: SMTP.helo_resp + + The response to the ``HELO`` command, see :meth:`helo`. + + +.. attribute:: SMTP.ehlo_resp + + The response to the ``EHLO`` command, see :meth:`ehlo`. + + +.. attribute:: SMTP.does_esmtp + + A boolean value indicating whether the server supports ESMTP, see + :meth:`ehlo`. + + +.. attribute:: SMTP.esmtp_features + + A dictionary of the names of SMTP service extensions supported by the server, + see :meth:`ehlo`. + .. _smtp-example: diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 60a80fe2c72..29fd3cfda45 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -29,7 +29,6 @@ Doc/library/profile.rst Doc/library/pyexpat.rst Doc/library/resource.rst Doc/library/select.rst -Doc/library/smtplib.rst Doc/library/socket.rst Doc/library/ssl.rst Doc/library/stdtypes.rst From 207f977bc7ced2bb97bfc205fbba1128ab57ceca Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Tue, 14 Oct 2025 09:15:17 +0100 Subject: [PATCH 141/373] gh-138993: Dedent `credits` text (#138994) --- Lib/site.py | 6 +++--- .../Library/2025-09-16-16-46-58.gh-issue-138993.-8s8_T.rst | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-09-16-16-46-58.gh-issue-138993.-8s8_T.rst diff --git a/Lib/site.py b/Lib/site.py index f0e74eeee2f..7c6810792cf 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -449,9 +449,9 @@ def setcopyright(): """Set 'copyright' and 'credits' in builtins""" builtins.copyright = _sitebuiltins._Printer("copyright", sys.copyright) builtins.credits = _sitebuiltins._Printer("credits", """\ - Thanks to CWI, CNRI, BeOpen, Zope Corporation, the Python Software - Foundation, and a cast of thousands for supporting Python - development. See www.python.org for more information.""") +Thanks to CWI, CNRI, BeOpen, Zope Corporation, the Python Software +Foundation, and a cast of thousands for supporting Python +development. See www.python.org for more information.""") files, dirs = [], [] # Not all modules are required to have a __file__ attribute. See # PEP 420 for more details. diff --git a/Misc/NEWS.d/next/Library/2025-09-16-16-46-58.gh-issue-138993.-8s8_T.rst b/Misc/NEWS.d/next/Library/2025-09-16-16-46-58.gh-issue-138993.-8s8_T.rst new file mode 100644 index 00000000000..1be588f2ba5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-16-16-46-58.gh-issue-138993.-8s8_T.rst @@ -0,0 +1 @@ +Dedent :data:`credits` text. From 7caa591bb96e75bc74a64f6bbbff4ddfd75655dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Simon?= Date: Tue, 14 Oct 2025 10:18:17 +0200 Subject: [PATCH 142/373] gh-102247: Improve documentation of http.HTTPStatus members update (#133190) --- Doc/library/http.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/http.rst b/Doc/library/http.rst index ce3fb9f8120..b0bdfc65e45 100644 --- a/Doc/library/http.rst +++ b/Doc/library/http.rst @@ -139,7 +139,8 @@ equal to the constant name (i.e. ``http.HTTPStatus.OK`` is also available as .. versionchanged:: 3.13 Implemented RFC9110 naming for status constants. Old constant names are preserved for - backwards compatibility. + backwards compatibility: ``413 REQUEST_ENTITY_TOO_LARGE``, ``414 REQUEST_URI_TOO_LONG``, + ``416 REQUESTED_RANGE_NOT_SATISFIABLE`` and ``422 UNPROCESSABLE_ENTITY``. HTTP status category -------------------- From 362fd59dc8c6cd23a5cfd4b5a8300902565a2f58 Mon Sep 17 00:00:00 2001 From: Morteza24 Date: Tue, 14 Oct 2025 11:49:35 +0330 Subject: [PATCH 143/373] gh-138072: Fix typos and grammatical errors and improve clarity in asyncio howto document (#138895) --- .../a-conceptual-overview-of-asyncio.rst | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/Doc/howto/a-conceptual-overview-of-asyncio.rst b/Doc/howto/a-conceptual-overview-of-asyncio.rst index d68f7cc6921..af1e39480cc 100644 --- a/Doc/howto/a-conceptual-overview-of-asyncio.rst +++ b/Doc/howto/a-conceptual-overview-of-asyncio.rst @@ -9,12 +9,11 @@ model of how :mod:`asyncio` fundamentally works, helping you understand the how and why behind the recommended patterns. You might be curious about some key :mod:`!asyncio` concepts. -You'll be comfortably able to answer these questions by the end of this -article: +By the end of this article, you'll be able to comfortably answer these questions: - What's happening behind the scenes when an object is awaited? - How does :mod:`!asyncio` differentiate between a task which doesn't need - CPU-time (such as a network request or file read) as opposed to a task that + CPU time (such as a network request or file read) as opposed to a task that does (such as computing n-factorial)? - How to write an asynchronous variant of an operation, such as an async sleep or database request. @@ -35,7 +34,7 @@ A conceptual overview part 1: the high-level -------------------------------------------- In part 1, we'll cover the main, high-level building blocks of :mod:`!asyncio`: -the event loop, coroutine functions, coroutine objects, tasks and ``await``. +the event loop, coroutine functions, coroutine objects, tasks, and ``await``. ========== Event Loop @@ -56,7 +55,7 @@ Once it pauses or completes, it returns control to the event loop. The event loop will then select another job from its pool and invoke it. You can *roughly* think of the collection of jobs as a queue: jobs are added and then processed one at a time, generally (but not always) in order. -This process repeats indefinitely with the event loop cycling endlessly +This process repeats indefinitely, with the event loop cycling endlessly onwards. If there are no more jobs pending execution, the event loop is smart enough to rest and avoid needlessly wasting CPU cycles, and will come back when there's @@ -276,7 +275,7 @@ in this case, a call to resume ``plant_a_tree()``. Generally speaking, when the awaited task finishes (``dig_the_hole_task``), the original task or coroutine (``plant_a_tree()``) is added back to the event -loops to-do list to be resumed. +loop's to-do list to be resumed. This is a basic, yet reliable mental model. In practice, the control handoffs are slightly more complex, but not by much. @@ -310,7 +309,7 @@ Consider this program:: The first statement in the coroutine ``main()`` creates ``task_b`` and schedules it for execution via the event loop. Then, ``coro_a()`` is repeatedly awaited. Control never cedes to the -event loop which is why we see the output of all three ``coro_a()`` +event loop, which is why we see the output of all three ``coro_a()`` invocations before ``coro_b()``'s output: .. code-block:: none @@ -338,8 +337,8 @@ This behavior of ``await coroutine`` can trip a lot of people up! That example highlights how using only ``await coroutine`` could unintentionally hog control from other tasks and effectively stall the event loop. -:func:`asyncio.run` can help you detect such occurences via the -``debug=True`` flag which accordingly enables +:func:`asyncio.run` can help you detect such occurrences via the +``debug=True`` flag, which enables :ref:`debug mode `. Among other things, it will log any coroutines that monopolize execution for 100ms or longer. @@ -348,8 +347,8 @@ The design intentionally trades off some conceptual clarity around usage of ``await`` for improved performance. Each time a task is awaited, control needs to be passed all the way up the call stack to the event loop. -That might sound minor, but in a large program with many ``await``'s and a deep -callstack that overhead can add up to a meaningful performance drag. +That might sound minor, but in a large program with many ``await`` statements and a deep +call stack, that overhead can add up to a meaningful performance drag. ------------------------------------------------ A conceptual overview part 2: the nuts and bolts @@ -372,7 +371,7 @@ resume a coroutine. If the coroutine was paused and is now being resumed, the argument ``arg`` will be sent in as the return value of the ``yield`` statement which originally paused it. -If the coroutine is being used for the first time (as opposed to being resumed) +If the coroutine is being used for the first time (as opposed to being resumed), ``arg`` must be ``None``. .. code-block:: @@ -403,14 +402,14 @@ If the coroutine is being used for the first time (as opposed to being resumed) returned_value = e.value print(f"Coroutine main() finished and provided value: {returned_value}.") -:ref:`yield `, like usual, pauses execution and returns control +:ref:`yield `, as usual, pauses execution and returns control to the caller. In the example above, the ``yield``, on line 3, is called by ``... = await rock`` on line 11. More broadly speaking, ``await`` calls the :meth:`~object.__await__` method of the given object. ``await`` also does one more very special thing: it propagates (or "passes -along") any ``yield``\ s it receives up the call-chain. +along") any ``yield``\ s it receives up the call chain. In this case, that's back to ``... = coroutine.send(None)`` on line 16. The coroutine is resumed via the ``coroutine.send(42)`` call on line 21. @@ -462,12 +461,12 @@ computation's status and result. The term is a nod to the idea of something still to come or not yet happened, and the object is a way to keep an eye on that something. -A future has a few important attributes. One is its state which can be either -"pending", "cancelled" or "done". +A future has a few important attributes. One is its state, which can be either +"pending", "cancelled", or "done". Another is its result, which is set when the state transitions to done. Unlike a coroutine, a future does not represent the actual computation to be done; instead, it represents the status and result of that computation, kind of -like a status light (red, yellow or green) or indicator. +like a status light (red, yellow, or green) or indicator. :class:`asyncio.Task` subclasses :class:`asyncio.Future` in order to gain these various capabilities. @@ -490,8 +489,8 @@ We'll go through an example of how you could leverage a future to create your own variant of asynchronous sleep (``async_sleep``) which mimics :func:`asyncio.sleep`. -This snippet registers a few tasks with the event loop and then awaits a -coroutine wrapped in a task: ``async_sleep(3)``. +This snippet registers a few tasks with the event loop and then awaits the task +created by ``asyncio.create_task``, which wraps the ``async_sleep(3)`` coroutine. We want that task to finish only after three seconds have elapsed, but without preventing other tasks from running. @@ -540,8 +539,8 @@ will monitor how much time has elapsed and, accordingly, call # Block until the future is marked as done. await future -Below, we'll use a rather bare object, ``YieldToEventLoop()``, to ``yield`` -from ``__await__`` in order to cede control to the event loop. +Below, we use a rather bare ``YieldToEventLoop()`` object to ``yield`` +from its ``__await__`` method, ceding control to the event loop. This is effectively the same as calling ``asyncio.sleep(0)``, but this approach offers more clarity, not to mention it's somewhat cheating to use ``asyncio.sleep`` when showcasing how to implement it! @@ -552,13 +551,13 @@ The ``watcher_task``, which runs the coroutine ``_sleep_watcher(...)``, will be invoked once per full cycle of the event loop. On each resumption, it'll check the time and if not enough has elapsed, then it'll pause once again and hand control back to the event loop. -Eventually, enough time will have elapsed, and ``_sleep_watcher(...)`` will -mark the future as done, and then itself finish too by breaking out of the +Once enough time has elapsed, ``_sleep_watcher(...)`` +marks the future as done and completes by exiting its infinite ``while`` loop. Given this helper task is only invoked once per cycle of the event loop, you'd be correct to note that this asynchronous sleep will sleep *at least* three seconds, rather than exactly three seconds. -Note this is also of true of ``asyncio.sleep``. +Note this is also true of ``asyncio.sleep``. :: @@ -601,6 +600,6 @@ For reference, you could implement it without futures, like so:: else: await YieldToEventLoop() -But, that's all for now. Hopefully you're ready to more confidently dive into +But that's all for now. Hopefully you're ready to more confidently dive into some async programming or check out advanced topics in the :mod:`rest of the documentation `. From fb25d6b35c7bf9267fd21c809ea095c4ce60b763 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 14 Oct 2025 09:21:13 +0100 Subject: [PATCH 144/373] gh-97914: Reword misleading sentence on conditional expressions (#139064) Co-authored-by: Gilles Peiffer Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/reference/expressions.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 9aca25e3214..c655d6c52ec 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -1938,8 +1938,9 @@ Conditional expressions conditional_expression: `or_test` ["if" `or_test` "else" `expression`] expression: `conditional_expression` | `lambda_expr` -Conditional expressions (sometimes called a "ternary operator") have the lowest -priority of all Python operations. +A conditional expression (sometimes called a "ternary operator") is an +alternative to the if-else statement. As it is an expression, it returns a value +and can appear as a sub-expression. The expression ``x if C else y`` first evaluates the condition, *C* rather than *x*. If *C* is true, *x* is evaluated and its value is returned; otherwise, *y* is From 025b4034d6c80900aedea3b397cb730dc4910ef0 Mon Sep 17 00:00:00 2001 From: Aziz Date: Tue, 14 Oct 2025 10:24:43 +0200 Subject: [PATCH 145/373] gh-137871: Clarify cmath.nan documentation by linking to math module (#137876) Co-authored-by: Sergey B Kirpichev --- Doc/library/cmath.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/cmath.rst b/Doc/library/cmath.rst index 26518a0458f..b6d5dbee21d 100644 --- a/Doc/library/cmath.rst +++ b/Doc/library/cmath.rst @@ -338,7 +338,7 @@ Constants .. data:: nan A floating-point "not a number" (NaN) value. Equivalent to - ``float('nan')``. + ``float('nan')``. See also :data:`math.nan`. .. versionadded:: 3.6 From e6102f07557a1d9390a850ae70d7820c96422460 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Tue, 14 Oct 2025 09:30:56 +0100 Subject: [PATCH 146/373] gh-82575: Adjust `time.get_clock_info` *adjustable* attribute doc (#135920) --- Doc/library/time.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/time.rst b/Doc/library/time.rst index 3e6f5a97b49..8aff2b2dc67 100644 --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -244,8 +244,8 @@ Functions The result has the following attributes: - - *adjustable*: ``True`` if the clock can be changed automatically (e.g. by - a NTP daemon) or manually by the system administrator, ``False`` otherwise + - *adjustable*: ``True`` if the clock can be set to jump forward or backward + in time, ``False`` otherwise. Does not refer to gradual NTP rate adjustments. - *implementation*: The name of the underlying C function used to get the clock value. Refer to :ref:`time-clock-id-constants` for possible values. - *monotonic*: ``True`` if the clock cannot go backward, From 13e9c2d18de26a590f9b11b1add637200bf66dd1 Mon Sep 17 00:00:00 2001 From: Doug Hoskisson Date: Tue, 14 Oct 2025 01:34:53 -0700 Subject: [PATCH 147/373] gh-114827: clarify `threading.Event.wait` timeout behavior (#114834) Co-authored-by: Kumar Aditya --- Lib/threading.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/threading.py b/Lib/threading.py index fac88d8b20c..4ebceae7029 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -653,7 +653,8 @@ def wait(self, timeout=None): (or fractions thereof). This method returns the internal flag on exit, so it will always return - True except if a timeout is given and the operation times out. + ``True`` except if a timeout is given and the operation times out, when + it will return ``False``. """ with self._cond: From 0c17473513a180612ecdfd45baec95abb78c9c59 Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Tue, 14 Oct 2025 12:18:09 +0200 Subject: [PATCH 148/373] gh-90949: Recommend `hasattr` with Expat security methods (#139800) --- Doc/library/pyexpat.rst | 28 +++++++++++++++++++++++---- Doc/library/xml.etree.elementtree.rst | 16 +++++++-------- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/Doc/library/pyexpat.rst b/Doc/library/pyexpat.rst index 9aae5c9da74..ec25c21c300 100644 --- a/Doc/library/pyexpat.rst +++ b/Doc/library/pyexpat.rst @@ -223,10 +223,10 @@ XMLParser Objects Calling ``SetReparseDeferralEnabled(True)`` allows re-enabling reparse deferral. - Note that :meth:`SetReparseDeferralEnabled` has been backported to some - prior releases of CPython as a security fix. Check for availability of - :meth:`SetReparseDeferralEnabled` using :func:`hasattr` if used in code - running across a variety of Python versions. + :meth:`!SetReparseDeferralEnabled` + has been backported to some prior releases of CPython as a security fix. + Check for availability using :func:`hasattr` if used in code running + across a variety of Python versions. .. versionadded:: 3.13 @@ -257,6 +257,11 @@ against some common XML vulnerabilities. The corresponding :attr:`~ExpatError.lineno` and :attr:`~ExpatError.offset` should not be used as they may have no special meaning. + :meth:`!SetBillionLaughsAttackProtectionActivationThreshold` + has been backported to some prior releases of CPython as a security fix. + Check for availability using :func:`hasattr` if used in code running + across a variety of Python versions. + .. note:: Activation thresholds below 4 MiB are known to break support for DITA 1.3 @@ -288,6 +293,11 @@ against some common XML vulnerabilities. The corresponding :attr:`~ExpatError.lineno` and :attr:`~ExpatError.offset` should not be used as they may have no special meaning. + :meth:`!SetBillionLaughsAttackProtectionMaximumAmplification` + has been backported to some prior releases of CPython as a security fix. + Check for availability using :func:`hasattr` if used in code running + across a variety of Python versions. + .. note:: The maximum amplification factor is only considered if the threshold @@ -309,6 +319,11 @@ against some common XML vulnerabilities. The corresponding :attr:`~ExpatError.lineno` and :attr:`~ExpatError.offset` should not be used as they may have no special meaning. + :meth:`!SetAllocTrackerActivationThreshold` + has been backported to some prior releases of CPython as a security fix. + Check for availability using :func:`hasattr` if used in code running + across a variety of Python versions. + .. versionadded:: next .. method:: xmlparser.SetAllocTrackerMaximumAmplification(max_factor, /) @@ -334,6 +349,11 @@ against some common XML vulnerabilities. The corresponding :attr:`~ExpatError.lineno` and :attr:`~ExpatError.offset` should not be used as they may have no special meaning. + :meth:`!SetAllocTrackerMaximumAmplification` + has been backported to some prior releases of CPython as a security fix. + Check for availability using :func:`hasattr` if used in code running + across a variety of Python versions. + .. note:: The maximum amplification factor is only considered if the threshold diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst index 00075ac2a23..881708a4dd7 100644 --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -1398,10 +1398,10 @@ XMLParser Objects Disabling reparse deferral has security consequences; please see :meth:`xml.parsers.expat.xmlparser.SetReparseDeferralEnabled` for details. - Note that :meth:`flush` has been backported to some prior releases of - CPython as a security fix. Check for availability of :meth:`flush` - using :func:`hasattr` if used in code running across a variety of Python - versions. + :meth:`!flush` + has been backported to some prior releases of CPython as a security fix. + Check for availability using :func:`hasattr` if used in code running + across a variety of Python versions. .. versionadded:: 3.13 @@ -1476,10 +1476,10 @@ XMLPullParser Objects Disabling reparse deferral has security consequences; please see :meth:`xml.parsers.expat.xmlparser.SetReparseDeferralEnabled` for details. - Note that :meth:`flush` has been backported to some prior releases of - CPython as a security fix. Check for availability of :meth:`flush` - using :func:`hasattr` if used in code running across a variety of Python - versions. + :meth:`!flush` + has been backported to some prior releases of CPython as a security fix. + Check for availability using :func:`hasattr` if used in code running + across a variety of Python versions. .. versionadded:: 3.13 From 253534515c9be908ff4a326f25698e61e8ac59d8 Mon Sep 17 00:00:00 2001 From: Gil Forcada Codinachs Date: Tue, 14 Oct 2025 12:20:14 +0200 Subject: [PATCH 149/373] gh-139958: Add TOML MIME type (#139959) Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> --- Lib/mimetypes.py | 3 ++- Lib/test/test_mimetypes.py | 1 + .../Library/2025-10-11-17-41-26.gh-issue-139958.AnCakj.rst | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-11-17-41-26.gh-issue-139958.AnCakj.rst diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py index 7d0f4c1fd40..6c0efb67197 100644 --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -500,6 +500,7 @@ def _default_mime_types(): '.ps' : 'application/postscript', '.ai' : 'application/postscript', '.eps' : 'application/postscript', + '.toml' : 'application/toml', '.trig' : 'application/trig', '.m3u' : 'application/vnd.apple.mpegurl', '.m3u8' : 'application/vnd.apple.mpegurl', @@ -587,9 +588,9 @@ def _default_mime_types(): '.aiff' : 'audio/x-aiff', '.ra' : 'audio/x-pn-realaudio', '.wav' : 'audio/vnd.wave', + '.weba' : 'audio/webm', '.otf' : 'font/otf', '.ttf' : 'font/ttf', - '.weba' : 'audio/webm', '.woff' : 'font/woff', '.woff2' : 'font/woff2', '.avif' : 'image/avif', diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index c1806b1c133..a29815bf49b 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -230,6 +230,7 @@ def check_extensions(): ("application/gzip", ".gz"), ("application/ogg", ".ogx"), ("application/postscript", ".ps"), + ("application/toml", ".toml"), ("application/vnd.apple.mpegurl", ".m3u"), ("application/vnd.ms-excel", ".xls"), ("application/vnd.ms-fontobject", ".eot"), diff --git a/Misc/NEWS.d/next/Library/2025-10-11-17-41-26.gh-issue-139958.AnCakj.rst b/Misc/NEWS.d/next/Library/2025-10-11-17-41-26.gh-issue-139958.AnCakj.rst new file mode 100644 index 00000000000..f129f8beb24 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-11-17-41-26.gh-issue-139958.AnCakj.rst @@ -0,0 +1,2 @@ +The ``application/toml`` mime type is now supported by :mod:`mimetypes`. +Patch by Gil Forcada. From 59764321389697964cb212b86b02fd01dc6aab98 Mon Sep 17 00:00:00 2001 From: Wulian233 <1055917385@qq.com> Date: Tue, 14 Oct 2025 18:25:12 +0800 Subject: [PATCH 150/373] gh-137634: Modernize `calendar.HTMLCalendar` output (#137635) --- Doc/whatsnew/3.15.rst | 8 +++ Lib/calendar.py | 42 +++++++------- Lib/test/test_calendar.py | 58 +++++++++++-------- ...-08-11-14-18-32.gh-issue-137634.M7iBG6.rst | 2 + 4 files changed, 65 insertions(+), 45 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-08-11-14-18-32.gh-issue-137634.M7iBG6.rst diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index a6be2716296..5446cd1fdab 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -294,6 +294,14 @@ New modules Improved modules ================ +calendar +-------- + +* Calendar pages generated by the :class:`calendar.HTMLCalendar` class now support + dark mode and have been migrated to the HTML5 standard for improved accessibility. + (Contributed by Jiahao Li and Hugo van Kemenade in :gh:`137634`.) + + collections ----------- diff --git a/Lib/calendar.py b/Lib/calendar.py index 678c7be5aac..ed6b74b2480 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -498,30 +498,29 @@ def formatday(self, day, weekday): """ if day == 0: # day outside month - return ' ' % self.cssclass_noday + return f' ' else: - return '%d' % (self.cssclasses[weekday], day) + return f'{day}' def formatweek(self, theweek): """ Return a complete week as a table row. """ s = ''.join(self.formatday(d, wd) for (d, wd) in theweek) - return '%s' % s + return f'{s}' def formatweekday(self, day): """ Return a weekday name as a table header. """ - return '%s' % ( - self.cssclasses_weekday_head[day], day_abbr[day]) + return f'{day_abbr[day]}' def formatweekheader(self): """ Return a header for a week as a table row. """ s = ''.join(self.formatweekday(i) for i in self.iterweekdays()) - return '%s' % s + return f'{s}' def formatmonthname(self, theyear, themonth, withyear=True): """ @@ -529,11 +528,10 @@ def formatmonthname(self, theyear, themonth, withyear=True): """ _validate_month(themonth) if withyear: - s = '%s %s' % (standalone_month_name[themonth], theyear) + s = f'{standalone_month_name[themonth]} {theyear}' else: s = standalone_month_name[themonth] - return '%s' % ( - self.cssclass_month_head, s) + return f'{s}' def formatmonth(self, theyear, themonth, withyear=True): """ @@ -541,8 +539,7 @@ def formatmonth(self, theyear, themonth, withyear=True): """ v = [] a = v.append - a('' % ( - self.cssclass_month)) + a(f'
') a('\n') a(self.formatmonthname(theyear, themonth, withyear=withyear)) a('\n') @@ -562,11 +559,9 @@ def formatyear(self, theyear, width=3): v = [] a = v.append width = max(width, 1) - a('
' % - self.cssclass_year) + a(f'
') a('\n') - a('' % ( - width, self.cssclass_year_head, theyear)) + a(f'') for i in range(JANUARY, JANUARY+12, width): # months in this row months = range(i, min(i+width, 13)) @@ -587,14 +582,19 @@ def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None): encoding = 'utf-8' v = [] a = v.append - a('\n' % encoding) - a('\n') - a('\n') + a('\n') + a('\n') a('\n') - a('\n' % encoding) + a(f'\n') + a('\n') + a(f'Calendar for {theyear}\n') + a('\n') if css is not None: - a('\n' % css) - a('Calendar for %d\n' % theyear) + a(f'\n') a('\n') a('\n') a(self.formatyear(theyear, width)) diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py index c129b3e51ba..020f9d61cae 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -113,18 +113,25 @@ default_format = dict(year="year", month="month", encoding="ascii") +result_2004_css = """""" + result_2004_html = """\ - - - + + - - + +Calendar for 2004 +{css_styles} + -
%s
{theyear}
-
2004
+
+
2004
@@ -133,7 +140,7 @@
January
MonTueWedThuFriSatSun
   1234
19202122232425
262728293031 
-
+
@@ -142,7 +149,7 @@
February
MonTueWedThuFriSatSun
      1
16171819202122
23242526272829
-
+
@@ -151,7 +158,7 @@
March
MonTueWedThuFriSatSun
1234567
22232425262728
293031    
-
+
@@ -160,7 +167,7 @@
April
MonTueWedThuFriSatSun
   1234
19202122232425
2627282930  
-
+
@@ -170,7 +177,7 @@
May
MonTueWedThuFriSatSun
     12
24252627282930
31      
-
+
@@ -179,7 +186,7 @@
June
MonTueWedThuFriSatSun
 123456
21222324252627
282930    
-
+
@@ -188,7 +195,7 @@
July
MonTueWedThuFriSatSun
   1234
19202122232425
262728293031 
-
+
@@ -198,7 +205,7 @@
August
MonTueWedThuFriSatSun
      1
23242526272829
3031     
-
+
@@ -207,7 +214,7 @@
September
MonTueWedThuFriSatSun
  12345
20212223242526
27282930   
-
+
@@ -216,7 +223,7 @@
October
MonTueWedThuFriSatSun
    123
18192021222324
25262728293031
-
+
@@ -225,7 +232,7 @@
November
MonTueWedThuFriSatSun
1234567
22232425262728
2930     
-
+
@@ -385,10 +392,12 @@ def check_htmlcalendar_encoding(self, req, res): cal = calendar.HTMLCalendar() format_ = default_format.copy() format_["encoding"] = req or 'utf-8' + format_with_css = {**format_, "css_styles": result_2004_css} + formatted_html = result_2004_html.format(**format_with_css) output = cal.formatyearpage(2004, encoding=req) self.assertEqual( output, - result_2004_html.format(**format_).encode(res) + formatted_html.encode(res) ) def test_output(self): @@ -1183,7 +1192,7 @@ def test_option_type(self): output = run('--type', 'text', '2004') self.assertEqual(output, conv(result_2004_text)) output = run('--type', 'html', '2004') - self.assertStartsWith(output, b'') self.assertIn(b'Calendar for 2004', output) def test_html_output_current_year(self): @@ -1196,15 +1205,16 @@ def test_html_output_current_year(self): def test_html_output_year_encoding(self): for run in self.runners: output = run('-t', 'html', '--encoding', 'ascii', '2004') - self.assertEqual(output, result_2004_html.format(**default_format).encode('ascii')) + format_with_css = default_format.copy() + format_with_css["css_styles"] = result_2004_css + self.assertEqual(output, result_2004_html.format(**format_with_css).encode('ascii')) def test_html_output_year_css(self): self.assertFailure('-t', 'html', '-c') self.assertFailure('-t', 'html', '--css') for run in self.runners: output = run('-t', 'html', '--css', 'custom.css', '2004') - self.assertIn(b'', output) + self.assertIn(b'', output) class MiscTestCase(unittest.TestCase): @@ -1258,7 +1268,7 @@ def test_formatweek_head(self): def test_format_year(self): self.assertIn( - ('
December
MonTueWedThuFriSatSun
  12345
' % + ('
' % self.cal.cssclass_year), self.cal.formatyear(2017)) def test_format_year_head(self): diff --git a/Misc/NEWS.d/next/Library/2025-08-11-14-18-32.gh-issue-137634.M7iBG6.rst b/Misc/NEWS.d/next/Library/2025-08-11-14-18-32.gh-issue-137634.M7iBG6.rst new file mode 100644 index 00000000000..e1d7c72d7e3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-08-11-14-18-32.gh-issue-137634.M7iBG6.rst @@ -0,0 +1,2 @@ +Calendar pages generated by the :class:`calendar.HTMLCalendar` class now support +dark mode and have been migrated to the HTML5 standard for improved accessibility. From f71c96cf2d10c2fe4a84188d8eb6568563899118 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 14 Oct 2025 13:32:55 +0300 Subject: [PATCH 151/373] gh-133879: Copyedit "What's new in Python 3.15" (#140007) --- Doc/whatsnew/3.15.rst | 277 +++++++++++++++++++++++------------------- 1 file changed, 155 insertions(+), 122 deletions(-) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 5446cd1fdab..c5321ee9983 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -3,7 +3,7 @@ What's new in Python 3.15 **************************** -:Editor: TBD +:Editor: Hugo van Kemenade .. Rules for maintenance: @@ -56,8 +56,8 @@ For full details, see the :ref:`changelog `. so it's worth checking back even after reading earlier versions. -Summary --- release highlights -============================== +Summary -- Release highlights +============================= .. This section singles out the most important changes in Python 3.15. Brevity is key. @@ -65,6 +65,13 @@ Summary --- release highlights .. PEP-sized items next. +* :pep:`799`: :ref:`A dedicated profiling package for organizing Python + profiling tools ` +* :pep:`686`: :ref:`Python now uses UTF-8 as the default encoding + ` +* :pep:`782`: :ref:`A new PyBytesWriter C API to create a Python bytes object + ` +* :ref:`Improved error messages ` New features @@ -72,8 +79,8 @@ New features .. _whatsnew315-sampling-profiler: -High frequency statistical sampling profiler --------------------------------------------- +:pep:`799`: High frequency statistical sampling profiler +-------------------------------------------------------- A new statistical sampling profiler has been added to the new :mod:`!profiling` module as :mod:`!profiling.sampling`. This profiler enables low-overhead performance analysis of @@ -95,23 +102,33 @@ Key features include: * **Multiple output formats**: Generate both detailed statistics and flamegraph data * **Thread-aware profiling**: Option to profile all threads or just the main thread -Profile process 1234 for 10 seconds with default settings:: +Profile process 1234 for 10 seconds with default settings: + +.. code-block:: shell python -m profiling.sampling 1234 -Profile with custom interval and duration, save to file:: +Profile with custom interval and duration, save to file: + +.. code-block:: shell python -m profiling.sampling -i 50 -d 30 -o profile.stats 1234 -Generate collapsed stacks for flamegraph:: +Generate collapsed stacks for flamegraph: + +.. code-block:: shell python -m profiling.sampling --collapsed 1234 -Profile all threads and sort by total time:: +Profile all threads and sort by total time: + +.. code-block:: shell python -m profiling.sampling -a --sort-tottime 1234 -The profiler generates statistical estimates of where time is spent:: +The profiler generates statistical estimates of where time is spent: + +.. code-block:: text Real-time sampling stats: Mean: 100261.5Hz (9.97µs) Min: 86333.4Hz (11.58µs) Max: 118807.2Hz (8.42µs) Samples: 400001 Captured 498841 samples in 5.00 seconds @@ -166,9 +183,13 @@ analysis, highlighting functions with high CPU usage and call frequency patterns This capability is particularly valuable for debugging performance issues in production systems where traditional profiling approaches would be too intrusive. + .. seealso:: :pep:`799` for further details. + (Contributed by Pablo Galindo and László Kiss Kollár in :gh:`135953`.) +.. _whatsnew315-improved-error-messages: + Improved error messages ----------------------- @@ -211,9 +232,11 @@ Improved error messages Other language changes ====================== +.. _whatsnew315-utf8-default: + * Python now uses UTF-8_ as the default encoding, independent of the system's environment. This means that I/O operations without an explicit encoding, - e.g. ``open('flying-circus.txt')``, will use UTF-8. + for example, ``open('flying-circus.txt')``, will use UTF-8. UTF-8 is a widely-supported Unicode_ character encoding that has become a *de facto* standard for representing text, including nearly every webpage on the internet, many common file formats, programming languages, and more. @@ -227,7 +250,7 @@ Other language changes To retain the previous behaviour, Python's UTF-8 mode may be disabled with the :envvar:`PYTHONUTF8=0 ` environment variable or the - :option:`-X utf8=0 <-X>` command line option. + :option:`-X utf8=0 <-X>` command-line option. .. seealso:: :pep:`686` for further details. @@ -386,7 +409,7 @@ locale ------ * :func:`~locale.setlocale` now supports language codes with ``@``-modifiers. - ``@``-modifier are no longer silently removed in :func:`~locale.getlocale`, + ``@``-modifiers are no longer silently removed in :func:`~locale.getlocale`, but included in the language code. (Contributed by Serhiy Storchaka in :gh:`137729`.) @@ -610,71 +633,6 @@ module_name -Deprecated -========== - -CLI ---- - -* Deprecate :option:`-b` and :option:`!-bb` command line options - and schedule them to become no-op in Python 3.17. - These were primarily helpers for the Python 2 -> 3 transition. - Starting with Python 3.17, no :exc:`BytesWarning` will be raised - for these cases; use a type checker instead. - - (Contributed by Nikita Sobolev in :gh:`136355`.) - -hashlib -------- - -* In hash function constructors such as :func:`~hashlib.new` or the - direct hash-named constructors such as :func:`~hashlib.md5` and - :func:`~hashlib.sha256`, their optional initial data parameter could - also be passed a keyword argument named ``data=`` or ``string=`` in - various :mod:`hashlib` implementations. - - Support for the ``string`` keyword argument name is now deprecated and - is slated for removal in Python 3.19. Prefer passing the initial data as - a positional argument for maximum backwards compatibility. - - (Contributed by Bénédikt Tran in :gh:`134978`.) - -__version__ ------------ - -* The ``__version__`` attribute has been deprecated in these standard library - modules and will be removed in Python 3.20. - Use :py:data:`sys.version_info` instead. - - - :mod:`argparse` - - :mod:`csv` - - :mod:`!ctypes.macholib` - - :mod:`ipaddress` - - :mod:`json` - - :mod:`logging` (``__date__`` also deprecated) - - :mod:`optparse` - - :mod:`pickle` - - :mod:`platform` - - :mod:`re` - - :mod:`socketserver` - - :mod:`tabnanny` - - :mod:`tkinter.font` - - :mod:`tkinter.ttk` - - (Contributed by Hugo van Kemenade in :gh:`76007`.) - -.. Add deprecations above alphabetically, not here at the end. - -.. include:: ../deprecations/pending-removal-in-3.16.rst - -.. include:: ../deprecations/pending-removal-in-3.17.rst - -.. include:: ../deprecations/pending-removal-in-3.19.rst - -.. include:: ../deprecations/pending-removal-in-3.20.rst - -.. include:: ../deprecations/pending-removal-in-future.rst - Removed ======= @@ -712,6 +670,12 @@ importlib.resources (Contributed by Semyon Moroz in :gh:`138044`) +mimetypes +--------- + +* Add ``application/toml``. (Contributed by Gil Forcada in :gh:`139959`.) + + pathlib ------- @@ -772,7 +736,7 @@ typing * Code like ``class B2(A[T2], Protocol[T1, T2]): ...`` now correctly handles type parameters order: it is ``(T1, T2)``, not ``(T2, T1)`` - as it was incorrectly infered in runtime before. + as it was incorrectly inferred in runtime before. (Contributed by Nikita Sobolev in :gh:`137191`.) * :class:`typing.ByteString` has been removed from ``typing.__all__``. @@ -814,20 +778,70 @@ zipimport (Contributed by Jiahao Li in :gh:`133656`.) -Porting to Python 3.15 -====================== +Deprecated +========== -This section lists previously described changes and other bugfixes -that may require changes to your code. +New deprecations +---------------- +* CLI: -Build changes -============= + * Deprecate :option:`-b` and :option:`!-bb` command-line options + and schedule them to become no-op in Python 3.17. + These were primarily helpers for the Python 2 -> 3 transition. + Starting with Python 3.17, no :exc:`BytesWarning` will be raised + for these cases; use a type checker instead. -* Removed implicit fallback to the bundled copy of the ``libmpdec`` library. - Now this should be explicitly enabled with :option:`--with-system-libmpdec` - set to ``no`` or with :option:`!--without-system-libmpdec`. - (Contributed by Sergey B Kirpichev in :gh:`115119`.) + (Contributed by Nikita Sobolev in :gh:`136355`.) + +* :mod:`hashlib`: + + * In hash function constructors such as :func:`~hashlib.new` or the + direct hash-named constructors such as :func:`~hashlib.md5` and + :func:`~hashlib.sha256`, their optional initial data parameter could + also be passed a keyword argument named ``data=`` or ``string=`` in + various :mod:`hashlib` implementations. + + Support for the ``string`` keyword argument name is now deprecated and + is slated for removal in Python 3.19. Prefer passing the initial data as + a positional argument for maximum backwards compatibility. + + (Contributed by Bénédikt Tran in :gh:`134978`.) + +* ``__version__`` + + * The ``__version__`` attribute has been deprecated in these standard library + modules and will be removed in Python 3.20. + Use :py:data:`sys.version_info` instead. + + - :mod:`argparse` + - :mod:`csv` + - :mod:`!ctypes.macholib` + - :mod:`ipaddress` + - :mod:`json` + - :mod:`logging` (``__date__`` also deprecated) + - :mod:`optparse` + - :mod:`pickle` + - :mod:`platform` + - :mod:`re` + - :mod:`socketserver` + - :mod:`tabnanny` + - :mod:`tkinter.font` + - :mod:`tkinter.ttk` + + (Contributed by Hugo van Kemenade in :gh:`76007`.) + +.. Add deprecations above alphabetically, not here at the end. + +.. include:: ../deprecations/pending-removal-in-3.16.rst + +.. include:: ../deprecations/pending-removal-in-3.17.rst + +.. include:: ../deprecations/pending-removal-in-3.19.rst + +.. include:: ../deprecations/pending-removal-in-3.20.rst + +.. include:: ../deprecations/pending-removal-in-future.rst C API changes @@ -850,6 +864,8 @@ New features and :c:data:`Py_mod_abi`. (Contributed by Petr Viktorin in :gh:`137210`.) +.. _whatsnew315-pep782: + * Implement :pep:`782`, the :ref:`PyBytesWriter API `. Add functions: @@ -901,39 +917,6 @@ Porting to Python 3.15 underlying syscall, instead of raising a :exc:`SystemError`. -Deprecated C APIs ------------------ - -* For unsigned integer formats in :c:func:`PyArg_ParseTuple`, - accepting Python integers with value that is larger than the maximal value - for the C type or less than the minimal value for the corresponding - signed integer type of the same size is now deprecated. - (Contributed by Serhiy Storchaka in :gh:`132629`.) - -* :c:func:`PyBytes_FromStringAndSize(NULL, len) ` - and :c:func:`_PyBytes_Resize` are :term:`soft deprecated`, - use the :c:type:`PyBytesWriter` API instead. - (Contributed by Victor Stinner in :gh:`129813`.) - -* Deprecate :c:member:`~PyComplexObject.cval` field of the the - :c:type:`PyComplexObject` type. - Use :c:func:`PyComplex_AsCComplex` and :c:func:`PyComplex_FromCComplex` - to convert a Python complex number to/from the C :c:type:`Py_complex` - representation. - (Contributed by Sergey B Kirpichev in :gh:`128813`.) - -* Functions :c:func:`_Py_c_sum`, :c:func:`_Py_c_diff`, :c:func:`_Py_c_neg`, - :c:func:`_Py_c_prod`, :c:func:`_Py_c_quot`, :c:func:`_Py_c_pow` and - :c:func:`_Py_c_abs` are :term:`soft deprecated`. - (Contributed by Sergey B Kirpichev in :gh:`128813`.) - -* :c:member:`~PyConfig.bytes_warning` is deprecated - since 3.15 and will be removed in 3.17. - (Contributed by Nikita Sobolev in :gh:`136355`.) - - -.. Add C API deprecations above alphabetically, not here at the end. - Removed C APIs -------------- @@ -1002,3 +985,53 @@ on Python 3.13 and older. .. |pythoncapi_compat_project| replace:: |pythoncapi_compat_project_link|_ .. |pythoncapi_compat_project_link| replace:: pythoncapi-compat project .. _pythoncapi_compat_project_link: https://github.com/python/pythoncapi-compat + + +Deprecated C APIs +----------------- + +* For unsigned integer formats in :c:func:`PyArg_ParseTuple`, + accepting Python integers with value that is larger than the maximal value + for the C type or less than the minimal value for the corresponding + signed integer type of the same size is now deprecated. + (Contributed by Serhiy Storchaka in :gh:`132629`.) + +* :c:func:`PyBytes_FromStringAndSize(NULL, len) ` + and :c:func:`_PyBytes_Resize` are :term:`soft deprecated`, + use the :c:type:`PyBytesWriter` API instead. + (Contributed by Victor Stinner in :gh:`129813`.) + +* Deprecate :c:member:`~PyComplexObject.cval` field of the the + :c:type:`PyComplexObject` type. + Use :c:func:`PyComplex_AsCComplex` and :c:func:`PyComplex_FromCComplex` + to convert a Python complex number to/from the C :c:type:`Py_complex` + representation. + (Contributed by Sergey B Kirpichev in :gh:`128813`.) + +* Functions :c:func:`_Py_c_sum`, :c:func:`_Py_c_diff`, :c:func:`_Py_c_neg`, + :c:func:`_Py_c_prod`, :c:func:`_Py_c_quot`, :c:func:`_Py_c_pow` and + :c:func:`_Py_c_abs` are :term:`soft deprecated`. + (Contributed by Sergey B Kirpichev in :gh:`128813`.) + +* :c:member:`~PyConfig.bytes_warning` is deprecated + since 3.15 and will be removed in 3.17. + (Contributed by Nikita Sobolev in :gh:`136355`.) + + +.. Add C API deprecations above alphabetically, not here at the end. + + +Build changes +============= + +* Removed implicit fallback to the bundled copy of the ``libmpdec`` library. + Now this should be explicitly enabled with :option:`--with-system-libmpdec` + set to ``no`` or with :option:`!--without-system-libmpdec`. + (Contributed by Sergey B Kirpichev in :gh:`115119`.) + + +Porting to Python 3.15 +====================== + +This section lists previously described changes and other bugfixes +that may require changes to your code. From aeff92d86a3f3bd2d3a32b36ba75f84ff2c279d8 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 14 Oct 2025 13:45:53 +0300 Subject: [PATCH 152/373] Python 3.15.0a1 --- Doc/c-api/arg.rst | 2 +- Doc/c-api/bytes.rst | 2 +- Doc/c-api/complex.rst | 2 +- Doc/c-api/object.rst | 4 +- Doc/c-api/stable.rst | 8 +- Doc/c-api/sys.rst | 8 +- Doc/c-api/tuple.rst | 2 +- Doc/library/_thread.rst | 2 +- Doc/library/ast.rst | 2 +- Doc/library/calendar.rst | 4 +- Doc/library/codecs.rst | 2 +- Doc/library/collections.rst | 2 +- Doc/library/datetime.rst | 8 +- Doc/library/dbm.rst | 4 +- Doc/library/difflib.rst | 2 +- Doc/library/doctest.rst | 6 +- Doc/library/exceptions.rst | 2 +- Doc/library/fcntl.rst | 4 +- Doc/library/gzip.rst | 6 +- Doc/library/http.client.rst | 6 +- Doc/library/http.cookies.rst | 2 +- Doc/library/imaplib.rst | 2 +- Doc/library/locale.rst | 4 +- Doc/library/math.rst | 10 +- Doc/library/mmap.rst | 6 +- Doc/library/os.path.rst | 6 +- Doc/library/os.rst | 10 +- Doc/library/pty.rst | 2 +- Doc/library/pyexpat.rst | 8 +- Doc/library/resource.rst | 12 +- Doc/library/select.rst | 10 +- Doc/library/shelve.rst | 12 +- Doc/library/signal.rst | 4 +- Doc/library/socket.rst | 4 +- Doc/library/socketserver.rst | 2 +- Doc/library/sqlite3.rst | 6 +- Doc/library/ssl.rst | 22 +- Doc/library/sys.rst | 4 +- Doc/library/tarfile.rst | 6 +- Doc/library/threading.rst | 10 +- Doc/library/time.rst | 10 +- Doc/library/types.rst | 2 +- Doc/library/unittest.rst | 2 +- Doc/library/winreg.rst | 2 +- Doc/library/zipimport.rst | 2 +- Doc/library/zlib.rst | 4 +- Doc/using/cmdline.rst | 4 +- Doc/using/windows.rst | 2 +- Include/patchlevel.h | 4 +- Lib/pydoc_data/topics.py | 2372 ++++-- Misc/NEWS.d/3.15.0a1.rst | 6438 +++++++++++++++++ ...-12-04-10-00-35.gh-issue-127545.t0THjE.rst | 1 - ...-01-03-13-02-06.gh-issue-123681.gQ67nK.rst | 3 - ...-04-16-09-38-48.gh-issue-117088.EFt_5c.rst | 1 - ...-05-14-09-43-48.gh-issue-131769.H0oy5x.rst | 1 - ...-05-16-07-46-06.gh-issue-115119.ALBgS_.rst | 4 - ...-05-19-18-09-20.gh-issue-134273.ZAliyy.rst | 1 - ...-05-21-19-46-28.gh-issue-134455.vdwlrq.rst | 2 - ...-05-21-22-13-30.gh-issue-134486.yvdL6f.rst | 3 - ...-05-24-16-59-20.gh-issue-134632.i0W2hc.rst | 3 - ...-05-30-11-02-30.gh-issue-134923.gBkRg4.rst | 3 - ...-06-14-10-32-11.gh-issue-135497.ajlV4F.rst | 1 - ...-06-16-07-20-28.gh-issue-119132.fcI8s7.rst | 1 - ...-06-25-13-27-14.gh-issue-135927.iCNPQc.rst | 1 - ...-07-18-17-15-00.gh-issue-135621.9cyCNb.rst | 2 - ...-08-13-12-10-12.gh-issue-132339.3Czz5y.rst | 1 - ...-08-26-21-18-32.gh-issue-128042.5voC8H.rst | 1 - ...-08-27-09-52-45.gh-issue-138061.fMVS9w.rst | 1 - ...5-08-27-11-32-02.gh-issue-95952.KSymc7.rst | 5 - ...-09-04-12-16-31.gh-issue-138497.Y_5YXh.rst | 4 - ...-09-24-13-59-26.gh-issue-138489.1AcuZM.rst | 6 - ...-10-18-14-36-35.gh-issue-108512.fMZLfr.rst | 2 - ...-04-14-07-41-28.gh-issue-131185.ZCjMHD.rst | 2 - ...-04-17-12-37-27.gh-issue-132629.01ArwX.rst | 4 - ...-05-07-21-18-00.gh-issue-133610.asdfjs.rst | 3 - ...-05-08-12-25-47.gh-issue-133644.Yb86Rm.rst | 2 - ...-05-08-12-40-59.gh-issue-133644.FNexLJ.rst | 3 - ...-05-08-13-14-45.gh-issue-133644.J8_KZ2.rst | 2 - ...-05-13-16-06-46.gh-issue-133968.6alWst.rst | 4 - ...-05-17-14-41-21.gh-issue-134144.xVpZik.rst | 1 - ...-05-20-17-13-51.gh-issue-134009.CpCmry.rst | 1 - ...-05-29-16-56-23.gh-issue-134891.7eKO8U.rst | 2 - ...-05-30-11-33-17.gh-issue-134745.GN-zk2.rst | 3 - ...-06-02-13-19-22.gh-issue-134989.sDDyBN.rst | 2 - ...-06-05-11-06-07.gh-issue-134989.74p4ud.rst | 3 - ...-06-19-12-47-18.gh-issue-133157.1WA85f.rst | 1 - ...-06-24-11-10-01.gh-issue-133296.lIEuVJ.rst | 3 - ...-06-25-01-03-10.gh-issue-135906.UBrCWq.rst | 1 - ...-07-01-16-22-39.gh-issue-135075.angu3J.rst | 2 - ...-07-08-22-07-54.gh-issue-136006.XRU5w4.rst | 2 - ...-07-22-15-18-08.gh-issue-112068.4WvT-8.rst | 1 - ...-07-23-22-30-23.gh-issue-136759.ffB4wO.rst | 1 - ...-07-29-18-00-22.gh-issue-137210.DD4VEm.rst | 3 - ...-07-31-04-30-42.gh-issue-128813.opL-Pv.rst | 5 - ...-08-13-13-41-04.gh-issue-137573.r6uwRf.rst | 2 - ...-08-19-15-31-36.gh-issue-137956.P4TK1d.rst | 2 - ...-09-12-13-05-20.gh-issue-129813.dJZpME.rst | 16 - ...-09-14-13-09-47.gh-issue-138886.dlcTXL.rst | 1 - ...-09-14-14-44-24.gh-issue-136355.LCaYyC.rst | 2 - ...-10-07-12-51-32.gh-issue-111489.LCKKlg.rst | 2 - ...-10-10-20-59-07.gh-issue-139924.ALByCb.rst | 1 - ...-05-24-07-02-47.gh-issue-119494.x3KUMC.rst | 1 - ...-06-04-20-26-21.gh-issue-116738.q_hPYq.rst | 1 - ...-01-08-12-52-47.gh-issue-128640.9nbh9z.rst | 1 - ...-02-22-01-23-23.gh-issue-130425.x5SNQ8.rst | 2 - ...-03-14-13-08-20.gh-issue-127266._tyfBp.rst | 6 - ...4-04-16-41-00.gh-issue-133379.asdjhjdf.rst | 1 - ...-04-10-01-52-42.gh-issue-132042.fePwlj.rst | 2 - ...-04-16-12-01-13.gh-issue-127971.pMDOQ0.rst | 1 - ...-04-19-16-22-47.gh-issue-132732.jgqhlF.rst | 1 - ...-04-19-17-16-46.gh-issue-132542.7T_TY_.rst | 2 - ...-04-26-17-50-01.gh-issue-131798.XiOgw5.rst | 2 - ...-04-28-18-59-11.gh-issue-130821.B11LU1.rst | 2 - ...-04-30-14-13-01.gh-issue-132554.GqQaUp.rst | 4 - ...-05-03-13-36-01.gh-issue-131798.U4_QEJ.rst | 2 - ...-05-03-22-31-53.gh-issue-131798.fQ0ato.rst | 2 - ...-05-06-15-01-41.gh-issue-133516.RqWVf2.rst | 2 - ...-05-07-23-26-53.gh-issue-133541.bHIC55.rst | 2 - ...-05-08-13-48-02.gh-issue-132762.tKbygC.rst | 1 - ...-05-08-22-19-10.gh-issue-133711.e91wUy.rst | 2 - ...-05-09-18-11-21.gh-issue-133778.pWEV3t.rst | 2 - ...-05-10-17-12-27.gh-issue-133703.bVM-re.rst | 1 - ...-05-11-09-40-19.gh-issue-133400.zkWla8.rst | 1 - ...-05-11-13-40-42.gh-issue-133886.ryBAyo.rst | 2 - ...-05-15-11-38-16.gh-issue-133999.uBZ8uS.rst | 2 - ...-05-16-09-06-38.gh-issue-134036.st2e-B.rst | 2 - ...-05-16-17-25-52.gh-issue-134100.5-FbLK.rst | 2 - ...-05-16-20-59-12.gh-issue-134119.w8expI.rst | 2 - ...-05-17-20-44-51.gh-issue-134158.ewLNLp.rst | 1 - ...5-05-17-20-56-05.gh-issue-91153.afgtG2.rst | 1 - ...-05-18-10-50-46.gh-issue-134170.J0Hvmi.rst | 1 - ...5-05-18-14-33-23.gh-issue-69605.ZMO49F.rst | 2 - ...-05-19-15-15-58.gh-issue-131798.PCP71j.rst | 1 - ...-05-19-20-52-53.gh-issue-134268.HPKX1e.rst | 2 - ...-05-20-13-58-18.gh-issue-131798.hG8xBw.rst | 1 - ...-05-20-14-41-50.gh-issue-128066.qzzGfv.rst | 3 - ...-05-20-23-32-11.gh-issue-131798.G9ZQZw.rst | 2 - ...-05-21-13-57-26.gh-issue-131798.QwS5Bb.rst | 1 - ...-05-21-15-14-32.gh-issue-130397.aG6EON.rst | 3 - ...-05-21-18-02-56.gh-issue-127960.W3J_2X.rst | 3 - ...-05-22-14-48-19.gh-issue-134381.2BXhth.rst | 1 - ...-05-22-17-49-39.gh-issue-131798.U6ZmFm.rst | 1 - ...-05-23-14-54-07.gh-issue-134584.y-WDjf.rst | 1 - ...-05-25-19-32-15.gh-issue-131798.f5h8aI.rst | 1 - ...-05-26-15-55-50.gh-issue-133912.-xAguL.rst | 2 - ...-05-27-18-59-54.gh-issue-134679.FWPBu6.rst | 2 - ...-05-27-20-21-34.gh-issue-131798.b32zkl.rst | 1 - ...-05-27-20-29-00.gh-issue-132617.EmUfQQ.rst | 3 - ...-05-28-23-58-50.gh-issue-117852.BO9g7z.rst | 1 - ...-05-30-15-56-19.gh-issue-134908.3a7PxM.rst | 1 - ...-05-30-18-09-54.gh-issue-134889.Ic9UM-.rst | 2 - ...-05-31-10-26-46.gh-issue-134876.8mBGJI.rst | 2 - ...-05-31-19-24-54.gh-issue-134280.NDVbzY.rst | 2 - ...-06-02-13-57-40.gh-issue-116738.ycJsL8.rst | 1 - ...-06-02-20-13-37.gh-issue-131798.JQRFvR.rst | 1 - ...-06-03-21-06-22.gh-issue-133136.Usnvri.rst | 2 - ...-06-05-21-58-30.gh-issue-131798.nt5Ab7.rst | 2 - ...-06-06-01-09-44.gh-issue-131798.1SuxO9.rst | 1 - ...-06-06-02-24-42.gh-issue-135148.r-t2sC.rst | 3 - ...-06-06-19-17-22.gh-issue-131798.XoV8Eb.rst | 1 - ...-06-08-14-24-29.gh-issue-131798.qfw91T.rst | 1 - ...-06-09-23-57-37.gh-issue-130077.MHknDB.rst | 2 - ...-06-11-15-08-10.gh-issue-127319.OVGFSZ.rst | 3 - ...-06-12-00-03-34.gh-issue-116738.iBBAdo.rst | 1 - ...-06-12-11-19-52.gh-issue-135422.F6yQi6.rst | 1 - ...-06-12-18-12-42.gh-issue-135371.R_YUtR.rst | 4 - ...-06-13-16-05-24.gh-issue-135474.67nOl3.rst | 1 - ...-06-14-01-01-14.gh-issue-135496.ER0Me3.rst | 1 - ...-06-16-02-31-42.gh-issue-135543.6b0HOF.rst | 2 - ...-06-16-03-56-15.gh-issue-135551.hRTQO-.rst | 1 - ...5-06-17-08-37-45.gh-issue-82088.TgPvLg.rst | 5 - ...-06-17-12-50-48.gh-issue-135608.PnHckD.rst | 1 - ...-06-17-22-34-58.gh-issue-135607.ucsLVu.rst | 2 - ...-06-18-12-19-13.gh-issue-135379.TCvGpj.rst | 3 - ...-06-18-16-45-36.gh-issue-135106.cpl6Aq.rst | 2 - ...-06-20-14-50-44.gh-issue-134584.3CJdAI.rst | 1 - ...-06-23-18-08-32.gh-issue-135871.50C528.rst | 2 - ...-06-24-06-41-47.gh-issue-129958.EaJuS0.rst | 2 - ...-06-24-16-46-34.gh-issue-135904.78xfon.rst | 2 - ...5-06-26-15-25-51.gh-issue-78465.MbDN8X.rst | 2 - ...-06-26-18-44-34.gh-issue-136003.sln51d.rst | 3 - ...-07-02-15-18-41.gh-issue-136203.Y934sC.rst | 2 - ...-07-03-06-04-42.gh-issue-135552.CbBQof.rst | 7 - ...-07-06-14-53-19.gh-issue-109700.KVNQQi.rst | 1 - ...-07-07-12-24-00.gh-issue-136355.MTcA8j.rst | 2 - ...5-07-07-17-26-06.gh-issue-91636.GyHU72.rst | 3 - ...-07-08-23-22-08.gh-issue-132661.34ftJl.rst | 5 - ...-07-08-23-53-51.gh-issue-132661.B84iYt.rst | 1 - ...-07-09-11-15-42.gh-issue-136459.m4Udh8.rst | 3 - ...-07-09-21-27-14.gh-issue-132657.kSA8R3.rst | 1 - ...-07-10-15-53-16.gh-issue-136525.xAko0e.rst | 2 - ...-07-10-23-23-50.gh-issue-136517._NHJyv.rst | 2 - ...-07-11-12-29-09.gh-issue-107545.ipfl7U.rst | 2 - ...-07-11-13-45-48.gh-issue-136541.uZ_-Ju.rst | 3 - ...-07-12-09-59-14.gh-issue-136421.ZD1rNj.rst | 1 - ...-07-13-21-21-17.gh-issue-136599.sLhm2O.rst | 1 - ...-07-14-17-01-23.gh-issue-136616.FQjXE_.rst | 2 - ...-07-15-10-03-57.gh-issue-116738.oFttKl.rst | 1 - ...-07-18-08-43-35.gh-issue-116738.i0HWtP.rst | 2 - ...5-07-19-10-35-31.gh-issue-74185.7hPCA5.rst | 4 - ...-07-19-12-37-05.gh-issue-136801.XU_tF2.rst | 1 - ...-07-19-17-08-09.gh-issue-127598.Mx8S-y.rst | 2 - ...-07-24-02-13-59.gh-issue-132732.p77xkb.rst | 1 - ...-07-24-17-30-58.gh-issue-136870.ncx82J.rst | 1 - ...-07-25-22-31-52.gh-issue-131338.zJDCMp.rst | 2 - ...5-07-28-17-01-05.gh-issue-88886.g4XFPb.rst | 4 - ...-07-28-19-11-34.gh-issue-134291.IiB9Id.rst | 2 - ...-07-31-23-02-02.gh-issue-137291.kIxVZd.rst | 1 - ...-08-01-18-54-31.gh-issue-137288.FhE7ku.rst | 2 - ...-08-02-10-27-53.gh-issue-137308.at05p_.rst | 3 - ...-08-02-23-04-57.gh-issue-137314.wjEdzD.rst | 5 - ...-08-05-10-22-15.gh-issue-136966.J5lrE0.rst | 4 - ...5-08-05-17-22-24.gh-issue-58124.q1__53.rst | 3 - ...-08-05-20-24-12.gh-issue-120037.MB7MmI.rst | 2 - ...-08-06-15-39-54.gh-issue-137400.xIw0zs.rst | 4 - ...-08-06-16-55-44.gh-issue-133143.l7CI9v.rst | 1 - ...-08-07-09-52-19.gh-issue-137400.AK1dy-.rst | 5 - ...-08-09-04-07-05.gh-issue-132732.8BiIVJ.rst | 1 - ...5-08-09-11-38-37.gh-issue-37817.Y5Fhde.rst | 2 - ...-08-10-21-34-12.gh-issue-137576.0ZicS-.rst | 2 - ...-08-13-13-39-02.gh-issue-137433.g6Atfz.rst | 3 - ...-08-13-16-58-58.gh-issue-137716.ZcZSyi.rst | 1 - ...-08-14-14-18-29.gh-issue-137728.HdYS9R.rst | 1 - ...-08-15-15-45-26.gh-issue-137079.YEow69.rst | 1 - ...-08-17-13-36-53.gh-issue-137883.55VDCN.rst | 1 - ...-08-19-16-07-07.gh-issue-137959.EWj0RZ.rst | 2 - ...-08-19-18-52-22.gh-issue-137967.uw67Ys.rst | 1 - ...-08-20-14-17-47.gh-issue-137992.fcL3SK.rst | 2 - ...-08-21-01-46-39.gh-issue-137976.p4sb4x.rst | 1 - ...-08-21-06-31-42.gh-issue-138004.FH2Hre.rst | 1 - ...-08-22-11-39-40.gh-issue-137384.j4b_in.rst | 1 - ...5-08-27-13-11-47.gh-issue-71679.V0yFeT.rst | 3 - ...-08-27-17-51-38.gh-issue-137838.lK6T0j.rst | 2 - ...-08-28-09-29-46.gh-issue-116738.yLZJpV.rst | 2 - ...5-08-30-00-55-35.gh-issue-61206.HeFLvl.rst | 1 - ...5-08-30-17-15-05.gh-issue-69605.KjBk99.rst | 1 - ...-09-01-13-54-43.gh-issue-138349.0fGmAi.rst | 2 - ...-09-01-16-09-02.gh-issue-138318.t-WEN5.rst | 3 - ...-09-01-21-52-54.gh-issue-138302.-ez47B.rst | 3 - ...-09-02-09-10-06.gh-issue-138372.h1Xk4-.rst | 2 - ...-09-02-22-17-55.gh-issue-138401.uTRvue.rst | 2 - ...-09-03-10-16-09.gh-issue-138378.r6BQxV.rst | 2 - ...-09-03-15-35-34.gh-issue-138431.EUsrtA.rst | 1 - ...-09-03-17-00-30.gh-issue-138479.qUxgWs.rst | 2 - ...-09-05-01-19-04.gh-issue-138192.erluq5.rst | 2 - ...-09-06-13-53-33.gh-issue-105487.a43YaY.rst | 1 - ...-09-09-23-59-13.gh-issue-138716.UawDY0.rst | 2 - ...5-09-10-14-53-59.gh-issue-71810.ppf0J-.rst | 2 - ...-09-11-15-56-18.gh-issue-138794.nrOn1K.rst | 5 - ...-09-15-14-04-56.gh-issue-134466.yR4fYW.rst | 2 - ...-09-17-17-17-21.gh-issue-138558.0VbzCH.rst | 1 - ...-09-21-14-33-17.gh-issue-116738.vNaI4h.rst | 2 - ...5-09-22-15-21-49.gh-issue-74857.5XRQaA.rst | 2 - ...-09-24-17-08-42.gh-issue-133059.EXvxb7.rst | 1 - ...-09-24-17-32-52.gh-issue-139275.novrqf.rst | 2 - ...-09-30-14-57-19.gh-issue-139116.nlVf40.rst | 2 - ...5-10-01-18-21-19.gh-issue-63161.ef1S6N.rst | 5 - ...-10-06-13-15-26.gh-issue-139516.d9Pkur.rst | 1 - ...-10-08-13-52-00.gh-issue-139748.jq0yFJ.rst | 2 - ...-10-12-11-00-06.gh-issue-139988.4wi51t.rst | 2 - ...-10-12-18-54-06.gh-issue-140009.-MbFh_.rst | 1 - ...-10-13-17-56-23.gh-issue-140000.tLhn3e.rst | 4 - .../2021-09-15-13-07-25.bpo-45210.RtGk7i.rst | 2 - ...-06-10-17-02-06.gh-issue-135171.quHvts.rst | 2 - ...-07-01-23-00-58.gh-issue-136155.4siQQO.rst | 1 - ...-10-08-08-35-50.gh-issue-139742.B3fZLg.rst | 1 - ...5-10-09-12-53-47.gh-issue-96491.4YKxvy.rst | 1 - .../2017-12-30-18-21-00.bpo-28494.Dt_Wks.rst | 1 - .../2020-09-23-11-54-17.bpo-41839.kU5Ywl.rst | 2 - .../2021-03-07-16-31-36.bpo-43429.Koa0mf.rst | 5 - ...1-09-21-17-17-29.gh-issue-84683.wDSRsG.rst | 1 - .../2021-12-18-12-46-20.bpo-45959.vPlr3P.rst | 1 - .../2022-01-07-16-56-57.bpo-38735.NFfJX6.rst | 2 - ...2-07-24-20-56-32.gh-issue-69426.unccw7.rst | 3 - ...2-10-08-14-56-07.gh-issue-93334.0KUm8d.rst | 3 - ...3-02-13-20-34-52.gh-issue-78319.V1zzed.rst | 1 - ...3-02-13-21-41-34.gh-issue-86155.ppIGSC.rst | 2 - ...3-02-13-21-56-38.gh-issue-62824.CBZzX3.rst | 1 - ...3-03-13-22-51-40.gh-issue-99813.40TV02.rst | 4 - ...-07-05-14-34-10.gh-issue-105497.HU5u89.rst | 1 - ...-05-13-09-50-31.gh-issue-118981.zgOQPv.rst | 2 - ...-06-06-17-49-07.gh-issue-120170.DUxhmT.rst | 3 - ...-07-06-14-32-30.gh-issue-119186.E5B1HQ.rst | 2 - ...4-07-16-00-01-04.gh-issue-99631.GWD4fD.rst | 2 - ...-09-13-09-43-15.gh-issue-120492.Mm6CJ6.rst | 2 - ...4-09-13-09-46-47.gh-issue-91216.LuOsF4.rst | 2 - ...-09-13-09-48-25.gh-issue-124033.WNudS0.rst | 1 - ...-10-17-01-12-22.gh-issue-119109.u4hcvb.rst | 1 - ...-10-22-16-21-55.gh-issue-125843.2ttzYo.rst | 2 - ...-10-28-06-54-22.gh-issue-125028.GEY8Ws.rst | 1 - ...-11-25-10-22-08.gh-issue-126883.MAEF7g.rst | 3 - ...5-03-08-17-07-00.gh-issue-88473.qg23g8.rst | 3 - ...-03-09-03-13-41.gh-issue-130999.tBRBVB.rst | 2 - ...-03-11-05-24-14.gh-issue-130664.g0yNMm.rst | 4 - ...-03-13-20-48-58.gh-issue-123471.cM4w4f.rst | 1 - ...-03-17-21-21-06.gh-issue-131146.A5Obgv.rst | 6 - ...5-03-19-12-41-42.gh-issue-91349.8eTOCP.rst | 3 - ...-03-27-08-13-32.gh-issue-131788.0RWiFc.rst | 1 - ...-04-07-06-41-54.gh-issue-131884.ym9BJN.rst | 1 - ...5-04-07-09-53-54.gh-issue-87790.6nj3zQ.rst | 2 - ...5-04-07-10-20-16.gh-issue-87790.X2SjJe.rst | 2 - ...-04-08-07-25-10.gh-issue-107583.JGfbhq.rst | 4 - ...-04-16-21-02-57.gh-issue-132551.Psa7pL.rst | 1 - ...-04-21-00-58-04.gh-issue-127081.3DCl92.rst | 2 - ...-04-21-01-03-15.gh-issue-127081.WXRliX.rst | 2 - ...-04-21-01-05-14.gh-issue-127081.Egrpq7.rst | 2 - ...-04-22-21-00-23.gh-issue-123471.asOLA2.rst | 1 - ...-04-25-11-48-00.gh-issue-122781.ajsdns.rst | 2 - ...5-04-25-11-53-37.gh-issue-95380.7dvPe-.rst | 2 - ...-04-25-16-06-53.gh-issue-132908.wV5rja.rst | 2 - ...-04-26-15-50-12.gh-issue-133009.etBuz5.rst | 3 - ...-04-29-11-48-46.gh-issue-132876.lyTQGZ.rst | 4 - ...-04-30-19-32-18.gh-issue-132969.EagQ3G.rst | 7 - ...-05-01-10-56-44.gh-issue-132813.rKurvp.rst | 2 - ...-05-01-16-03-11.gh-issue-133017.k7RLQp.rst | 4 - ...-05-04-17-04-55.gh-issue-132493.huirKi.rst | 2 - ...-05-05-03-14-08.gh-issue-133390.AuTggn.rst | 2 - ...-05-05-10-41-41.gh-issue-133253.J5-xDD.rst | 1 - ...-05-05-18-50-00.gh-issue-133447.ajshdb.rst | 1 - ...-05-05-22-11-24.gh-issue-133439.LpmyFz.rst | 2 - ...-05-06-14-44-55.gh-issue-133517.Ca6NgW.rst | 2 - ...-05-06-22-54-37.gh-issue-133551.rfy1tJ.rst | 2 - ...5-05-07-13-31-06.gh-issue-92897.ubeqGE.rst | 2 - ...-05-07-14-36-30.gh-issue-133577.BggPk9.rst | 1 - ...-05-07-19-16-41.gh-issue-133581.kERUCJ.rst | 4 - ...-05-07-22-15-15.gh-issue-133595.c3U88r.rst | 7 - ...-05-08-13-43-19.gh-issue-133489.9eGS1Z.rst | 2 - ...-05-08-20-03-20.gh-issue-133722.1-B82a.rst | 2 - ...-05-08-20-45-35.gh-issue-133656.cxZODA.rst | 2 - ...-05-09-08-49-03.gh-issue-133701.KI8tGz.rst | 3 - ...-05-09-09-10-34.gh-issue-130328.s9h4By.rst | 2 - ...5-05-09-15-50-00.gh-issue-77057.fV8SU-.rst | 2 - ...-05-09-18-29-25.gh-issue-133684.Y1DFSt.rst | 3 - ...-05-09-19-05-24.gh-issue-133783.1voCnR.rst | 3 - ...-05-09-20-59-24.gh-issue-132641.3qTw44.rst | 1 - ...-05-10-11-04-47.gh-issue-133810.03WhnK.rst | 3 - ...-05-10-12-06-55.gh-issue-133653.Gb2aG4.rst | 7 - ...-05-10-12-07-54.gh-issue-133817.4GMtKV.rst | 2 - ...-05-10-17-42-03.gh-issue-125996.vaQp0-.rst | 1 - ...-05-11-08-48-55.gh-issue-133823.F8udQy.rst | 3 - ...-05-11-10-01-48.gh-issue-133866.g3dHP_.rst | 3 - ...-05-11-10-28-11.gh-issue-133873.H03nov.rst | 3 - ...-05-11-11-39-05.gh-issue-133875.pUar3l.rst | 2 - ...-05-11-12-56-52.gh-issue-133604.kFxhc8.rst | 1 - ...-05-12-06-52-10.gh-issue-133925.elInBY.rst | 1 - ...-05-12-20-38-57.gh-issue-133960.Aee79f.rst | 3 - ...5-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst | 3 - ...-05-13-18-54-56.gh-issue-133970.6G-Oi6.rst | 2 - ...-05-15-00-27-09.gh-issue-134004.e8k4-R.rst | 2 - ...-05-15-14-27-01.gh-issue-134062.fRbJet.rst | 3 - ...-05-16-12-40-37.gh-issue-132124.T_5Odx.rst | 6 - ...-05-16-20-10-25.gh-issue-134098.YyTkKr.rst | 2 - ...-05-17-12-40-12.gh-issue-133889.Eh-zO4.rst | 3 - ...-05-17-13-46-20.gh-issue-134097.fgkjE1.rst | 1 - ...-05-17-18-08-35.gh-issue-133890.onn9_X.rst | 2 - ...-05-17-20-23-57.gh-issue-133982.smS7au.rst | 3 - ...-05-18-07-25-15.gh-issue-134173.53oOoF.rst | 3 - ...-05-18-12-23-07.gh-issue-134087.HilZWl.rst | 3 - ...5-05-18-12-48-39.gh-issue-62184.y11l10.rst | 2 - ...-05-18-13-23-29.gh-issue-134168.hgx3Xg.rst | 2 - ...-05-18-23-46-21.gh-issue-134152.30HwbX.rst | 1 - ...-05-19-10-32-11.gh-issue-134152.INJC2j.rst | 2 - ...-05-19-15-05-24.gh-issue-134235.pz9PwV.rst | 2 - ...-05-19-15-30-00.gh-issue-132983.asdsfs.rst | 1 - ...5-05-19-17-27-21.gh-issue-80184.LOkbaw.rst | 1 - ...5-05-19-18-12-42.gh-issue-88994.7avvVu.rst | 3 - ...-05-19-20-59-06.gh-issue-134209.anhTcF.rst | 3 - ...5-05-20-11-35-08.gh-issue-72902.jzEI-E.rst | 2 - ...5-05-20-11-51-17.gh-issue-71189.0LpTB1.rst | 1 - ...5-05-20-15-13-43.gh-issue-86802.trF7TM.rst | 3 - ...-05-20-19-16-30.gh-issue-134323.ZQZGvw.rst | 1 - ...5-05-20-21-45-58.gh-issue-90871.Gkvtp6.rst | 2 - ...-05-22-13-10-32.gh-issue-114177.3TYUJ3.rst | 1 - ...-05-22-14-12-53.gh-issue-134451.M1rD-j.rst | 1 - ...-05-22-18-14-13.gh-issue-134546.fjLVzK.rst | 1 - ...-05-23-10-15-36.gh-issue-134565.zmb66C.rst | 3 - ...-05-23-20-01-52.gh-issue-134580.xnaJ70.rst | 3 - ...-05-23-23-43-39.gh-issue-134582.9POq3l.rst | 1 - ...5-05-24-03-10-36.gh-issue-80334.z21cMa.rst | 2 - ...-05-24-13-10-35.gh-issue-134210.0IuMY2.rst | 2 - ...-05-25-11-02-05.gh-issue-134657.3YFhR9.rst | 1 - ...-05-25-13-46-37.gh-issue-134635.ZlPrlX.rst | 3 - ...-05-25-23-23-05.gh-issue-134151.13Wwsb.rst | 2 - ...-05-26-10-52-27.gh-issue-134698.aJ1mZ1.rst | 2 - ...-05-26-11-01-54.gh-issue-134531.my1Fzt.rst | 2 - ...-05-26-12-31-08.gh-issue-132710.ApU3TZ.rst | 3 - ...-05-26-14-04-39.gh-issue-134696.P04xUa.rst | 5 - ...-05-26-17-06-40.gh-issue-134637.9-3zRL.rst | 1 - ...-05-26-22-18-32.gh-issue-134771.RKXpLT.rst | 2 - ...-05-27-11-13-51.gh-issue-133579.KY9M6S.rst | 8 - ...-05-27-11-18-13.gh-issue-133579.ohtgdC.rst | 3 - ...-05-27-11-24-38.gh-issue-133579.WGPUC1.rst | 7 - ...-05-28-15-53-27.gh-issue-128840.Nur2pB.rst | 1 - ...-05-28-20-49-29.gh-issue-134857.dVYXVO.rst | 3 - ...-05-29-06-53-40.gh-issue-134885.-_L22o.rst | 2 - ...-05-29-17-39-13.gh-issue-108885.MegCRA.rst | 3 - ...-05-30-09-46-21.gh-issue-134939.Pu3nnm.rst | 1 - ...-05-30-13-07-29.gh-issue-134718.9Qvhxn.rst | 2 - ...-05-30-18-13-48.gh-issue-134718.5FEspx.rst | 1 - ...-05-31-12-08-12.gh-issue-134970.lgSaxq.rst | 3 - ...-05-31-15-49-46.gh-issue-134978.mXXuvW.rst | 7 - ...-06-01-11-14-00.gh-issue-134953.ashdfs.rst | 2 - ...-06-01-14-18-48.gh-issue-135004.cq3-fp.rst | 3 - ...-06-02-14-28-30.gh-issue-130662.EIgIR8.rst | 3 - ...-06-02-14-36-28.gh-issue-130662.Gpr2GB.rst | 3 - ...-06-03-12-59-17.gh-issue-135069.xop30V.rst | 3 - ...-06-06-17-34-18.gh-issue-133934.yT1r68.rst | 1 - ...-06-08-01-10-34.gh-issue-135241.5j18IW.rst | 3 - ...-06-08-10-22-22.gh-issue-135244.Y2SOTJ.rst | 4 - ...-06-08-11-11-07.gh-issue-135234.wJCdh0.rst | 3 - ...-06-08-14-50-34.gh-issue-135276.ZLUhV1.rst | 6 - ...-06-09-10-16-55.gh-issue-121914.G6Avkq.rst | 3 - ...-06-10-00-42-30.gh-issue-135321.UHh9jT.rst | 1 - ...-06-10-10-22-18.gh-issue-130870.JipqbO.rst | 2 - ...-06-10-16-11-00.gh-issue-133967.P0c24q.rst | 1 - ...-06-10-21-00-48.gh-issue-126631.eITVJd.rst | 2 - ...-06-10-21-42-04.gh-issue-135335.WnUqb_.rst | 2 - ...-06-11-15-08-02.gh-issue-135336.6Gq6MI.rst | 1 - ...-06-12-10-45-02.gh-issue-135368.OjWVHL.rst | 2 - ...-06-12-18-15-31.gh-issue-135429.mch75_.rst | 1 - ...-06-14-12-06-55.gh-issue-135487.KdVFff.rst | 2 - ...-06-14-14-19-13.gh-issue-135497.1pzwdA.rst | 1 - ...5-06-15-03-03-22.gh-issue-65697.COdwZd.rst | 1 - ...-06-16-12-37-02.gh-issue-135444.An2eeA.rst | 2 - ...-06-16-15-00-13.gh-issue-135386.lNrxLc.rst | 2 - ...-06-16-15-03-03.gh-issue-135561.mJCN8D.rst | 2 - ...-06-17-22-44-19.gh-issue-119180.Ogv8Nj.rst | 2 - ...-06-17-23-13-56.gh-issue-135557.Bfcy4v.rst | 2 - ...-06-18-11-43-17.gh-issue-135646.r7ekEn.rst | 1 - ...-06-18-13-58-13.gh-issue-135645.109nff.rst | 2 - ...-06-18-19-25-32.gh-issue-123471.lx1Xbt.rst | 1 - ...-06-20-16-28-47.gh-issue-135759.jne0Zi.rst | 4 - ...5-06-20-17-06-59.gh-issue-90117.GYWVrn.rst | 1 - ...-06-22-02-16-17.gh-issue-135640.FXyFL6.rst | 4 - ...-06-22-16-23-44.gh-issue-135815.0DandH.rst | 2 - ...-06-22-22-03-06.gh-issue-135823.iDBg97.rst | 3 - ...-06-23-10-19-11.gh-issue-135855.-J0AGF.rst | 3 - ...-06-23-11-04-25.gh-issue-135836.-C-c4v.rst | 1 - ...-06-23-13-02-08.gh-issue-134531.yUmj07.rst | 3 - ...-06-24-10-23-37.gh-issue-135853.6xDNOG.rst | 2 - ...-06-24-10-52-35.gh-issue-135836.s37351.rst | 3 - ...-06-24-13-30-47.gh-issue-135853.7ejTvK.rst | 2 - ...-06-24-14-43-24.gh-issue-135878.Db4roX.rst | 3 - ...5-06-26-11-52-40.gh-issue-53203.TMigBr.rst | 2 - ...-06-26-17-19-36.gh-issue-105456.eR9oHB.rst | 2 - ...-06-26-17-28-49.gh-issue-135995.pPrDCt.rst | 1 - ...5-06-27-09-26-04.gh-issue-87135.33z0UW.rst | 3 - ...-06-27-13-34-28.gh-issue-136028.RY727g.rst | 3 - ...-06-28-11-32-57.gh-issue-134759.AjjKcG.rst | 2 - ...5-06-29-15-22-13.gh-issue-90733.NiquaA.rst | 2 - ...5-06-30-11-12-24.gh-issue-85702.0Lrbwu.rst | 3 - ...-07-01-14-44-03.gh-issue-136097.bI1n14.rst | 2 - ...-07-02-10-48-21.gh-issue-136193.xfvras.rst | 2 - ...-07-02-18-41-45.gh-issue-133982.7qqAn6.rst | 1 - ...-07-04-12-53-02.gh-issue-136156.OYlXoz.rst | 3 - ...-07-04-23-45-00.gh-issue-136306.O1YLIU.rst | 1 - ...-07-05-06-56-16.gh-issue-136316.3zj_Do.rst | 2 - ...-07-05-06-59-46.gh-issue-136047.qWvycf.rst | 2 - ...-07-05-09-45-04.gh-issue-136286.N67Amr.rst | 2 - ...-07-06-10-18-48.gh-issue-136021.f-FJYT.rst | 3 - ...-07-06-18-38-10.gh-issue-135953.Z29DCz.rst | 9 - ...5-07-07-16-46-55.gh-issue-72327.wLvRuj.rst | 2 - ...-07-07-22-12-32.gh-issue-136380.1b_nXl.rst | 3 - ...-07-08-20-58-01.gh-issue-136434.uuJsjS.rst | 2 - ...-07-09-20-29-30.gh-issue-136476.HyLLzh.rst | 2 - ...-07-10-00-47-37.gh-issue-136470.KlUEUG.rst | 2 - ...5-07-10-10-18-19.gh-issue-52876.9Vjrd8.rst | 3 - ...-07-10-21-02-43.gh-issue-136507.pnEuGS.rst | 1 - ...-07-11-03-39-15.gh-issue-136523.s7caKL.rst | 1 - ...5-07-11-08-15-17.gh-issue-83336.ptpmq7.rst | 1 - ...-07-11-10-23-44.gh-issue-136492.BVi5h0.rst | 1 - ...-07-11-23-04-39.gh-issue-136549.oAi8u4.rst | 1 - ...-07-12-14-15-47.gh-issue-136571.muHmBv.rst | 2 - ...-07-12-18-05-37.gh-issue-136591.ujXmSN.rst | 3 - ...-07-13-11-20-05.gh-issue-136134.xhh0Kq.rst | 3 - ...-07-13-13-31-22.gh-issue-136134.mh6VjS.rst | 5 - ...-07-15-16-37-34.gh-issue-136669.Yexwah.rst | 1 - ...5-07-16-09-45-58.gh-issue-53144.mrKwMW.rst | 1 - ...-07-17-16-12-23.gh-issue-136234.VmTxtj.rst | 2 - ...-07-19-11-53-19.gh-issue-135427.iJM_X2.rst | 4 - ...-07-19-15-40-47.gh-issue-131724.LS59nA.rst | 4 - ...-07-19-16-20-54.gh-issue-130645.O-dYcN.rst | 1 - ...-07-20-10-21-49.gh-issue-136787._0Rbp_.rst | 4 - ...-07-20-16-02-00.gh-issue-136874.cLC3o1.rst | 1 - ...-07-20-16-56-55.gh-issue-135228.n_XIao.rst | 4 - ...5-07-21-01-16-32.gh-issue-83424.Y3tEV4.rst | 2 - ...-07-21-11-56-47.gh-issue-136912.zWosAL.rst | 3 - ...-07-21-15-40-00.gh-issue-136914.-GNG-d.rst | 2 - ...-07-21-16-10-24.gh-issue-124621.wyoWc1.rst | 1 - ...-07-21-16-13-20.gh-issue-136929.obKZ2S.rst | 5 - ...-07-21-20-00-42.gh-issue-121237.DyxNqo.rst | 3 - ...-07-21-22-35-50.gh-issue-136170.QUlc78.rst | 3 - ...-07-23-00-35-29.gh-issue-130577.c7EITy.rst | 3 - ...-07-23-11-59-48.gh-issue-136980.BIJzkB.rst | 1 - ...-07-24-00-38-07.gh-issue-137059.fr64oW.rst | 3 - ...-07-25-09-21-56.gh-issue-130522.Crwq68.rst | 2 - ...-07-27-17-03-17.gh-issue-133951.7kwt78.rst | 2 - ...-07-28-20-48-32.gh-issue-137185.fgI7-B.rst | 2 - ...5-07-28-23-11-29.gh-issue-81325.jMJFBe.rst | 2 - ...-07-29-05-12-50.gh-issue-137197.bMK3sO.rst | 2 - ...-07-29-21-18-31.gh-issue-137226.B_4lpu.rst | 3 - ...-07-30-11-12-22.gh-issue-124503.d4hc7b.rst | 1 - ...-07-30-17-42-36.gh-issue-137239.qSpj32.rst | 1 - ...-07-30-18-07-33.gh-issue-137257.XBtzf2.rst | 1 - ...-07-31-10-31-56.gh-issue-137282.GOCwIC.rst | 1 - ...-07-31-16-43-16.gh-issue-137191.FIogE8.rst | 4 - ...-08-01-15-07-59.gh-issue-137273.4V8Xmv.rst | 1 - ...-08-01-23-11-25.gh-issue-137017.0yGcNc.rst | 3 - ...5-08-01-23-52-49.gh-issue-75989.5aYXNJ.rst | 3 - ...-08-03-00-36-57.gh-issue-115766.nJCFkW.rst | 1 - ...-08-03-13-16-39.gh-issue-137044.0hPVL_.rst | 4 - ...-08-06-16-13-47.gh-issue-137466.Whv0-A.rst | 3 - ...-08-06-16-54-22.gh-issue-137481.eSTkK0.rst | 2 - ...-08-06-23-16-42.gh-issue-137477.bk6BDV.rst | 2 - ...-08-07-12-32-23.gh-issue-137044.abNoIy.rst | 4 - ...-08-07-15-07-44.gh-issue-137512.j2or5h.rst | 4 - ...-08-07-17-18-57.gh-issue-137490.s89ieZ.rst | 2 - ...-08-08-15-00-38.gh-issue-137426.lW-Rk2.rst | 3 - ...5-08-08-21-20-14.gh-issue-92936.rOgG1S.rst | 2 - ...-08-09-08-53-32.gh-issue-137583.s6OZud.rst | 4 - ...-08-11-05-05-08.gh-issue-137630.9lmqyc.rst | 2 - ...-08-11-14-18-32.gh-issue-137634.M7iBG6.rst | 2 - ...5-08-13-10-50-22.gh-issue-73487.DUHbBq.rst | 3 - ...-08-14-00-00-12.gh-issue-137729.i9NSKP.rst | 3 - ...-08-14-10-27-07.gh-issue-125854.vDzFcZ.rst | 1 - ...-08-16-09-02-11.gh-issue-137754.mCev1Y.rst | 2 - ...-08-16-16-04-15.gh-issue-137317.Dl13B5.rst | 3 - ...5-08-16-18-11-41.gh-issue-90548.q3aJUK.rst | 2 - ...-08-17-10-22-31.gh-issue-132947.XR4MJ8.rst | 6 - ...-08-18-07-10-55.gh-issue-137840.9b7AnG.rst | 3 - ...-08-18-16-02-51.gh-issue-134869.GnAjnU.rst | 1 - ...-08-19-00-12-57.gh-issue-137884.4faCA_.rst | 2 - ...5-08-22-09-53-45.gh-issue-86819.ECxvwx.rst | 1 - ...-08-22-12-48-14.gh-issue-138044.lEQULC.rst | 2 - ...-08-24-02-04-32.gh-issue-138092.V4-wTO.rst | 2 - ...-08-25-16-22-32.gh-issue-138122.eMNDZ1.rst | 2 - ...-08-25-18-06-04.gh-issue-138133.Zh9rGo.rst | 1 - ...-08-25-22-38-03.gh-issue-134716.kyYKeX.rst | 2 - ...-08-27-17-05-36.gh-issue-138010.ZZJmPL.rst | 4 - ...-08-28-13-20-09.gh-issue-138204.8oLOud.rst | 2 - ...5-08-29-12-05-33.gh-issue-78502.VpIMxg.rst | 2 - ...-08-29-12-56-55.gh-issue-138239.uthZFI.rst | 2 - ...5-08-30-10-04-28.gh-issue-60462.yh_vDc.rst | 1 - ...-08-30-10-58-15.gh-issue-138253.9Ehj-N.rst | 3 - ...-08-30-17-58-04.gh-issue-138252.CDiEby.rst | 4 - ...-08-31-09-06-49.gh-issue-138008.heOvsU.rst | 1 - ...-08-31-12-34-02.gh-issue-138205.iHXb1z.rst | 2 - ...5-08-31-22-10-22.gh-issue-57911.N_Ixtv.rst | 2 - ...-09-02-10-23-09.gh-issue-116946.U6RpwK.rst | 2 - ...-09-02-10-27-21.gh-issue-116946.VxXNGD.rst | 2 - ...-09-03-09-03-11.gh-issue-132657.cbAIDh.rst | 1 - ...-09-03-15-20-10.gh-issue-138432.RMc7UX.rst | 6 - ...-09-04-15-18-11.gh-issue-111788.tuTEM5.rst | 3 - ...5-09-05-05-53-43.gh-issue-99948.KMSlG6.rst | 1 - ...-09-05-07-50-18.gh-issue-138515.E3M-pu.rst | 1 - ...5-09-05-15-35-59.gh-issue-88375.dC491a.rst | 4 - ...-09-05-21-10-24.gh-issue-137706.0EztiJ.rst | 1 - ...-09-06-11-26-21.gh-issue-138514.66ltOb.rst | 2 - ...-09-06-14-47-23.gh-issue-116946.hj_u1t.rst | 5 - ...-09-06-14-53-19.gh-issue-116946.c-npxd.rst | 2 - ...-09-06-14-54-01.gh-issue-116946.hzQEWI.rst | 3 - ...-09-06-14-56-40.gh-issue-116946.GGIeyO.rst | 2 - ...-09-06-20-09-32.gh-issue-138535.mlntEe.rst | 2 - ...5-09-08-17-32-02.gh-issue-76007.peEgcr.rst | 2 - ...-09-09-10-48-26.gh-issue-138706.xB--LX.rst | 1 - ...-09-09-17-57-49.gh-issue-138720.hAtsm-.rst | 4 - ...-09-10-10-02-59.gh-issue-128636.ldRKGZ.rst | 2 - ...-09-10-10-11-59.gh-issue-138712.avrPG5.rst | 1 - ...-09-10-13-32-25.gh-issue-138682.iExqx1.rst | 1 - ...-09-11-11-09-28.gh-issue-138779.TNZnLr.rst | 3 - ...-09-12-01-01-05.gh-issue-138804.46ZukT.rst | 3 - ...-09-15-08-57-39.gh-issue-138899.Uh6fvY.rst | 3 - ...-09-15-13-09-19.gh-issue-137226.HH3_ik.rst | 2 - ...-09-15-19-29-12.gh-issue-130567.shDEnT.rst | 2 - ...-09-16-15-56-29.gh-issue-118803.aOPtmL.rst | 3 - ...-09-16-16-46-58.gh-issue-138993.-8s8_T.rst | 1 - ...-09-16-19-05-29.gh-issue-138998.URl0Y_.rst | 1 - ...-09-17-08-32-43.gh-issue-138813.LHkHjX.rst | 1 - ...-09-17-12-07-21.gh-issue-139001.O6tseN.rst | 2 - ...-09-17-19-08-34.gh-issue-139065.Hu8fM5.rst | 2 - ...-09-17-21-52-30.gh-issue-139090.W7vbhF.rst | 1 - ...-09-17-21-54-53.gh-issue-139076.2eX9lG.rst | 3 - ...-09-18-05-32-18.gh-issue-135729.8AmMza.rst | 2 - ...-09-18-14-21-57.gh-issue-118803.2JPbto.rst | 15 - ...-09-19-07-41-52.gh-issue-126016.Uz9W6h.rst | 2 - ...-09-19-09-36-42.gh-issue-112729.mmty0_.rst | 2 - ...-09-20-17-50-31.gh-issue-138860.Y9JXap.rst | 1 - ...-09-21-15-58-57.gh-issue-139210.HGbMvz.rst | 1 - ...5-09-22-11-19-05.gh-issue-95953.7oLoag.rst | 2 - ...5-09-22-11-30-45.gh-issue-67795.fROoZt.rst | 3 - ...5-09-22-14-40-11.gh-issue-90949.UM35nb.rst | 5 - ...-09-24-14-17-34.gh-issue-139289.Vmk25k.rst | 1 - ...-09-25-07-33-43.gh-issue-139312.ygE8AC.rst | 1 - ...5-09-26-18-04-28.gh-issue-90949.YHjSzX.rst | 7 - ...-09-27-08-26-31.gh-issue-139374.hfh-dl.rst | 1 - ...-09-28-16-34-11.gh-issue-139391.nRFnmx.rst | 3 - ...-09-29-14-15-20.gh-issue-139184.dNl9O4.rst | 1 - ...-10-01-20-30-03.gh-issue-135953.NAofJl.rst | 1 - ...-10-02-15-45-08.gh-issue-139322.rouPGj.rst | 2 - ...5-10-02-17-40-10.gh-issue-70765.zVlLZn.rst | 5 - ...-10-08-00-06-30.gh-issue-139736.baPeBd.rst | 2 - ...-10-09-03-06-19.gh-issue-139809.lzHJNu.rst | 1 - ...-10-09-13-48-28.gh-issue-139783.__NUgo.rst | 2 - ...-10-09-21-37-20.gh-issue-139845.dzx5UP.rst | 1 - ...-10-10-11-22-50.gh-issue-139894.ECAXqj.rst | 1 - ...-10-11-10-02-56.gh-issue-139905.UyJIR_.rst | 3 - ...-10-11-14-37-42.gh-issue-139823.uGF4oh.rst | 2 - ...-10-11-17-41-26.gh-issue-139958.AnCakj.rst | 2 - ...-10-11-20-03-13.gh-issue-139482.du2Stg.rst | 3 - ...-01-14-11-19-07.gh-issue-128840.M1doZW.rst | 2 - ...-05-07-22-49-27.gh-issue-133623.fgWkBm.rst | 1 - ...-05-09-20-22-54.gh-issue-133767.kN2i3Q.rst | 2 - ...-06-02-11-32-23.gh-issue-135034.RLGjbp.rst | 6 - ...-06-09-20-38-25.gh-issue-118350.KgWCcP.rst | 2 - ...-06-13-15-55-22.gh-issue-135462.KBeJpc.rst | 4 - ...-06-18-13-28-08.gh-issue-102555.nADrzJ.rst | 3 - ...-06-18-13-34-55.gh-issue-135661.NZlpWf.rst | 5 - ...-06-25-14-13-39.gh-issue-135661.idjQ0B.rst | 20 - ...-06-27-21-23-19.gh-issue-136053.QZxcee.rst | 1 - ...-09-24-13-39-56.gh-issue-139283.jODz_q.rst | 4 - ...-09-29-00-01-28.gh-issue-139400.X2T-jO.rst | 4 - ...-10-07-19-31-34.gh-issue-139700.vNHU1O.rst | 3 - ...-05-08-15-06-01.gh-issue-133639.50-kbV.rst | 2 - ...-05-09-04-11-06.gh-issue-133682.-_lwo3.rst | 1 - ...-05-09-14-54-48.gh-issue-133744.LCquu0.rst | 3 - ...-05-23-09-19-52.gh-issue-134567.hwEIMb.rst | 2 - ...-06-04-13-07-44.gh-issue-135120.NapnZT.rst | 1 - ...-06-11-16-52-49.gh-issue-135401.ccMXmL.rst | 1 - ...-06-14-13-20-17.gh-issue-135489.Uh0yVO.rst | 1 - ...-06-17-08-48-08.gh-issue-132815.CY1Esu.rst | 1 - ...-06-19-15-29-38.gh-issue-135494.FVl9a0.rst | 2 - ...-06-26-15-15-35.gh-issue-135966.EBpF8Y.rst | 1 - ...-09-21-16-00-30.gh-issue-138313.lBx2en.rst | 2 - ...-09-22-15-40-09.gh-issue-139208.Tc13dl.rst | 2 - ...-05-19-14-57-46.gh-issue-134215.sbdDK6.rst | 1 - ...-06-11-12-14-06.gh-issue-135379.25ttXq.rst | 4 - ...-06-26-15-58-13.gh-issue-135968.C4v_-W.rst | 1 - ...-07-05-15-10-42.gh-issue-136251.GRM6o8.rst | 1 - ...-07-30-10-28-35.gh-issue-137243.NkdUqH.rst | 2 - ...-07-30-11-15-47.gh-issue-137248.8IxwY3.rst | 2 - ...-08-01-13-27-43.gh-issue-137025.ubuhQC.rst | 3 - ...-08-06-11-54-55.gh-issue-137484.8iFAQs.rst | 2 - ...-08-21-14-04-50.gh-issue-137873.qxffLt.rst | 3 - ...-08-27-11-14-53.gh-issue-138171.Suz8ob.rst | 3 - ...-08-28-06-22-26.gh-issue-132006.eZQmc6.rst | 2 - ...-09-25-10-31-02.gh-issue-139330.5WWkY0.rst | 3 - ...-03-31-15-37-57.gh-issue-131942.jip_aL.rst | 1 - ...-05-07-08-19-15.gh-issue-133537.yzf963.rst | 1 - ...-05-07-09-02-19.gh-issue-133562.lqqNW1.rst | 1 - ...-05-07-11-25-29.gh-issue-133568.oYV0d8.rst | 1 - ...-05-07-11-45-30.gh-issue-133572.Xc2zxH.rst | 1 - ...-05-07-13-04-22.gh-issue-133580.jBMujJ.rst | 1 - ...-05-08-19-07-26.gh-issue-133626.yFTKYK.rst | 2 - ...-05-13-13-25-27.gh-issue-133779.-YcTBz.rst | 6 - ...5-05-19-03-02-04.gh-issue-76023.vHOf6M.rst | 1 - ...-05-20-21-43-20.gh-issue-130727.-69t4D.rst | 2 - ...-06-03-18-26-54.gh-issue-135099.Q9usKm.rst | 2 - ...-07-27-02-16-53.gh-issue-137134.W0WpDF.rst | 1 - ...-07-27-14-25-11.gh-issue-137136.xNthFT.rst | 2 - ...-09-03-01-07-44.gh-issue-138314.IeWQ2i.rst | 1 - ...-09-15-15-34-29.gh-issue-138896.lkiF_7.rst | 1 - ...-10-04-12-18-45.gh-issue-139573.EO9kVB.rst | 1 - ...-10-08-22-54-38.gh-issue-139810.LAaemi.rst | 2 - ...-07-27-02-17-40.gh-issue-137134.pjgITs.rst | 1 - ...-08-06-06-29-12.gh-issue-137450.JZypb7.rst | 3 - ...-10-13-23-46-12.gh-issue-132339.kAp603.rst | 1 - ...-10-14-00-08-16.gh-issue-124111.7-j-DQ.rst | 1 - ...-10-14-00-17-48.gh-issue-115119.470I1N.rst | 1 - README.rst | 2 +- 669 files changed, 8387 insertions(+), 2073 deletions(-) create mode 100644 Misc/NEWS.d/3.15.0a1.rst delete mode 100644 Misc/NEWS.d/next/Build/2024-12-04-10-00-35.gh-issue-127545.t0THjE.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-01-03-13-02-06.gh-issue-123681.gQ67nK.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-04-16-09-38-48.gh-issue-117088.EFt_5c.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-05-14-09-43-48.gh-issue-131769.H0oy5x.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-05-16-07-46-06.gh-issue-115119.ALBgS_.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-05-19-18-09-20.gh-issue-134273.ZAliyy.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-05-21-19-46-28.gh-issue-134455.vdwlrq.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-05-21-22-13-30.gh-issue-134486.yvdL6f.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-05-24-16-59-20.gh-issue-134632.i0W2hc.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-05-30-11-02-30.gh-issue-134923.gBkRg4.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-06-14-10-32-11.gh-issue-135497.ajlV4F.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-06-16-07-20-28.gh-issue-119132.fcI8s7.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-06-25-13-27-14.gh-issue-135927.iCNPQc.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-07-18-17-15-00.gh-issue-135621.9cyCNb.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-08-13-12-10-12.gh-issue-132339.3Czz5y.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-08-26-21-18-32.gh-issue-128042.5voC8H.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-08-27-09-52-45.gh-issue-138061.fMVS9w.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-08-27-11-32-02.gh-issue-95952.KSymc7.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-09-04-12-16-31.gh-issue-138497.Y_5YXh.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-09-24-13-59-26.gh-issue-138489.1AcuZM.rst delete mode 100644 Misc/NEWS.d/next/C_API/2023-10-18-14-36-35.gh-issue-108512.fMZLfr.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-04-14-07-41-28.gh-issue-131185.ZCjMHD.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-05-07-21-18-00.gh-issue-133610.asdfjs.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-05-08-12-25-47.gh-issue-133644.Yb86Rm.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-05-08-12-40-59.gh-issue-133644.FNexLJ.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-05-08-13-14-45.gh-issue-133644.J8_KZ2.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-05-13-16-06-46.gh-issue-133968.6alWst.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-05-17-14-41-21.gh-issue-134144.xVpZik.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-05-20-17-13-51.gh-issue-134009.CpCmry.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-05-29-16-56-23.gh-issue-134891.7eKO8U.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-05-30-11-33-17.gh-issue-134745.GN-zk2.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-06-02-13-19-22.gh-issue-134989.sDDyBN.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-06-05-11-06-07.gh-issue-134989.74p4ud.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-06-19-12-47-18.gh-issue-133157.1WA85f.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-06-24-11-10-01.gh-issue-133296.lIEuVJ.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-06-25-01-03-10.gh-issue-135906.UBrCWq.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-07-01-16-22-39.gh-issue-135075.angu3J.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-07-08-22-07-54.gh-issue-136006.XRU5w4.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-07-22-15-18-08.gh-issue-112068.4WvT-8.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-07-23-22-30-23.gh-issue-136759.ffB4wO.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-07-29-18-00-22.gh-issue-137210.DD4VEm.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-07-31-04-30-42.gh-issue-128813.opL-Pv.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-08-13-13-41-04.gh-issue-137573.r6uwRf.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-08-19-15-31-36.gh-issue-137956.P4TK1d.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-09-12-13-05-20.gh-issue-129813.dJZpME.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-09-14-13-09-47.gh-issue-138886.dlcTXL.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-09-14-14-44-24.gh-issue-136355.LCaYyC.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-10-07-12-51-32.gh-issue-111489.LCKKlg.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-10-10-20-59-07.gh-issue-139924.ALByCb.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-05-24-07-02-47.gh-issue-119494.x3KUMC.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-06-04-20-26-21.gh-issue-116738.q_hPYq.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-08-12-52-47.gh-issue-128640.9nbh9z.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-02-22-01-23-23.gh-issue-130425.x5SNQ8.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-03-14-13-08-20.gh-issue-127266._tyfBp.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-04-04-16-41-00.gh-issue-133379.asdjhjdf.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-04-10-01-52-42.gh-issue-132042.fePwlj.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-04-16-12-01-13.gh-issue-127971.pMDOQ0.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-04-19-16-22-47.gh-issue-132732.jgqhlF.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-04-19-17-16-46.gh-issue-132542.7T_TY_.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-04-26-17-50-01.gh-issue-131798.XiOgw5.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-04-28-18-59-11.gh-issue-130821.B11LU1.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-04-30-14-13-01.gh-issue-132554.GqQaUp.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-03-13-36-01.gh-issue-131798.U4_QEJ.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-03-22-31-53.gh-issue-131798.fQ0ato.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-06-15-01-41.gh-issue-133516.RqWVf2.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-07-23-26-53.gh-issue-133541.bHIC55.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-08-13-48-02.gh-issue-132762.tKbygC.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-08-22-19-10.gh-issue-133711.e91wUy.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-09-18-11-21.gh-issue-133778.pWEV3t.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-10-17-12-27.gh-issue-133703.bVM-re.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-11-09-40-19.gh-issue-133400.zkWla8.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-11-13-40-42.gh-issue-133886.ryBAyo.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-15-11-38-16.gh-issue-133999.uBZ8uS.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-16-09-06-38.gh-issue-134036.st2e-B.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-16-17-25-52.gh-issue-134100.5-FbLK.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-16-20-59-12.gh-issue-134119.w8expI.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-17-20-44-51.gh-issue-134158.ewLNLp.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-17-20-56-05.gh-issue-91153.afgtG2.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-18-10-50-46.gh-issue-134170.J0Hvmi.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-18-14-33-23.gh-issue-69605.ZMO49F.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-15-15-58.gh-issue-131798.PCP71j.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-20-52-53.gh-issue-134268.HPKX1e.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-20-13-58-18.gh-issue-131798.hG8xBw.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-20-14-41-50.gh-issue-128066.qzzGfv.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-20-23-32-11.gh-issue-131798.G9ZQZw.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-21-13-57-26.gh-issue-131798.QwS5Bb.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-21-15-14-32.gh-issue-130397.aG6EON.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-21-18-02-56.gh-issue-127960.W3J_2X.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-22-14-48-19.gh-issue-134381.2BXhth.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-22-17-49-39.gh-issue-131798.U6ZmFm.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-23-14-54-07.gh-issue-134584.y-WDjf.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-25-19-32-15.gh-issue-131798.f5h8aI.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-26-15-55-50.gh-issue-133912.-xAguL.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-27-18-59-54.gh-issue-134679.FWPBu6.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-27-20-21-34.gh-issue-131798.b32zkl.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-27-20-29-00.gh-issue-132617.EmUfQQ.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-28-23-58-50.gh-issue-117852.BO9g7z.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-30-15-56-19.gh-issue-134908.3a7PxM.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-30-18-09-54.gh-issue-134889.Ic9UM-.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-31-10-26-46.gh-issue-134876.8mBGJI.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-31-19-24-54.gh-issue-134280.NDVbzY.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-02-13-57-40.gh-issue-116738.ycJsL8.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-02-20-13-37.gh-issue-131798.JQRFvR.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-03-21-06-22.gh-issue-133136.Usnvri.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-05-21-58-30.gh-issue-131798.nt5Ab7.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-06-01-09-44.gh-issue-131798.1SuxO9.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-06-02-24-42.gh-issue-135148.r-t2sC.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-06-19-17-22.gh-issue-131798.XoV8Eb.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-08-14-24-29.gh-issue-131798.qfw91T.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-09-23-57-37.gh-issue-130077.MHknDB.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-11-15-08-10.gh-issue-127319.OVGFSZ.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-12-00-03-34.gh-issue-116738.iBBAdo.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-12-11-19-52.gh-issue-135422.F6yQi6.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-12-18-12-42.gh-issue-135371.R_YUtR.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-13-16-05-24.gh-issue-135474.67nOl3.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-14-01-01-14.gh-issue-135496.ER0Me3.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-16-02-31-42.gh-issue-135543.6b0HOF.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-16-03-56-15.gh-issue-135551.hRTQO-.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-12-50-48.gh-issue-135608.PnHckD.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-18-12-19-13.gh-issue-135379.TCvGpj.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-18-16-45-36.gh-issue-135106.cpl6Aq.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-20-14-50-44.gh-issue-134584.3CJdAI.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-23-18-08-32.gh-issue-135871.50C528.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-06-41-47.gh-issue-129958.EaJuS0.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-16-46-34.gh-issue-135904.78xfon.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-26-15-25-51.gh-issue-78465.MbDN8X.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-26-18-44-34.gh-issue-136003.sln51d.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-02-15-18-41.gh-issue-136203.Y934sC.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-03-06-04-42.gh-issue-135552.CbBQof.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-06-14-53-19.gh-issue-109700.KVNQQi.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-07-12-24-00.gh-issue-136355.MTcA8j.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-07-17-26-06.gh-issue-91636.GyHU72.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-08-23-22-08.gh-issue-132661.34ftJl.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-08-23-53-51.gh-issue-132661.B84iYt.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-09-11-15-42.gh-issue-136459.m4Udh8.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-09-21-27-14.gh-issue-132657.kSA8R3.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-10-15-53-16.gh-issue-136525.xAko0e.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-10-23-23-50.gh-issue-136517._NHJyv.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-11-12-29-09.gh-issue-107545.ipfl7U.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-11-13-45-48.gh-issue-136541.uZ_-Ju.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-12-09-59-14.gh-issue-136421.ZD1rNj.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-13-21-21-17.gh-issue-136599.sLhm2O.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-14-17-01-23.gh-issue-136616.FQjXE_.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-15-10-03-57.gh-issue-116738.oFttKl.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-18-08-43-35.gh-issue-116738.i0HWtP.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-12-37-05.gh-issue-136801.XU_tF2.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-17-08-09.gh-issue-127598.Mx8S-y.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-24-02-13-59.gh-issue-132732.p77xkb.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-24-17-30-58.gh-issue-136870.ncx82J.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-25-22-31-52.gh-issue-131338.zJDCMp.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-28-17-01-05.gh-issue-88886.g4XFPb.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-28-19-11-34.gh-issue-134291.IiB9Id.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-31-23-02-02.gh-issue-137291.kIxVZd.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-01-18-54-31.gh-issue-137288.FhE7ku.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-02-10-27-53.gh-issue-137308.at05p_.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-02-23-04-57.gh-issue-137314.wjEdzD.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-05-10-22-15.gh-issue-136966.J5lrE0.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-05-17-22-24.gh-issue-58124.q1__53.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-05-20-24-12.gh-issue-120037.MB7MmI.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-06-15-39-54.gh-issue-137400.xIw0zs.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-06-16-55-44.gh-issue-133143.l7CI9v.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-07-09-52-19.gh-issue-137400.AK1dy-.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-09-04-07-05.gh-issue-132732.8BiIVJ.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-09-11-38-37.gh-issue-37817.Y5Fhde.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-10-21-34-12.gh-issue-137576.0ZicS-.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-13-13-39-02.gh-issue-137433.g6Atfz.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-13-16-58-58.gh-issue-137716.ZcZSyi.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-14-14-18-29.gh-issue-137728.HdYS9R.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-15-15-45-26.gh-issue-137079.YEow69.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-17-13-36-53.gh-issue-137883.55VDCN.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-19-16-07-07.gh-issue-137959.EWj0RZ.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-19-18-52-22.gh-issue-137967.uw67Ys.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-20-14-17-47.gh-issue-137992.fcL3SK.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-21-01-46-39.gh-issue-137976.p4sb4x.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-21-06-31-42.gh-issue-138004.FH2Hre.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-22-11-39-40.gh-issue-137384.j4b_in.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-27-13-11-47.gh-issue-71679.V0yFeT.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-27-17-51-38.gh-issue-137838.lK6T0j.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-28-09-29-46.gh-issue-116738.yLZJpV.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-30-00-55-35.gh-issue-61206.HeFLvl.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-30-17-15-05.gh-issue-69605.KjBk99.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-01-13-54-43.gh-issue-138349.0fGmAi.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-01-16-09-02.gh-issue-138318.t-WEN5.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-01-21-52-54.gh-issue-138302.-ez47B.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-02-09-10-06.gh-issue-138372.h1Xk4-.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-02-22-17-55.gh-issue-138401.uTRvue.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-03-10-16-09.gh-issue-138378.r6BQxV.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-03-15-35-34.gh-issue-138431.EUsrtA.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-03-17-00-30.gh-issue-138479.qUxgWs.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-05-01-19-04.gh-issue-138192.erluq5.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-06-13-53-33.gh-issue-105487.a43YaY.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-09-23-59-13.gh-issue-138716.UawDY0.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-10-14-53-59.gh-issue-71810.ppf0J-.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-11-15-56-18.gh-issue-138794.nrOn1K.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-15-14-04-56.gh-issue-134466.yR4fYW.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-17-17-17-21.gh-issue-138558.0VbzCH.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-21-14-33-17.gh-issue-116738.vNaI4h.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-22-15-21-49.gh-issue-74857.5XRQaA.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-24-17-08-42.gh-issue-133059.EXvxb7.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-24-17-32-52.gh-issue-139275.novrqf.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-30-14-57-19.gh-issue-139116.nlVf40.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-01-18-21-19.gh-issue-63161.ef1S6N.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-13-15-26.gh-issue-139516.d9Pkur.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-08-13-52-00.gh-issue-139748.jq0yFJ.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-12-11-00-06.gh-issue-139988.4wi51t.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-12-18-54-06.gh-issue-140009.-MbFh_.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-13-17-56-23.gh-issue-140000.tLhn3e.rst delete mode 100644 Misc/NEWS.d/next/Documentation/2021-09-15-13-07-25.bpo-45210.RtGk7i.rst delete mode 100644 Misc/NEWS.d/next/Documentation/2025-06-10-17-02-06.gh-issue-135171.quHvts.rst delete mode 100644 Misc/NEWS.d/next/Documentation/2025-07-01-23-00-58.gh-issue-136155.4siQQO.rst delete mode 100644 Misc/NEWS.d/next/IDLE/2025-10-08-08-35-50.gh-issue-139742.B3fZLg.rst delete mode 100644 Misc/NEWS.d/next/IDLE/2025-10-09-12-53-47.gh-issue-96491.4YKxvy.rst delete mode 100644 Misc/NEWS.d/next/Library/2017-12-30-18-21-00.bpo-28494.Dt_Wks.rst delete mode 100644 Misc/NEWS.d/next/Library/2020-09-23-11-54-17.bpo-41839.kU5Ywl.rst delete mode 100644 Misc/NEWS.d/next/Library/2021-03-07-16-31-36.bpo-43429.Koa0mf.rst delete mode 100644 Misc/NEWS.d/next/Library/2021-09-21-17-17-29.gh-issue-84683.wDSRsG.rst delete mode 100644 Misc/NEWS.d/next/Library/2021-12-18-12-46-20.bpo-45959.vPlr3P.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-01-07-16-56-57.bpo-38735.NFfJX6.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-07-24-20-56-32.gh-issue-69426.unccw7.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-10-08-14-56-07.gh-issue-93334.0KUm8d.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-02-13-20-34-52.gh-issue-78319.V1zzed.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-02-13-21-41-34.gh-issue-86155.ppIGSC.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-02-13-21-56-38.gh-issue-62824.CBZzX3.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-03-13-22-51-40.gh-issue-99813.40TV02.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-05-14-34-10.gh-issue-105497.HU5u89.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-05-13-09-50-31.gh-issue-118981.zgOQPv.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-06-06-17-49-07.gh-issue-120170.DUxhmT.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-07-06-14-32-30.gh-issue-119186.E5B1HQ.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-07-16-00-01-04.gh-issue-99631.GWD4fD.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-09-13-09-43-15.gh-issue-120492.Mm6CJ6.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-09-13-09-46-47.gh-issue-91216.LuOsF4.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-09-13-09-48-25.gh-issue-124033.WNudS0.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-10-17-01-12-22.gh-issue-119109.u4hcvb.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-10-22-16-21-55.gh-issue-125843.2ttzYo.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-10-28-06-54-22.gh-issue-125028.GEY8Ws.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-25-10-22-08.gh-issue-126883.MAEF7g.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-03-09-03-13-41.gh-issue-130999.tBRBVB.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-03-11-05-24-14.gh-issue-130664.g0yNMm.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-03-13-20-48-58.gh-issue-123471.cM4w4f.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-03-17-21-21-06.gh-issue-131146.A5Obgv.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-03-19-12-41-42.gh-issue-91349.8eTOCP.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-03-27-08-13-32.gh-issue-131788.0RWiFc.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-04-07-06-41-54.gh-issue-131884.ym9BJN.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-04-07-09-53-54.gh-issue-87790.6nj3zQ.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-04-07-10-20-16.gh-issue-87790.X2SjJe.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-04-08-07-25-10.gh-issue-107583.JGfbhq.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-04-16-21-02-57.gh-issue-132551.Psa7pL.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-04-21-00-58-04.gh-issue-127081.3DCl92.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-04-21-01-03-15.gh-issue-127081.WXRliX.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-04-21-01-05-14.gh-issue-127081.Egrpq7.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-04-22-21-00-23.gh-issue-123471.asOLA2.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-04-25-11-48-00.gh-issue-122781.ajsdns.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-04-25-11-53-37.gh-issue-95380.7dvPe-.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-04-25-16-06-53.gh-issue-132908.wV5rja.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-04-26-15-50-12.gh-issue-133009.etBuz5.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-04-29-11-48-46.gh-issue-132876.lyTQGZ.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-04-30-19-32-18.gh-issue-132969.EagQ3G.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-01-10-56-44.gh-issue-132813.rKurvp.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-01-16-03-11.gh-issue-133017.k7RLQp.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-04-17-04-55.gh-issue-132493.huirKi.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-05-03-14-08.gh-issue-133390.AuTggn.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-05-10-41-41.gh-issue-133253.J5-xDD.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-05-18-50-00.gh-issue-133447.ajshdb.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-05-22-11-24.gh-issue-133439.LpmyFz.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-06-14-44-55.gh-issue-133517.Ca6NgW.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-06-22-54-37.gh-issue-133551.rfy1tJ.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-07-13-31-06.gh-issue-92897.ubeqGE.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-07-14-36-30.gh-issue-133577.BggPk9.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-07-19-16-41.gh-issue-133581.kERUCJ.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-07-22-15-15.gh-issue-133595.c3U88r.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-08-13-43-19.gh-issue-133489.9eGS1Z.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-08-20-03-20.gh-issue-133722.1-B82a.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-08-20-45-35.gh-issue-133656.cxZODA.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-09-08-49-03.gh-issue-133701.KI8tGz.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-09-09-10-34.gh-issue-130328.s9h4By.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-09-15-50-00.gh-issue-77057.fV8SU-.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-09-18-29-25.gh-issue-133684.Y1DFSt.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-09-19-05-24.gh-issue-133783.1voCnR.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-09-20-59-24.gh-issue-132641.3qTw44.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-10-11-04-47.gh-issue-133810.03WhnK.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-10-12-06-55.gh-issue-133653.Gb2aG4.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-10-12-07-54.gh-issue-133817.4GMtKV.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-10-17-42-03.gh-issue-125996.vaQp0-.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-11-08-48-55.gh-issue-133823.F8udQy.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-11-10-01-48.gh-issue-133866.g3dHP_.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-11-10-28-11.gh-issue-133873.H03nov.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-11-11-39-05.gh-issue-133875.pUar3l.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-11-12-56-52.gh-issue-133604.kFxhc8.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-12-06-52-10.gh-issue-133925.elInBY.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-12-20-38-57.gh-issue-133960.Aee79f.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-13-18-54-56.gh-issue-133970.6G-Oi6.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-15-00-27-09.gh-issue-134004.e8k4-R.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-15-14-27-01.gh-issue-134062.fRbJet.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-16-12-40-37.gh-issue-132124.T_5Odx.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-16-20-10-25.gh-issue-134098.YyTkKr.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-17-12-40-12.gh-issue-133889.Eh-zO4.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-17-13-46-20.gh-issue-134097.fgkjE1.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-17-18-08-35.gh-issue-133890.onn9_X.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-17-20-23-57.gh-issue-133982.smS7au.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-18-07-25-15.gh-issue-134173.53oOoF.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-18-12-23-07.gh-issue-134087.HilZWl.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-18-12-48-39.gh-issue-62184.y11l10.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-18-13-23-29.gh-issue-134168.hgx3Xg.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-18-23-46-21.gh-issue-134152.30HwbX.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-19-10-32-11.gh-issue-134152.INJC2j.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-19-15-05-24.gh-issue-134235.pz9PwV.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-19-15-30-00.gh-issue-132983.asdsfs.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-19-17-27-21.gh-issue-80184.LOkbaw.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-19-18-12-42.gh-issue-88994.7avvVu.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-20-11-35-08.gh-issue-72902.jzEI-E.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-20-11-51-17.gh-issue-71189.0LpTB1.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-20-15-13-43.gh-issue-86802.trF7TM.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-20-19-16-30.gh-issue-134323.ZQZGvw.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-20-21-45-58.gh-issue-90871.Gkvtp6.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-22-13-10-32.gh-issue-114177.3TYUJ3.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-22-14-12-53.gh-issue-134451.M1rD-j.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-22-18-14-13.gh-issue-134546.fjLVzK.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-23-10-15-36.gh-issue-134565.zmb66C.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-23-20-01-52.gh-issue-134580.xnaJ70.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-23-23-43-39.gh-issue-134582.9POq3l.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-24-03-10-36.gh-issue-80334.z21cMa.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-24-13-10-35.gh-issue-134210.0IuMY2.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-25-11-02-05.gh-issue-134657.3YFhR9.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-25-13-46-37.gh-issue-134635.ZlPrlX.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-25-23-23-05.gh-issue-134151.13Wwsb.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-26-10-52-27.gh-issue-134698.aJ1mZ1.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-26-11-01-54.gh-issue-134531.my1Fzt.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-26-12-31-08.gh-issue-132710.ApU3TZ.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-26-14-04-39.gh-issue-134696.P04xUa.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-26-17-06-40.gh-issue-134637.9-3zRL.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-26-22-18-32.gh-issue-134771.RKXpLT.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-27-11-13-51.gh-issue-133579.KY9M6S.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-27-11-18-13.gh-issue-133579.ohtgdC.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-27-11-24-38.gh-issue-133579.WGPUC1.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-28-15-53-27.gh-issue-128840.Nur2pB.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-28-20-49-29.gh-issue-134857.dVYXVO.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-29-06-53-40.gh-issue-134885.-_L22o.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-29-17-39-13.gh-issue-108885.MegCRA.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-30-09-46-21.gh-issue-134939.Pu3nnm.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-30-13-07-29.gh-issue-134718.9Qvhxn.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-30-18-13-48.gh-issue-134718.5FEspx.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-31-12-08-12.gh-issue-134970.lgSaxq.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-05-31-15-49-46.gh-issue-134978.mXXuvW.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-01-11-14-00.gh-issue-134953.ashdfs.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-01-14-18-48.gh-issue-135004.cq3-fp.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-02-14-28-30.gh-issue-130662.EIgIR8.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-02-14-36-28.gh-issue-130662.Gpr2GB.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-03-12-59-17.gh-issue-135069.xop30V.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-06-17-34-18.gh-issue-133934.yT1r68.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-08-01-10-34.gh-issue-135241.5j18IW.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-08-10-22-22.gh-issue-135244.Y2SOTJ.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-08-11-11-07.gh-issue-135234.wJCdh0.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-08-14-50-34.gh-issue-135276.ZLUhV1.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-09-10-16-55.gh-issue-121914.G6Avkq.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-10-00-42-30.gh-issue-135321.UHh9jT.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-10-10-22-18.gh-issue-130870.JipqbO.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-10-16-11-00.gh-issue-133967.P0c24q.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-10-21-00-48.gh-issue-126631.eITVJd.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-10-21-42-04.gh-issue-135335.WnUqb_.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-11-15-08-02.gh-issue-135336.6Gq6MI.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-12-10-45-02.gh-issue-135368.OjWVHL.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-12-18-15-31.gh-issue-135429.mch75_.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-14-12-06-55.gh-issue-135487.KdVFff.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-14-14-19-13.gh-issue-135497.1pzwdA.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-15-03-03-22.gh-issue-65697.COdwZd.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-16-12-37-02.gh-issue-135444.An2eeA.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-16-15-00-13.gh-issue-135386.lNrxLc.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-16-15-03-03.gh-issue-135561.mJCN8D.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-17-22-44-19.gh-issue-119180.Ogv8Nj.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-17-23-13-56.gh-issue-135557.Bfcy4v.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-18-11-43-17.gh-issue-135646.r7ekEn.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-18-13-58-13.gh-issue-135645.109nff.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-18-19-25-32.gh-issue-123471.lx1Xbt.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-20-16-28-47.gh-issue-135759.jne0Zi.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-20-17-06-59.gh-issue-90117.GYWVrn.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-22-02-16-17.gh-issue-135640.FXyFL6.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-22-16-23-44.gh-issue-135815.0DandH.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-22-22-03-06.gh-issue-135823.iDBg97.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-23-10-19-11.gh-issue-135855.-J0AGF.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-23-11-04-25.gh-issue-135836.-C-c4v.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-23-13-02-08.gh-issue-134531.yUmj07.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-24-10-23-37.gh-issue-135853.6xDNOG.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-24-10-52-35.gh-issue-135836.s37351.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-24-13-30-47.gh-issue-135853.7ejTvK.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-24-14-43-24.gh-issue-135878.Db4roX.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-26-11-52-40.gh-issue-53203.TMigBr.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-26-17-19-36.gh-issue-105456.eR9oHB.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-26-17-28-49.gh-issue-135995.pPrDCt.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-27-09-26-04.gh-issue-87135.33z0UW.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-27-13-34-28.gh-issue-136028.RY727g.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-28-11-32-57.gh-issue-134759.AjjKcG.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-29-15-22-13.gh-issue-90733.NiquaA.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-06-30-11-12-24.gh-issue-85702.0Lrbwu.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-01-14-44-03.gh-issue-136097.bI1n14.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-02-10-48-21.gh-issue-136193.xfvras.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-02-18-41-45.gh-issue-133982.7qqAn6.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-04-12-53-02.gh-issue-136156.OYlXoz.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-04-23-45-00.gh-issue-136306.O1YLIU.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-05-06-56-16.gh-issue-136316.3zj_Do.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-05-06-59-46.gh-issue-136047.qWvycf.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-05-09-45-04.gh-issue-136286.N67Amr.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-06-10-18-48.gh-issue-136021.f-FJYT.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-06-18-38-10.gh-issue-135953.Z29DCz.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-07-16-46-55.gh-issue-72327.wLvRuj.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-07-22-12-32.gh-issue-136380.1b_nXl.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-08-20-58-01.gh-issue-136434.uuJsjS.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-09-20-29-30.gh-issue-136476.HyLLzh.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-10-00-47-37.gh-issue-136470.KlUEUG.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-10-10-18-19.gh-issue-52876.9Vjrd8.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-10-21-02-43.gh-issue-136507.pnEuGS.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-11-08-15-17.gh-issue-83336.ptpmq7.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-11-10-23-44.gh-issue-136492.BVi5h0.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-11-23-04-39.gh-issue-136549.oAi8u4.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-12-14-15-47.gh-issue-136571.muHmBv.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-12-18-05-37.gh-issue-136591.ujXmSN.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-13-11-20-05.gh-issue-136134.xhh0Kq.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-13-13-31-22.gh-issue-136134.mh6VjS.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-15-16-37-34.gh-issue-136669.Yexwah.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-16-09-45-58.gh-issue-53144.mrKwMW.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-17-16-12-23.gh-issue-136234.VmTxtj.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-19-11-53-19.gh-issue-135427.iJM_X2.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-19-15-40-47.gh-issue-131724.LS59nA.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-19-16-20-54.gh-issue-130645.O-dYcN.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-20-10-21-49.gh-issue-136787._0Rbp_.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-20-16-02-00.gh-issue-136874.cLC3o1.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-20-16-56-55.gh-issue-135228.n_XIao.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-21-01-16-32.gh-issue-83424.Y3tEV4.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-21-11-56-47.gh-issue-136912.zWosAL.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-21-15-40-00.gh-issue-136914.-GNG-d.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-21-16-10-24.gh-issue-124621.wyoWc1.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-21-16-13-20.gh-issue-136929.obKZ2S.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-21-20-00-42.gh-issue-121237.DyxNqo.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-21-22-35-50.gh-issue-136170.QUlc78.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-23-11-59-48.gh-issue-136980.BIJzkB.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-24-00-38-07.gh-issue-137059.fr64oW.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-25-09-21-56.gh-issue-130522.Crwq68.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-27-17-03-17.gh-issue-133951.7kwt78.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-28-20-48-32.gh-issue-137185.fgI7-B.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-28-23-11-29.gh-issue-81325.jMJFBe.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-29-05-12-50.gh-issue-137197.bMK3sO.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-29-21-18-31.gh-issue-137226.B_4lpu.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-30-11-12-22.gh-issue-124503.d4hc7b.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-30-17-42-36.gh-issue-137239.qSpj32.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-30-18-07-33.gh-issue-137257.XBtzf2.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-31-10-31-56.gh-issue-137282.GOCwIC.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-07-31-16-43-16.gh-issue-137191.FIogE8.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-01-15-07-59.gh-issue-137273.4V8Xmv.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-01-23-11-25.gh-issue-137017.0yGcNc.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-01-23-52-49.gh-issue-75989.5aYXNJ.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-03-00-36-57.gh-issue-115766.nJCFkW.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-03-13-16-39.gh-issue-137044.0hPVL_.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-06-16-13-47.gh-issue-137466.Whv0-A.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-06-16-54-22.gh-issue-137481.eSTkK0.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-06-23-16-42.gh-issue-137477.bk6BDV.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-07-12-32-23.gh-issue-137044.abNoIy.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-07-15-07-44.gh-issue-137512.j2or5h.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-07-17-18-57.gh-issue-137490.s89ieZ.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-08-15-00-38.gh-issue-137426.lW-Rk2.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-08-21-20-14.gh-issue-92936.rOgG1S.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-09-08-53-32.gh-issue-137583.s6OZud.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-11-05-05-08.gh-issue-137630.9lmqyc.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-11-14-18-32.gh-issue-137634.M7iBG6.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-13-10-50-22.gh-issue-73487.DUHbBq.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-14-00-00-12.gh-issue-137729.i9NSKP.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-14-10-27-07.gh-issue-125854.vDzFcZ.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-16-09-02-11.gh-issue-137754.mCev1Y.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-16-16-04-15.gh-issue-137317.Dl13B5.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-16-18-11-41.gh-issue-90548.q3aJUK.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-17-10-22-31.gh-issue-132947.XR4MJ8.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-18-07-10-55.gh-issue-137840.9b7AnG.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-18-16-02-51.gh-issue-134869.GnAjnU.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-19-00-12-57.gh-issue-137884.4faCA_.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-22-09-53-45.gh-issue-86819.ECxvwx.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-22-12-48-14.gh-issue-138044.lEQULC.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-24-02-04-32.gh-issue-138092.V4-wTO.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-25-16-22-32.gh-issue-138122.eMNDZ1.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-25-18-06-04.gh-issue-138133.Zh9rGo.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-25-22-38-03.gh-issue-134716.kyYKeX.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-27-17-05-36.gh-issue-138010.ZZJmPL.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-28-13-20-09.gh-issue-138204.8oLOud.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-29-12-05-33.gh-issue-78502.VpIMxg.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-29-12-56-55.gh-issue-138239.uthZFI.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-30-10-04-28.gh-issue-60462.yh_vDc.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-30-10-58-15.gh-issue-138253.9Ehj-N.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-30-17-58-04.gh-issue-138252.CDiEby.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-31-09-06-49.gh-issue-138008.heOvsU.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-31-12-34-02.gh-issue-138205.iHXb1z.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-08-31-22-10-22.gh-issue-57911.N_Ixtv.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-02-10-23-09.gh-issue-116946.U6RpwK.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-02-10-27-21.gh-issue-116946.VxXNGD.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-03-09-03-11.gh-issue-132657.cbAIDh.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-03-15-20-10.gh-issue-138432.RMc7UX.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-04-15-18-11.gh-issue-111788.tuTEM5.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-05-05-53-43.gh-issue-99948.KMSlG6.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-05-07-50-18.gh-issue-138515.E3M-pu.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-05-15-35-59.gh-issue-88375.dC491a.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-05-21-10-24.gh-issue-137706.0EztiJ.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-06-11-26-21.gh-issue-138514.66ltOb.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-06-14-47-23.gh-issue-116946.hj_u1t.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-06-14-53-19.gh-issue-116946.c-npxd.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-06-14-54-01.gh-issue-116946.hzQEWI.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-06-14-56-40.gh-issue-116946.GGIeyO.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-06-20-09-32.gh-issue-138535.mlntEe.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-08-17-32-02.gh-issue-76007.peEgcr.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-09-10-48-26.gh-issue-138706.xB--LX.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-09-17-57-49.gh-issue-138720.hAtsm-.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-10-10-02-59.gh-issue-128636.ldRKGZ.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-10-10-11-59.gh-issue-138712.avrPG5.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-10-13-32-25.gh-issue-138682.iExqx1.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-11-11-09-28.gh-issue-138779.TNZnLr.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-12-01-01-05.gh-issue-138804.46ZukT.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-15-08-57-39.gh-issue-138899.Uh6fvY.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-15-13-09-19.gh-issue-137226.HH3_ik.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-15-19-29-12.gh-issue-130567.shDEnT.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-16-15-56-29.gh-issue-118803.aOPtmL.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-16-16-46-58.gh-issue-138993.-8s8_T.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-16-19-05-29.gh-issue-138998.URl0Y_.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-17-08-32-43.gh-issue-138813.LHkHjX.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-17-12-07-21.gh-issue-139001.O6tseN.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-17-19-08-34.gh-issue-139065.Hu8fM5.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-17-21-52-30.gh-issue-139090.W7vbhF.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-17-21-54-53.gh-issue-139076.2eX9lG.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-18-05-32-18.gh-issue-135729.8AmMza.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-18-14-21-57.gh-issue-118803.2JPbto.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-19-07-41-52.gh-issue-126016.Uz9W6h.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-19-09-36-42.gh-issue-112729.mmty0_.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-20-17-50-31.gh-issue-138860.Y9JXap.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-21-15-58-57.gh-issue-139210.HGbMvz.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-22-11-19-05.gh-issue-95953.7oLoag.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-22-11-30-45.gh-issue-67795.fROoZt.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-22-14-40-11.gh-issue-90949.UM35nb.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-24-14-17-34.gh-issue-139289.Vmk25k.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-25-07-33-43.gh-issue-139312.ygE8AC.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-26-18-04-28.gh-issue-90949.YHjSzX.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-27-08-26-31.gh-issue-139374.hfh-dl.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-28-16-34-11.gh-issue-139391.nRFnmx.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-09-29-14-15-20.gh-issue-139184.dNl9O4.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-10-01-20-30-03.gh-issue-135953.NAofJl.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-10-02-15-45-08.gh-issue-139322.rouPGj.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-10-02-17-40-10.gh-issue-70765.zVlLZn.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-10-08-00-06-30.gh-issue-139736.baPeBd.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-10-09-03-06-19.gh-issue-139809.lzHJNu.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-10-09-13-48-28.gh-issue-139783.__NUgo.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-10-09-21-37-20.gh-issue-139845.dzx5UP.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-10-10-11-22-50.gh-issue-139894.ECAXqj.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-10-11-10-02-56.gh-issue-139905.UyJIR_.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-10-11-14-37-42.gh-issue-139823.uGF4oh.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-10-11-17-41-26.gh-issue-139958.AnCakj.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-10-11-20-03-13.gh-issue-139482.du2Stg.rst delete mode 100644 Misc/NEWS.d/next/Security/2025-01-14-11-19-07.gh-issue-128840.M1doZW.rst delete mode 100644 Misc/NEWS.d/next/Security/2025-05-07-22-49-27.gh-issue-133623.fgWkBm.rst delete mode 100644 Misc/NEWS.d/next/Security/2025-05-09-20-22-54.gh-issue-133767.kN2i3Q.rst delete mode 100644 Misc/NEWS.d/next/Security/2025-06-02-11-32-23.gh-issue-135034.RLGjbp.rst delete mode 100644 Misc/NEWS.d/next/Security/2025-06-09-20-38-25.gh-issue-118350.KgWCcP.rst delete mode 100644 Misc/NEWS.d/next/Security/2025-06-13-15-55-22.gh-issue-135462.KBeJpc.rst delete mode 100644 Misc/NEWS.d/next/Security/2025-06-18-13-28-08.gh-issue-102555.nADrzJ.rst delete mode 100644 Misc/NEWS.d/next/Security/2025-06-18-13-34-55.gh-issue-135661.NZlpWf.rst delete mode 100644 Misc/NEWS.d/next/Security/2025-06-25-14-13-39.gh-issue-135661.idjQ0B.rst delete mode 100644 Misc/NEWS.d/next/Security/2025-06-27-21-23-19.gh-issue-136053.QZxcee.rst delete mode 100644 Misc/NEWS.d/next/Security/2025-09-24-13-39-56.gh-issue-139283.jODz_q.rst delete mode 100644 Misc/NEWS.d/next/Security/2025-09-29-00-01-28.gh-issue-139400.X2T-jO.rst delete mode 100644 Misc/NEWS.d/next/Security/2025-10-07-19-31-34.gh-issue-139700.vNHU1O.rst delete mode 100644 Misc/NEWS.d/next/Tests/2025-05-08-15-06-01.gh-issue-133639.50-kbV.rst delete mode 100644 Misc/NEWS.d/next/Tests/2025-05-09-04-11-06.gh-issue-133682.-_lwo3.rst delete mode 100644 Misc/NEWS.d/next/Tests/2025-05-09-14-54-48.gh-issue-133744.LCquu0.rst delete mode 100644 Misc/NEWS.d/next/Tests/2025-05-23-09-19-52.gh-issue-134567.hwEIMb.rst delete mode 100644 Misc/NEWS.d/next/Tests/2025-06-04-13-07-44.gh-issue-135120.NapnZT.rst delete mode 100644 Misc/NEWS.d/next/Tests/2025-06-11-16-52-49.gh-issue-135401.ccMXmL.rst delete mode 100644 Misc/NEWS.d/next/Tests/2025-06-14-13-20-17.gh-issue-135489.Uh0yVO.rst delete mode 100644 Misc/NEWS.d/next/Tests/2025-06-17-08-48-08.gh-issue-132815.CY1Esu.rst delete mode 100644 Misc/NEWS.d/next/Tests/2025-06-19-15-29-38.gh-issue-135494.FVl9a0.rst delete mode 100644 Misc/NEWS.d/next/Tests/2025-06-26-15-15-35.gh-issue-135966.EBpF8Y.rst delete mode 100644 Misc/NEWS.d/next/Tests/2025-09-21-16-00-30.gh-issue-138313.lBx2en.rst delete mode 100644 Misc/NEWS.d/next/Tests/2025-09-22-15-40-09.gh-issue-139208.Tc13dl.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2025-05-19-14-57-46.gh-issue-134215.sbdDK6.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2025-06-11-12-14-06.gh-issue-135379.25ttXq.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2025-06-26-15-58-13.gh-issue-135968.C4v_-W.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2025-07-05-15-10-42.gh-issue-136251.GRM6o8.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2025-07-30-10-28-35.gh-issue-137243.NkdUqH.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2025-07-30-11-15-47.gh-issue-137248.8IxwY3.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2025-08-01-13-27-43.gh-issue-137025.ubuhQC.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2025-08-06-11-54-55.gh-issue-137484.8iFAQs.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2025-08-21-14-04-50.gh-issue-137873.qxffLt.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2025-08-27-11-14-53.gh-issue-138171.Suz8ob.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2025-08-28-06-22-26.gh-issue-132006.eZQmc6.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2025-09-25-10-31-02.gh-issue-139330.5WWkY0.rst delete mode 100644 Misc/NEWS.d/next/Windows/2025-03-31-15-37-57.gh-issue-131942.jip_aL.rst delete mode 100644 Misc/NEWS.d/next/Windows/2025-05-07-08-19-15.gh-issue-133537.yzf963.rst delete mode 100644 Misc/NEWS.d/next/Windows/2025-05-07-09-02-19.gh-issue-133562.lqqNW1.rst delete mode 100644 Misc/NEWS.d/next/Windows/2025-05-07-11-25-29.gh-issue-133568.oYV0d8.rst delete mode 100644 Misc/NEWS.d/next/Windows/2025-05-07-11-45-30.gh-issue-133572.Xc2zxH.rst delete mode 100644 Misc/NEWS.d/next/Windows/2025-05-07-13-04-22.gh-issue-133580.jBMujJ.rst delete mode 100644 Misc/NEWS.d/next/Windows/2025-05-08-19-07-26.gh-issue-133626.yFTKYK.rst delete mode 100644 Misc/NEWS.d/next/Windows/2025-05-13-13-25-27.gh-issue-133779.-YcTBz.rst delete mode 100644 Misc/NEWS.d/next/Windows/2025-05-19-03-02-04.gh-issue-76023.vHOf6M.rst delete mode 100644 Misc/NEWS.d/next/Windows/2025-05-20-21-43-20.gh-issue-130727.-69t4D.rst delete mode 100644 Misc/NEWS.d/next/Windows/2025-06-03-18-26-54.gh-issue-135099.Q9usKm.rst delete mode 100644 Misc/NEWS.d/next/Windows/2025-07-27-02-16-53.gh-issue-137134.W0WpDF.rst delete mode 100644 Misc/NEWS.d/next/Windows/2025-07-27-14-25-11.gh-issue-137136.xNthFT.rst delete mode 100644 Misc/NEWS.d/next/Windows/2025-09-03-01-07-44.gh-issue-138314.IeWQ2i.rst delete mode 100644 Misc/NEWS.d/next/Windows/2025-09-15-15-34-29.gh-issue-138896.lkiF_7.rst delete mode 100644 Misc/NEWS.d/next/Windows/2025-10-04-12-18-45.gh-issue-139573.EO9kVB.rst delete mode 100644 Misc/NEWS.d/next/Windows/2025-10-08-22-54-38.gh-issue-139810.LAaemi.rst delete mode 100644 Misc/NEWS.d/next/macOS/2025-07-27-02-17-40.gh-issue-137134.pjgITs.rst delete mode 100644 Misc/NEWS.d/next/macOS/2025-08-06-06-29-12.gh-issue-137450.JZypb7.rst delete mode 100644 Misc/NEWS.d/next/macOS/2025-10-13-23-46-12.gh-issue-132339.kAp603.rst delete mode 100644 Misc/NEWS.d/next/macOS/2025-10-14-00-08-16.gh-issue-124111.7-j-DQ.rst delete mode 100644 Misc/NEWS.d/next/macOS/2025-10-14-00-17-48.gh-issue-115119.470I1N.rst diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 3429a4eb652..28bf04651bd 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -305,7 +305,7 @@ the minimal value for the corresponding signed integer type of the same size. ``D`` (:class:`complex`) [Py_complex] Convert a Python complex number to a C :c:type:`Py_complex` structure. -.. deprecated:: next +.. deprecated:: 3.15 For unsigned integer formats ``B``, ``H``, ``I``, ``k`` and ``K``, :exc:`DeprecationWarning` is emitted when the value is larger than diff --git a/Doc/c-api/bytes.rst b/Doc/c-api/bytes.rst index 0a73c4748f4..9bddfe4dce2 100644 --- a/Doc/c-api/bytes.rst +++ b/Doc/c-api/bytes.rst @@ -236,7 +236,7 @@ PyBytesWriter The :c:type:`PyBytesWriter` API can be used to create a Python :class:`bytes` object. -.. versionadded:: next +.. versionadded:: 3.15 .. c:type:: PyBytesWriter diff --git a/Doc/c-api/complex.rst b/Doc/c-api/complex.rst index aa91b85d07f..d135637a741 100644 --- a/Doc/c-api/complex.rst +++ b/Doc/c-api/complex.rst @@ -16,7 +16,7 @@ Complex Number Objects The complex number value, using the C :c:type:`Py_complex` representation. - .. deprecated-removed:: next 3.20 + .. deprecated-removed:: 3.15 3.20 Use :c:func:`PyComplex_AsCComplex` and :c:func:`PyComplex_FromCComplex` to convert a Python complex number to/from the C :c:type:`Py_complex` diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 56857929a5a..8629b768a29 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -201,7 +201,7 @@ Object Protocol This case can arise from forgetting ``NULL`` checks and would delete the attribute. - .. versionchanged:: next + .. versionchanged:: 3.15 Must not be called with NULL value if an exception is set. @@ -226,7 +226,7 @@ Object Protocol For more details, see :c:func:`PyUnicode_InternFromString`, which may be used internally to create a key object. - .. versionchanged:: next + .. versionchanged:: 3.15 Must not be called with NULL value if an exception is set. diff --git a/Doc/c-api/stable.rst b/Doc/c-api/stable.rst index 8fed4b69b25..290166696dd 100644 --- a/Doc/c-api/stable.rst +++ b/Doc/c-api/stable.rst @@ -202,7 +202,7 @@ third-party distributors. ABI Checking ============ -.. versionadded:: next +.. versionadded:: 3.15 Python includes a rudimentary check for ABI compatibility. @@ -249,7 +249,7 @@ The full API is described below for advanced use cases. may lead to crashes. In particular, it is not safe to examine the raised exception. - .. versionadded:: next + .. versionadded:: 3.15 .. c:macro:: PyABIInfo_VAR(NAME) @@ -266,7 +266,7 @@ The full API is described below for advanced use cases. PyABIInfo_DEFAULT_ABI_VERSION } - .. versionadded:: next + .. versionadded:: 3.15 .. c:type:: PyABIInfo @@ -352,7 +352,7 @@ The full API is described below for advanced use cases. values of macros such as :c:macro:`Py_LIMITED_API`, :c:macro:`PY_VERSION_HEX` and :c:macro:`Py_GIL_DISABLED`. - .. versionadded:: next + .. versionadded:: 3.15 .. _limited-api-list: diff --git a/Doc/c-api/sys.rst b/Doc/c-api/sys.rst index 9a5e26d231c..336e3ef9640 100644 --- a/Doc/c-api/sys.rst +++ b/Doc/c-api/sys.rst @@ -268,7 +268,7 @@ accessible to C code. They all work with the current interpreter thread's If the non-existing object should not be treated as a failure, you can use :c:func:`PySys_GetOptionalAttr` instead. - .. versionadded:: next + .. versionadded:: 3.15 .. c:function:: PyObject *PySys_GetAttrString(const char *name) @@ -279,7 +279,7 @@ accessible to C code. They all work with the current interpreter thread's If the non-existing object should not be treated as a failure, you can use :c:func:`PySys_GetOptionalAttrString` instead. - .. versionadded:: next + .. versionadded:: 3.15 .. c:function:: int PySys_GetOptionalAttr(PyObject *name, PyObject **result) @@ -293,7 +293,7 @@ accessible to C code. They all work with the current interpreter thread's * Set an exception, set *\*result* to ``NULL``, and return ``-1``, if an error occurred. - .. versionadded:: next + .. versionadded:: 3.15 .. c:function:: int PySys_GetOptionalAttrString(const char *name, PyObject **result) @@ -301,7 +301,7 @@ accessible to C code. They all work with the current interpreter thread's specified as a :c:expr:`const char*` UTF-8 encoded bytes string, rather than a :c:expr:`PyObject*`. - .. versionadded:: next + .. versionadded:: 3.15 .. c:function:: PyObject *PySys_GetObject(const char *name) diff --git a/Doc/c-api/tuple.rst b/Doc/c-api/tuple.rst index 65f8334c437..14a7c05efea 100644 --- a/Doc/c-api/tuple.rst +++ b/Doc/c-api/tuple.rst @@ -47,7 +47,7 @@ Tuple Objects On success, return a new reference. On error, set an exception and return ``NULL``. - .. versionadded:: next + .. versionadded:: 3.15 .. c:function:: PyObject* PyTuple_Pack(Py_ssize_t n, ...) diff --git a/Doc/library/_thread.rst b/Doc/library/_thread.rst index 1d00d05817e..47f5eabb6f2 100644 --- a/Doc/library/_thread.rst +++ b/Doc/library/_thread.rst @@ -127,7 +127,7 @@ This module defines the following constants and functions: .. versionchanged:: 3.13 Added support for GNU/kFreeBSD. - .. versionchanged:: next + .. versionchanged:: 3.15 Added support for Solaris. diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index 319b2c81505..ea3ec7d95dc 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -2507,7 +2507,7 @@ and classes for traversing abstract syntax trees: .. versionchanged:: 3.13 Added the *show_empty* option. - .. versionchanged:: next + .. versionchanged:: 3.15 Omit optional ``Load()`` values by default. diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index fd397547a04..3af01b13241 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -533,7 +533,7 @@ The :mod:`calendar` module exports the following data attributes: in the standalone form if the locale provides one. Else it is equivalent to :data:`month_name`. - .. versionadded:: next + .. versionadded:: 3.15 .. data:: standalone_month_abbr @@ -542,7 +542,7 @@ The :mod:`calendar` module exports the following data attributes: locale in the standalone form if the locale provides one. Else it is equivalent to :data:`month_abbr`. - .. versionadded:: next + .. versionadded:: 3.15 .. data:: JANUARY diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst index 8c5c87a7ef1..194b8054ca5 100644 --- a/Doc/library/codecs.rst +++ b/Doc/library/codecs.rst @@ -78,7 +78,7 @@ The full details for each codec can also be looked up directly: .. versionchanged:: 3.9 Any characters except ASCII letters and digits and a dot are converted to underscore. - .. versionchanged:: next + .. versionchanged:: 3.15 No characters are converted to underscore anymore. Spaces are converted to hyphens. diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst index 52178d6c526..9a8108d882e 100644 --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -404,7 +404,7 @@ or subtracting from an empty counter. .. versionadded:: 3.3 Added support for unary plus, unary minus, and in-place multiset operations. -.. versionadded:: next +.. versionadded:: 3.15 Added support for the symmetric difference multiset operation, ``c ^ d``. .. note:: diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 3892367ff06..8ae1c1fb9e4 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -535,7 +535,7 @@ Other constructors, all class methods: :c:func:`localtime` function. Raise :exc:`OSError` instead of :exc:`ValueError` on :c:func:`localtime` failure. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number as *timestamp*, not only integer or float. @@ -1023,7 +1023,7 @@ Other constructors, all class methods: .. versionchanged:: 3.6 :meth:`fromtimestamp` may return instances with :attr:`.fold` set to 1. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number as *timestamp*, not only integer or float. @@ -1067,7 +1067,7 @@ Other constructors, all class methods: Use :meth:`datetime.fromtimestamp` with :const:`UTC` instead. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number as *timestamp*, not only integer or float. @@ -2641,7 +2641,7 @@ differences between platforms in handling of unsupported format specifiers. .. versionadded:: 3.12 ``%:z`` was added for :meth:`~.datetime.strftime` -.. versionadded:: next +.. versionadded:: 3.15 ``%:z`` was added for :meth:`~.datetime.strptime` Technical Detail diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst index 7b7ac2df126..02eb68d7b49 100644 --- a/Doc/library/dbm.rst +++ b/Doc/library/dbm.rst @@ -217,7 +217,7 @@ or any other SQLite browser, including the SQLite CLI. While reorganizing, as much as two times the size of the original database is required in free disk space. However, be aware that this factor changes for each :mod:`dbm` submodule. - .. versionadded:: next + .. versionadded:: 3.15 :mod:`dbm.gnu` --- GNU database manager @@ -519,7 +519,7 @@ The :mod:`!dbm.dumb` module defines the following: While reorganizing, no additional free disk space is required. However, be aware that this factor changes for each :mod:`dbm` submodule. - .. versionadded:: next + .. versionadded:: 3.15 .. method:: dumbdbm.sync() diff --git a/Doc/library/difflib.rst b/Doc/library/difflib.rst index 7a427078909..9e5a62d8fe5 100644 --- a/Doc/library/difflib.rst +++ b/Doc/library/difflib.rst @@ -323,7 +323,7 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module. See :ref:`difflib-interface` for a more detailed example. - .. versionchanged:: next + .. versionchanged:: 3.15 Added the *color* parameter. diff --git a/Doc/library/doctest.rst b/Doc/library/doctest.rst index 02b73ccd3f3..df3de8f622a 100644 --- a/Doc/library/doctest.rst +++ b/Doc/library/doctest.rst @@ -1123,7 +1123,7 @@ from text files and modules with doctests: The global ``__file__`` is added to the globals provided to doctests loaded from a text file using :func:`DocFileSuite`. - .. versionchanged:: next + .. versionchanged:: 3.15 Run each example as a :ref:`subtest `. @@ -1164,7 +1164,7 @@ from text files and modules with doctests: :func:`DocTestSuite` returns an empty :class:`unittest.TestSuite` if *module* contains no docstrings instead of raising :exc:`ValueError`. - .. versionchanged:: next + .. versionchanged:: 3.15 Run each example as a :ref:`subtest `. Under the covers, :func:`DocTestSuite` creates a :class:`unittest.TestSuite` out @@ -1564,7 +1564,7 @@ DocTestRunner objects containing *example*. *out* is the output function that was passed to :meth:`DocTestRunner.run`. - .. versionadded:: next + .. versionadded:: 3.15 .. method:: report_start(out, test, example) diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index 89ebb69d931..16d42c010f6 100644 --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -450,7 +450,7 @@ The following exceptions are the exceptions that are usually raised. :meth:`threading.Thread.join` can now raise this exception. - .. versionchanged:: next + .. versionchanged:: 3.15 This exception may be raised when acquiring :meth:`threading.Lock` or :meth:`threading.RLock`. diff --git a/Doc/library/fcntl.rst b/Doc/library/fcntl.rst index 5c078df44ff..f57fcdf0bcf 100644 --- a/Doc/library/fcntl.rst +++ b/Doc/library/fcntl.rst @@ -125,7 +125,7 @@ The module defines the following functions: Add support of arbitrary :term:`bytes-like objects `, not only :class:`bytes`. - .. versionchanged:: next + .. versionchanged:: 3.15 The size of bytes-like objects is no longer limited to 1024 bytes. @@ -187,7 +187,7 @@ The module defines the following functions: The GIL is always released during a system call. System calls failing with EINTR are automatically retried. - .. versionchanged:: next + .. versionchanged:: 3.15 The size of not mutated bytes-like objects is no longer limited to 1024 bytes. diff --git a/Doc/library/gzip.rst b/Doc/library/gzip.rst index c59014a6f5b..4bdcec66088 100644 --- a/Doc/library/gzip.rst +++ b/Doc/library/gzip.rst @@ -59,7 +59,7 @@ The module defines the following items: .. versionchanged:: 3.6 Accepts a :term:`path-like object`. - .. versionchanged:: next + .. versionchanged:: 3.15 The default compression level was reduced to 6 (down from 9). It is the default level used by most compression tools and a better tradeoff between speed and performance. @@ -186,7 +186,7 @@ The module defines the following items: Remove the ``filename`` attribute, use the :attr:`~GzipFile.name` attribute instead. - .. versionchanged:: next + .. versionchanged:: 3.15 The default compression level was reduced to 6 (down from 9). It is the default level used by most compression tools and a better tradeoff between speed and performance. @@ -216,7 +216,7 @@ The module defines the following items: The *mtime* parameter now defaults to 0 for reproducible output. For the previous behaviour of using the current time, pass ``None`` to *mtime*. - .. versionchanged:: next + .. versionchanged:: 3.15 The default compression level was reduced to 6 (down from 9). It is the default level used by most compression tools and a better tradeoff between speed and performance. diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst index 07f5ebf57c9..589152f2968 100644 --- a/Doc/library/http.client.rst +++ b/Doc/library/http.client.rst @@ -68,7 +68,7 @@ The module provides the following classes: .. versionchanged:: 3.7 *blocksize* parameter was added. - .. versionchanged:: next + .. versionchanged:: 3.15 *max_response_headers* parameter was added. @@ -114,7 +114,7 @@ The module provides the following classes: The deprecated *key_file*, *cert_file* and *check_hostname* parameters have been removed. - .. versionchanged:: next + .. versionchanged:: 3.15 *max_response_headers* parameter was added. @@ -429,7 +429,7 @@ HTTPConnection Objects The maximum number of allowed response headers to help prevent denial-of-service attacks. By default, the maximum number of allowed headers is set to 100. - .. versionadded:: next + .. versionadded:: 3.15 As an alternative to using the :meth:`~HTTPConnection.request` method described above, you can diff --git a/Doc/library/http.cookies.rst b/Doc/library/http.cookies.rst index 9e7648ef6d8..88e978d7f5e 100644 --- a/Doc/library/http.cookies.rst +++ b/Doc/library/http.cookies.rst @@ -30,7 +30,7 @@ in a cookie name (as :attr:`~Morsel.key`). .. versionchanged:: 3.3 Allowed '``:``' as a valid cookie name character. -.. versionchanged:: next +.. versionchanged:: 3.15 Allowed '``"``' as a valid cookie value character. .. note:: diff --git a/Doc/library/imaplib.rst b/Doc/library/imaplib.rst index 2a12a0ca8e9..0b0537d3bbd 100644 --- a/Doc/library/imaplib.rst +++ b/Doc/library/imaplib.rst @@ -413,7 +413,7 @@ An :class:`IMAP4` instance has the following methods: the password. Will only work if the server ``CAPABILITY`` response includes the phrase ``AUTH=CRAM-MD5``. - .. versionchanged:: next + .. versionchanged:: 3.15 An :exc:`IMAP4.error` is raised if MD5 support is not available. diff --git a/Doc/library/locale.rst b/Doc/library/locale.rst index 0800b3e5677..4824391e597 100644 --- a/Doc/library/locale.rst +++ b/Doc/library/locale.rst @@ -58,7 +58,7 @@ The :mod:`locale` module defines the following exception and functions: specified in the :envvar:`LANG` environment variable). If the locale is not changed thereafter, using multithreading should not cause problems. - .. versionchanged:: next + .. versionchanged:: 3.15 Support language codes with ``@``-modifiers. @@ -374,7 +374,7 @@ The :mod:`locale` module defines the following exception and functions: determined. The "C" locale is represented as ``(None, None)``. - .. versionchanged:: next + .. versionchanged:: 3.15 ``@``-modifier are no longer silently removed, but included in the language code. diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 55f2de07553..5eb82a10a8c 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -259,7 +259,7 @@ Floating point arithmetic is, :func:`!fmax` is not required to be sensitive to the sign of such operands (see Annex F of the C11 standard, §F.10.0.3 and §F.10.9.2). - .. versionadded:: next + .. versionadded:: 3.15 .. function:: fmin(x, y) @@ -271,7 +271,7 @@ Floating point arithmetic is, :func:`!fmin` is not required to be sensitive to the sign of such operands (see Annex F of the C11 standard, §F.10.0.3 and §F.10.9.3). - .. versionadded:: next + .. versionadded:: 3.15 .. function:: fmod(x, y) @@ -408,7 +408,7 @@ Floating point manipulation functions nonzero number that is not a subnormal (see :func:`issubnormal`). Return ``False`` otherwise. - .. versionadded:: next + .. versionadded:: 3.15 .. function:: issubnormal(x) @@ -417,7 +417,7 @@ Floating point manipulation functions nonzero number with a magnitude smaller than :data:`sys.float_info.min`. Return ``False`` otherwise. - .. versionadded:: next + .. versionadded:: 3.15 .. function:: isinf(x) @@ -464,7 +464,7 @@ Floating point manipulation functions This is useful to detect the sign bit of zeroes, infinities and NaNs. - .. versionadded:: next + .. versionadded:: 3.15 .. function:: ulp(x) diff --git a/Doc/library/mmap.rst b/Doc/library/mmap.rst index b6ffb5cebc0..f32aa322c40 100644 --- a/Doc/library/mmap.rst +++ b/Doc/library/mmap.rst @@ -78,7 +78,7 @@ To map anonymous memory, -1 should be passed as the fileno along with the length This mode is useful to limit the number of open file handles. The original file can be renamed (but not deleted) after closing *fileno*. - .. versionchanged:: next + .. versionchanged:: 3.15 The *trackfd* parameter was added. .. audit-event:: mmap.__new__ fileno,length,access,offset mmap.mmap @@ -229,7 +229,7 @@ To map anonymous memory, -1 should be passed as the fileno along with the length on error under Windows. A zero value was returned on success; an exception was raised on error under Unix. - .. versionchanged:: next + .. versionchanged:: 3.15 Allow specifying *offset* without *size*. Previously, both *offset* and *size* parameters were required together. Now *offset* can be specified alone, and the flush operation will extend from *offset* @@ -334,7 +334,7 @@ To map anonymous memory, -1 should be passed as the fileno along with the length memory-mapped area. For an anonymous mapping, return its size. - .. versionchanged:: next + .. versionchanged:: 3.15 Anonymous mappings are now supported on Unix. diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst index bb9a7c86464..c90e30ccf4f 100644 --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -449,7 +449,7 @@ the :mod:`glob` module.) .. versionchanged:: 3.10 The *strict* parameter was added. - .. versionchanged:: next + .. versionchanged:: 3.15 The :data:`ALL_BUT_LAST` and :data:`ALLOW_MISSING` values for the *strict* parameter was added. @@ -457,13 +457,13 @@ the :mod:`glob` module.) Special value used for the *strict* argument in :func:`realpath`. - .. versionadded:: next + .. versionadded:: 3.15 .. data:: ALLOW_MISSING Special value used for the *strict* argument in :func:`realpath`. - .. versionadded:: next + .. versionadded:: 3.15 .. function:: relpath(path, start=os.curdir) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 540eaa09d0e..a5843f3fc0d 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -108,7 +108,7 @@ Python UTF-8 Mode .. versionadded:: 3.7 See :pep:`540` for more details. -.. versionchanged:: next +.. versionchanged:: 3.15 Python UTF-8 mode is now enabled by default (:pep:`686`). It may be disabled with by setting :envvar:`PYTHONUTF8=0 ` as @@ -1553,7 +1553,7 @@ or `the MSDN `_ on Windo .. availability:: Linux >= 6.14 - .. versionadded:: next + .. versionadded:: 3.15 .. function:: ptsname(fd, /) @@ -2645,7 +2645,7 @@ features: Non-existent device. - .. versionadded:: next + .. versionadded:: 3.15 .. function:: pathconf(path, name) @@ -3646,7 +3646,7 @@ features: .. versionchanged:: 3.6 Accepts a :term:`path-like object`. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real numbers as *times*, not only integers or floats. @@ -4610,7 +4610,7 @@ written in Python, such as a mail server's external command delivery program. threads, this now raises a :exc:`DeprecationWarning`. See the longer explanation on :func:`os.fork`. - .. versionchanged:: next + .. versionchanged:: 3.15 The returned file descriptor is now made non-inheritable. .. availability:: Unix, not WASI, not Android, not iOS. diff --git a/Doc/library/pty.rst b/Doc/library/pty.rst index 9fef8760b62..2912c9e16c6 100644 --- a/Doc/library/pty.rst +++ b/Doc/library/pty.rst @@ -38,7 +38,7 @@ The :mod:`pty` module defines the following functions: .. warning:: On macOS the use of this function is unsafe when mixed with using higher-level system APIs, and that includes using :mod:`urllib.request`. - .. versionchanged:: next + .. versionchanged:: 3.15 The returned file descriptor is now made non-inheritable. diff --git a/Doc/library/pyexpat.rst b/Doc/library/pyexpat.rst index ec25c21c300..f533850c0ca 100644 --- a/Doc/library/pyexpat.rst +++ b/Doc/library/pyexpat.rst @@ -267,7 +267,7 @@ against some common XML vulnerabilities. Activation thresholds below 4 MiB are known to break support for DITA 1.3 payload and are hence not recommended. - .. versionadded:: next + .. versionadded:: 3.15 .. method:: xmlparser.SetBillionLaughsAttackProtectionMaximumAmplification(max_factor, /) @@ -304,7 +304,7 @@ against some common XML vulnerabilities. that can be adjusted by :meth:`.SetBillionLaughsAttackProtectionActivationThreshold` is exceeded. - .. versionadded:: next + .. versionadded:: 3.15 .. method:: xmlparser.SetAllocTrackerActivationThreshold(threshold, /) @@ -324,7 +324,7 @@ against some common XML vulnerabilities. Check for availability using :func:`hasattr` if used in code running across a variety of Python versions. - .. versionadded:: next + .. versionadded:: 3.15 .. method:: xmlparser.SetAllocTrackerMaximumAmplification(max_factor, /) @@ -360,7 +360,7 @@ against some common XML vulnerabilities. that can be adjusted by :meth:`.SetAllocTrackerActivationThreshold` is exceeded. - .. versionadded:: next + .. versionadded:: 3.15 :class:`xmlparser` objects have the following attributes: diff --git a/Doc/library/resource.rst b/Doc/library/resource.rst index 5bc68fdeff4..9f6c3a88f0a 100644 --- a/Doc/library/resource.rst +++ b/Doc/library/resource.rst @@ -51,7 +51,7 @@ this module for those platforms. Constant used to represent the limit for an unlimited resource. - .. versionchanged:: next + .. versionchanged:: 3.15 It is now always positive. Previously, it could be negative, such as -1 or -3. @@ -63,7 +63,7 @@ this module for those platforms. cannot be represented in the ``rlim_t`` value in C. Can be equal to :data:`RLIM_INFINITY`. - .. versionadded:: next + .. versionadded:: 3.15 .. function:: getrlimit(resource) @@ -296,7 +296,7 @@ platform. .. availability:: NetBSD >= 7.0. - .. versionadded:: next + .. versionadded:: 3.15 .. data:: RLIMIT_PIPEBUF @@ -306,7 +306,7 @@ platform. .. availability:: FreeBSD >= 14.2. - .. versionadded:: next + .. versionadded:: 3.15 .. data:: RLIMIT_THREADS @@ -315,7 +315,7 @@ platform. .. availability:: AIX. - .. versionadded:: next + .. versionadded:: 3.15 .. data:: RLIMIT_UMTXP @@ -325,7 +325,7 @@ platform. .. availability:: FreeBSD >= 11. - .. versionadded:: next + .. versionadded:: 3.15 Resource Usage diff --git a/Doc/library/select.rst b/Doc/library/select.rst index 5b14428574c..e821cb01d94 100644 --- a/Doc/library/select.rst +++ b/Doc/library/select.rst @@ -165,7 +165,7 @@ The module defines the following: :pep:`475` for the rationale), instead of raising :exc:`InterruptedError`. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number as *timeout*, not only integer or float. @@ -274,7 +274,7 @@ object. :pep:`475` for the rationale), instead of raising :exc:`InterruptedError`. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number as *timeout*, not only integer or float. @@ -385,7 +385,7 @@ Edge and Level Trigger Polling (epoll) Objects :pep:`475` for the rationale), instead of raising :exc:`InterruptedError`. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number as *timeout*, not only integer or float. @@ -476,7 +476,7 @@ linearly scanned again. :c:func:`!select` is *O*\ (*highest file descriptor*), w :pep:`475` for the rationale), instead of raising :exc:`InterruptedError`. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number as *timeout*, not only integer or float. @@ -520,7 +520,7 @@ Kqueue Objects :pep:`475` for the rationale), instead of raising :exc:`InterruptedError`. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number as *timeout*, not only integer or float. diff --git a/Doc/library/shelve.rst b/Doc/library/shelve.rst index b88fe4157bd..51bae2fce30 100644 --- a/Doc/library/shelve.rst +++ b/Doc/library/shelve.rst @@ -64,7 +64,7 @@ lots of shared sub-objects. The keys are ordinary strings. .. versionchanged:: 3.11 Accepts :term:`path-like object` for filename. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts custom *serializer* and *deserializer* functions in place of :func:`pickle.dumps` and :func:`pickle.loads`. @@ -103,7 +103,7 @@ Two additional methods are supported: Calls :meth:`sync` and attempts to shrink space used on disk by removing empty space resulting from deletions. - .. versionadded:: next + .. versionadded:: 3.15 .. method:: Shelf.close() @@ -185,7 +185,7 @@ Restrictions :const:`pickle.DEFAULT_PROTOCOL` is now used as the default pickle protocol. - .. versionchanged:: next + .. versionchanged:: 3.15 Added the *serializer* and *deserializer* parameters. @@ -204,7 +204,7 @@ Restrictions optional *protocol*, *writeback*, *keyencoding*, *serializer* and *deserializer* parameters have the same interpretation as in :func:`~shelve.open`. - .. versionchanged:: next + .. versionchanged:: 3.15 Added the *serializer* and *deserializer* parameters. @@ -220,7 +220,7 @@ Restrictions and *deserializer* parameters have the same interpretation as in :func:`~shelve.open`. - .. versionchanged:: next + .. versionchanged:: 3.15 Added the *serializer* and *deserializer* parameters. @@ -274,7 +274,7 @@ Exceptions The *deserializer* and *serializer* arguments must be given together. - .. versionadded:: next + .. versionadded:: 3.15 .. seealso:: diff --git a/Doc/library/signal.rst b/Doc/library/signal.rst index 47f824488f5..bbc4b91b921 100644 --- a/Doc/library/signal.rst +++ b/Doc/library/signal.rst @@ -523,7 +523,7 @@ The :mod:`signal` module defines the following functions: .. availability:: Unix. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real numbers as *seconds* and *interval*, not only integers or floats. @@ -702,7 +702,7 @@ The :mod:`signal` module defines the following functions: by a signal not in *sigset* and the signal handler does not raise an exception (see :pep:`475` for the rationale). - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number as *timeout*, not only integer or float. diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 134d0962db8..89bca9b5b20 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -1412,7 +1412,7 @@ The :mod:`socket` module also offers various network-related services: :meth:`~socket.settimeout` for possible values and their respective meanings. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number, not only integer or float. @@ -2088,7 +2088,7 @@ to sockets. The method no longer toggles :const:`SOCK_NONBLOCK` flag on :attr:`socket.type`. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number, not only integer or float. diff --git a/Doc/library/socketserver.rst b/Doc/library/socketserver.rst index 7fb629f7d2f..7bc2f7afbbb 100644 --- a/Doc/library/socketserver.rst +++ b/Doc/library/socketserver.rst @@ -24,7 +24,7 @@ There are four basic concrete server classes: :meth:`~BaseServer.server_activate`. The other parameters are passed to the :class:`BaseServer` base class. - .. versionchanged:: next + .. versionchanged:: 3.15 The default queue size is now ``socket.SOMAXCONN`` for :class:`socketserver.TCPServer`. .. class:: UDPServer(server_address, RequestHandlerClass, bind_and_activate=True) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index a7c9923f116..9d56e81dee1 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -514,7 +514,7 @@ Module constants This constant is only available if Python was compiled with SQLite 3.24.0 or greater. - .. versionadded:: next + .. versionadded:: 3.15 .. data:: threadsafety @@ -1611,7 +1611,7 @@ Cursor objects If the *size* parameter is used, then it is best for it to retain the same value from one :meth:`fetchmany` call to the next. - .. versionchanged:: next + .. versionchanged:: 3.15 Negative *size* values are rejected by raising :exc:`ValueError`. .. method:: fetchall() @@ -1641,7 +1641,7 @@ Cursor objects Read/write attribute that controls the number of rows returned by :meth:`fetchmany`. The default value is 1 which means a single row would be fetched per call. - .. versionchanged:: next + .. versionchanged:: 3.15 Negative values are rejected by raising :exc:`ValueError`. .. attribute:: connection diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 5b59a0698e4..e0d85c852fa 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -232,7 +232,7 @@ Signature algorithms :meth:`SSLContext.set_client_sigalgs` and :meth:`SSLContext.set_server_sigalgs` methods. - .. versionadded:: next + .. versionadded:: 3.15 Exceptions @@ -958,7 +958,7 @@ Constants Whether the OpenSSL library has built-in support for External PSKs in TLS 1.3 as described in :rfc:`9258`. - .. versionadded:: next + .. versionadded:: 3.15 .. data:: HAS_PHA @@ -1133,7 +1133,7 @@ SSL Sockets functions support reading and writing of data larger than 2 GB. Writing zero-length data no longer fails with a protocol violation error. - .. versionchanged:: next + .. versionchanged:: 3.15 Python now uses ``SSL_sendfile`` internally when possible. The function sends a file more efficiently because it performs TLS encryption in the kernel to avoid additional context switches. @@ -1317,7 +1317,7 @@ SSL sockets also have the following additional methods and attributes: Return the group used for doing key agreement on this connection. If no connection has been established, returns ``None``. - .. versionadded:: next + .. versionadded:: 3.15 .. method:: SSLSocket.client_sigalg() @@ -1325,7 +1325,7 @@ SSL sockets also have the following additional methods and attributes: authentication on this connection, or ``None`` if no connection has been established or client authentication didn't occur. - .. versionadded:: next + .. versionadded:: 3.15 .. method:: SSLSocket.server_sigalg() @@ -1333,7 +1333,7 @@ SSL sockets also have the following additional methods and attributes: handshake on this connection, or ``None`` if no connection has been established or the cipher suite has no signature. - .. versionadded:: next + .. versionadded:: 3.15 .. method:: SSLSocket.compression() @@ -1709,7 +1709,7 @@ to speed up repeated connections from the same clients. :const:`True` this method will also return any associated aliases such as the ECDH curve names supported in older versions of OpenSSL. - .. versionadded:: next + .. versionadded:: 3.15 .. method:: SSLContext.set_default_verify_paths() @@ -1748,7 +1748,7 @@ to speed up repeated connections from the same clients. When connected, the :meth:`SSLSocket.cipher` method of SSL sockets will return details about the negotiated cipher. - .. versionadded:: next + .. versionadded:: 3.15 .. method:: SSLContext.set_groups(groups, /) @@ -1761,7 +1761,7 @@ to speed up repeated connections from the same clients. When connected, the :meth:`SSLSocket.group` method of SSL sockets will return the group used for key agreement on that connection. - .. versionadded:: next + .. versionadded:: 3.15 .. method:: SSLContext.set_client_sigalgs(sigalgs, /) @@ -1776,7 +1776,7 @@ to speed up repeated connections from the same clients. sockets will return the signature algorithm used for performing certificate-based client authentication on that connection. - .. versionadded:: next + .. versionadded:: 3.15 .. method:: SSLContext.set_server_sigalgs(sigalgs, /) @@ -1790,7 +1790,7 @@ to speed up repeated connections from the same clients. sockets will return the signature algorithm used by the server to complete the TLS handshake on that connection. - .. versionadded:: next + .. versionadded:: 3.15 .. method:: SSLContext.set_alpn_protocols(alpn_protocols) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 34764a7e4f0..698a9d0689d 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -13,7 +13,7 @@ always available. Unless explicitly noted otherwise, all variables are read-only .. data:: abi_info - .. versionadded:: next + .. versionadded:: 3.15 An object containing information about the ABI of the currently running Python interpreter. @@ -2205,7 +2205,7 @@ always available. Unless explicitly noted otherwise, all variables are read-only :func:`sys.unraisablehook` can be overridden to control how unraisable exceptions are handled. - .. versionchanged:: next + .. versionchanged:: 3.15 Exceptions are now printed with colorful text. .. seealso:: diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index acaec5b592b..c4614bf28a4 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -198,7 +198,7 @@ Some facts and figures: .. versionchanged:: 3.14 The *preset* keyword argument also works for streams. - .. versionchanged:: next + .. versionchanged:: 3.15 The default compression level was reduced to 6 (down from 9). It is the default level used by most compression tools and a better tradeoff between speed and performance. @@ -294,7 +294,7 @@ The :mod:`tarfile` module defines the following exceptions: The exception that was raised to reject the replacement member is available as :attr:`!BaseException.__context__`. - .. versionadded:: next + .. versionadded:: 3.15 The following constants are available at the module level: @@ -1146,7 +1146,7 @@ reused in custom filters: Note that this filter does not block *all* dangerous archive features. See :ref:`tarfile-further-verification` for details. - .. versionchanged:: next + .. versionchanged:: 3.15 Link targets are now normalized. diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index c1705939fb6..19cc4f191df 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -198,7 +198,7 @@ This module defines the following functions: .. versionchanged:: 3.13 Added support for GNU/kFreeBSD. - .. versionchanged:: next + .. versionchanged:: 3.15 Added support for Solaris. @@ -632,7 +632,7 @@ since it is impossible to detect the termination of alien threads. May raise :exc:`PythonFinalizationError`. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number as *timeout*, not only integer or float. .. attribute:: name @@ -786,7 +786,7 @@ All methods are executed atomically. .. versionchanged:: 3.14 Lock acquisition can now be interrupted by signals on Windows. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number as *timeout*, not only integer or float. @@ -896,7 +896,7 @@ call release as many times the lock has been acquired can lead to deadlock. .. versionchanged:: 3.2 The *timeout* parameter is new. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number as *timeout*, not only integer or float. @@ -1159,7 +1159,7 @@ Semaphores also support the :ref:`context management protocol `. .. versionchanged:: 3.2 The *timeout* parameter is new. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number as *timeout*, not only integer or float. .. method:: release(n=1) diff --git a/Doc/library/time.rst b/Doc/library/time.rst index 8aff2b2dc67..350ffade7af 100644 --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -201,7 +201,7 @@ Functions .. versionadded:: 3.3 - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number as *time*, not only integer or float. @@ -226,7 +226,7 @@ Functions ``asctime(localtime(secs))``. Locale information is not used by :func:`ctime`. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number, not only integer or float. @@ -264,7 +264,7 @@ Functions :class:`struct_time` object. See :func:`calendar.timegm` for the inverse of this function. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number, not only integer or float. @@ -280,7 +280,7 @@ Functions :c:func:`gmtime` failure. It's common for this to be restricted to years between 1970 and 2038. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number, not only integer or float. @@ -439,7 +439,7 @@ Functions .. versionchanged:: 3.13 Raises an auditing event. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts any real number, not only integer or float. .. index:: diff --git a/Doc/library/types.rst b/Doc/library/types.rst index 207024a7619..40b5f3db13d 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -338,7 +338,7 @@ Standard names are defined for the following types: The type of frame locals proxy objects, as found on the :attr:`frame.f_locals` attribute. - .. versionadded:: next + .. versionadded:: 3.15 .. seealso:: :pep:`667` diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index b5331e0676d..fe45becce2e 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -1177,7 +1177,7 @@ Test cases .. versionadded:: 3.4 - .. versionchanged:: next + .. versionchanged:: 3.15 Now accepts a *formatter* to control how messages are formatted. .. method:: assertNoLogs(logger=None, level=None) diff --git a/Doc/library/winreg.rst b/Doc/library/winreg.rst index 61056d41cf7..83c49876d26 100644 --- a/Doc/library/winreg.rst +++ b/Doc/library/winreg.rst @@ -188,7 +188,7 @@ This module offers the following functions: .. audit-event:: winreg.DeleteTree key,sub_key winreg.DeleteTree - .. versionadded:: next + .. versionadded:: 3.15 .. function:: DeleteValue(key, value) diff --git a/Doc/library/zipimport.rst b/Doc/library/zipimport.rst index 765e5cfd3bb..444c3d631a4 100644 --- a/Doc/library/zipimport.rst +++ b/Doc/library/zipimport.rst @@ -30,7 +30,7 @@ Any files may be present in the ZIP archive, but importers are only invoked for corresponding :file:`.pyc` file, meaning that if a ZIP archive doesn't contain :file:`.pyc` files, importing may be rather slow. -.. versionchanged:: next +.. versionchanged:: 3.15 Zstandard (*zstd*) compressed zip file entries are supported. .. versionchanged:: 3.13 diff --git a/Doc/library/zlib.rst b/Doc/library/zlib.rst index d4727f366d0..6a51320df70 100644 --- a/Doc/library/zlib.rst +++ b/Doc/library/zlib.rst @@ -56,7 +56,7 @@ The available exception and functions in this module are: that were concurrently computed. To compute checksums sequentially, use :func:`adler32` with the running checksum as the ``value`` argument. - .. versionadded:: next + .. versionadded:: 3.15 .. function:: compress(data, /, level=-1, wbits=MAX_WBITS) @@ -162,7 +162,7 @@ The available exception and functions in this module are: that were concurrently computed. To compute checksums sequentially, use :func:`crc32` with the running checksum as the ``value`` argument. - .. versionadded:: next + .. versionadded:: 3.15 .. function:: decompress(data, /, wbits=MAX_WBITS, bufsize=DEF_BUF_SIZE) diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index 773fdabe99e..aff165191b7 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -512,7 +512,7 @@ Miscellaneous options See :ref:`warning-filter` and :ref:`describing-warning-filters` for more details. - .. versionchanged:: next + .. versionchanged:: 3.15 Added regular expression support for *message* and *module*. @@ -989,7 +989,7 @@ conflict. See :ref:`warning-filter` and :ref:`describing-warning-filters` for more details. - .. versionchanged:: next + .. versionchanged:: 3.15 Added regular expression support for *message* and *module*. diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index 76624862633..0b98cfb8d27 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -1060,7 +1060,7 @@ UTF-8 mode ========== .. versionadded:: 3.7 -.. versionchanged:: next +.. versionchanged:: 3.15 Python UTF-8 mode is now enabled by default (:pep:`686`). diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 63ebbb32d06..640da6aa6fd 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -24,10 +24,10 @@ #define PY_MINOR_VERSION 15 #define PY_MICRO_VERSION 0 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA -#define PY_RELEASE_SERIAL 0 +#define PY_RELEASE_SERIAL 1 /* Version as a string */ -#define PY_VERSION "3.15.0a0" +#define PY_VERSION "3.15.0a1" /*--end constants--*/ diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index 5f7e14a79d3..293c3189589 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,4 +1,4 @@ -# Autogenerated by Sphinx on Tue May 6 18:33:44 2025 +# Autogenerated by Sphinx on Tue Oct 14 13:46:01 2025 # as part of the release process. topics = { @@ -435,9 +435,9 @@ async def func(param1, param2): 'atom-identifiers': r'''Identifiers (Names) ******************* -An identifier occurring as an atom is a name. See section Identifiers -and keywords for lexical definition and section Naming and binding for -documentation of naming and binding. +An identifier occurring as an atom is a name. See section Names +(identifiers and keywords) for lexical definition and section Naming +and binding for documentation of naming and binding. When the name is bound to an object, evaluation of the atom yields that object. When a name is not bound, an attempt to evaluate it @@ -492,19 +492,65 @@ async def func(param1, param2): Python supports string and bytes literals and various numeric literals: - literal: stringliteral | bytesliteral - | integer | floatnumber | imagnumber + literal: strings | NUMBER Evaluation of a literal yields an object of the given type (string, bytes, integer, floating-point number, complex number) with the given value. The value may be approximated in the case of floating-point -and imaginary (complex) literals. See section Literals for details. +and imaginary (complex) literals. See section Literals for details. +See section String literal concatenation for details on "strings". All literals correspond to immutable data types, and hence the object’s identity is less important than its value. Multiple evaluations of literals with the same value (either the same occurrence in the program text or a different occurrence) may obtain the same object or a different object with the same value. + + +String literal concatenation +============================ + +Multiple adjacent string or bytes literals (delimited by whitespace), +possibly using different quoting conventions, are allowed, and their +meaning is the same as their concatenation: + + >>> "hello" 'world' + "helloworld" + +Formally: + + strings: ( STRING | fstring)+ | tstring+ + +This feature is defined at the syntactical level, so it only works +with literals. To concatenate string expressions at run time, the ‘+’ +operator may be used: + + >>> greeting = "Hello" + >>> space = " " + >>> name = "Blaise" + >>> print(greeting + space + name) # not: print(greeting space name) + Hello Blaise + +Literal concatenation can freely mix raw strings, triple-quoted +strings, and formatted string literals. For example: + + >>> "Hello" r', ' f"{name}!" + "Hello, Blaise!" + +This feature can be used to reduce the number of backslashes needed, +to split long strings conveniently across long lines, or even to add +comments to parts of strings. For example: + + re.compile("[A-Za-z_]" # letter or underscore + "[A-Za-z0-9_]*" # letter, digit or underscore + ) + +However, bytes literals may only be combined with other byte literals; +not with string literals of any kind. Also, template string literals +may only be combined with other template string literals: + + >>> t"Hello" t"{name}!" + Template(strings=('Hello', '!'), interpolations=(...)) ''', 'attribute-access': r'''Customizing attribute access **************************** @@ -588,6 +634,9 @@ class instances. Customizing module attribute access =================================== +module.__getattr__() +module.__dir__() + Special names "__getattr__" and "__dir__" can be also used to customize access to module attributes. The "__getattr__" function at the module level should accept one argument which is the name of an @@ -603,6 +652,8 @@ class instances. present, this function overrides the standard "dir()" search on a module. +module.__class__ + For a more fine grained customization of the module behavior (setting attributes, properties, etc.), one can set the "__class__" attribute of a module object to a subclass of "types.ModuleType". For example: @@ -1047,12 +1098,33 @@ class and instance attributes applies as for regular assignments. 'bltin-ellipsis-object': r'''The Ellipsis Object ******************* -This object is commonly used by slicing (see Slicings). It supports -no special operations. There is exactly one ellipsis object, named -"Ellipsis" (a built-in name). "type(Ellipsis)()" produces the -"Ellipsis" singleton. +This object is commonly used used to indicate that something is +omitted. It supports no special operations. There is exactly one +ellipsis object, named "Ellipsis" (a built-in name). +"type(Ellipsis)()" produces the "Ellipsis" singleton. It is written as "Ellipsis" or "...". + +In typical use, "..." as the "Ellipsis" object appears in a few +different places, for instance: + +* In type annotations, such as callable arguments or tuple elements. + +* As the body of a function instead of a pass statement. + +* In third-party libraries, such as Numpy’s slicing and striding. + +Python also uses three dots in ways that are not "Ellipsis" objects, +for instance: + +* Doctest’s "ELLIPSIS", as a pattern for missing content. + +* The default Python prompt of the *interactive* shell when partial + input is incomplete. + +Lastly, the Python documentation often uses three dots in conventional +English usage to mean omitted content, even in code examples that also +use them as the "Ellipsis". ''', 'bltin-null-object': r'''The Null Object *************** @@ -1314,6 +1386,9 @@ class Foo: class Foo(object): pass +There may be one or more base classes; see Multiple inheritance below +for more information. + The class’s suite is then executed in a new execution frame (see Naming and binding), using a newly created local namespace and the original global namespace. (Usually, the suite contains mostly @@ -1377,6 +1452,115 @@ class attributes; they are shared by instances. Instance attributes **PEP 3129** - Class Decorators The proposal that added class decorators. Function and method decorators were introduced in **PEP 318**. + + +Multiple inheritance +==================== + +Python classes may have multiple base classes, a technique known as +*multiple inheritance*. The base classes are specified in the class +definition by listing them in parentheses after the class name, +separated by commas. For example, the following class definition: + + >>> class A: pass + >>> class B: pass + >>> class C(A, B): pass + +defines a class "C" that inherits from classes "A" and "B". + +The *method resolution order* (MRO) is the order in which base classes +are searched when looking up an attribute on a class. See The Python +2.3 Method Resolution Order for a description of how Python determines +the MRO for a class. + +Multiple inheritance is not always allowed. Attempting to define a +class with multiple inheritance will raise an error if one of the +bases does not allow subclassing, if a consistent MRO cannot be +created, if no valid metaclass can be determined, or if there is an +instance layout conflict. We’ll discuss each of these in turn. + +First, all base classes must allow subclassing. While most classes +allow subclassing, some built-in classes do not, such as "bool": + + >>> class SubBool(bool): # TypeError + ... pass + Traceback (most recent call last): + ... + TypeError: type 'bool' is not an acceptable base type + +In the resolved MRO of a class, the class’s bases appear in the order +they were specified in the class’s bases list. Additionally, the MRO +always lists a child class before any of its bases. A class definition +will fail if it is impossible to resolve a consistent MRO that +satisfies these rules from the list of bases provided: + + >>> class Base: pass + >>> class Child(Base): pass + >>> class Grandchild(Base, Child): pass # TypeError + Traceback (most recent call last): + ... + TypeError: Cannot create a consistent method resolution order (MRO) for bases Base, Child + +In the MRO of "Grandchild", "Base" must appear before "Child" because +it is first in the base class list, but it must also appear after +"Child" because it is a parent of "Child". This is a contradiction, so +the class cannot be defined. + +If some of the bases have a custom *metaclass*, the metaclass of the +resulting class is chosen among the metaclasses of the bases and the +explicitly specified metaclass of the child class. It must be a +metaclass that is a subclass of all other candidate metaclasses. If no +such metaclass exists among the candidates, the class cannot be +created, as explained in Determining the appropriate metaclass. + +Finally, the instance layouts of the bases must be compatible. This +means that it must be possible to compute a *solid base* for the +class. Exactly which classes are solid bases depends on the Python +implementation. + +**CPython implementation detail:** In CPython, a class is a solid base +if it has a nonempty "__slots__" definition. Many but not all classes +defined in C are also solid bases, including most builtins (such as +"int" or "BaseException") but excluding most concrete "Exception" +classes. Generally, a C class is a solid base if its underlying struct +is different in size from its base class. + +Every class has a solid base. "object", the base class, has itself as +its solid base. If there is a single base, the child class’s solid +base is that class if it is a solid base, or else the base class’s +solid base. If there are multiple bases, we first find the solid base +for each base class to produce a list of candidate solid bases. If +there is a unique solid base that is a subclass of all others, then +that class is the solid base. Otherwise, class creation fails. + +Example: + + >>> class Solid1: + ... __slots__ = ("solid1",) + >>> + >>> class Solid2: + ... __slots__ = ("solid2",) + >>> + >>> class SolidChild(Solid1): + ... __slots__ = ("solid_child",) + >>> + >>> class C1: # solid base is `object` + ... pass + >>> + >>> # OK: solid bases are `Solid1` and `object`, and `Solid1` is a subclass of `object`. + >>> class C2(Solid1, C1): # solid base is `Solid1` + ... pass + >>> + >>> # OK: solid bases are `SolidChild` and `Solid1`, and `SolidChild` is a subclass of `Solid1`. + >>> class C3(SolidChild, Solid1): # solid base is `SolidChild` + ... pass + >>> + >>> # Error: solid bases are `Solid1` and `Solid2`, but neither is a subclass of the other. + >>> class C4(Solid1, Solid2): # error: no single solid base + ... pass + Traceback (most recent call last): + ... + TypeError: multiple bases have instance lay-out conflict ''', 'comparisons': r'''Comparisons *********** @@ -1724,16 +1908,16 @@ class attributes; they are shared by instances. Instance attributes The "for" statement is used to iterate over the elements of a sequence (such as a string, tuple or list) or other iterable object: - for_stmt: "for" target_list "in" starred_list ":" suite + for_stmt: "for" target_list "in" starred_expression_list ":" suite ["else" ":" suite] -The "starred_list" expression is evaluated once; it should yield an -*iterable* object. An *iterator* is created for that iterable. The -first item provided by the iterator is then assigned to the target -list using the standard rules for assignments (see Assignment -statements), and the suite is executed. This repeats for each item -provided by the iterator. When the iterator is exhausted, the suite -in the "else" clause, if present, is executed, and the loop +The "starred_expression_list" expression is evaluated once; it should +yield an *iterable* object. An *iterator* is created for that +iterable. The first item provided by the iterator is then assigned to +the target list using the standard rules for assignments (see +Assignment statements), and the suite is executed. This repeats for +each item provided by the iterator. When the iterator is exhausted, +the suite in the "else" clause, if present, is executed, and the loop terminates. A "break" statement executed in the first suite terminates the loop @@ -1874,15 +2058,29 @@ class attributes; they are shared by instances. Instance attributes "except*" clause ---------------- -The "except*" clause(s) are used for handling "ExceptionGroup"s. The -exception type for matching is interpreted as in the case of "except", -but in the case of exception groups we can have partial matches when -the type matches some of the exceptions in the group. This means that -multiple "except*" clauses can execute, each handling part of the -exception group. Each clause executes at most once and handles an -exception group of all matching exceptions. Each exception in the -group is handled by at most one "except*" clause, the first that -matches it. +The "except*" clause(s) specify one or more handlers for groups of +exceptions ("BaseExceptionGroup" instances). A "try" statement can +have either "except" or "except*" clauses, but not both. The exception +type for matching is mandatory in the case of "except*", so "except*:" +is a syntax error. The type is interpreted as in the case of "except", +but matching is performed on the exceptions contained in the group +that is being handled. An "TypeError" is raised if a matching type is +a subclass of "BaseExceptionGroup", because that would have ambiguous +semantics. + +When an exception group is raised in the try block, each "except*" +clause splits (see "split()") it into the subgroups of matching and +non-matching exceptions. If the matching subgroup is not empty, it +becomes the handled exception (the value returned from +"sys.exception()") and assigned to the target of the "except*" clause +(if there is one). Then, the body of the "except*" clause executes. If +the non-matching subgroup is not empty, it is processed by the next +"except*" in the same manner. This continues until all exceptions in +the group have been matched, or the last "except*" clause has run. + +After all "except*" clauses execute, the group of unhandled exceptions +is merged with any exceptions that were raised or re-raised from +within "except*" clauses. This merged exception group propagates on.: >>> try: ... raise ExceptionGroup("eg", @@ -1895,20 +2093,19 @@ class attributes; they are shared by instances. Instance attributes caught with nested (TypeError(2),) caught with nested (OSError(3), OSError(4)) + Exception Group Traceback (most recent call last): - | File "", line 2, in - | ExceptionGroup: eg + | File "", line 2, in + | raise ExceptionGroup("eg", + | [ValueError(1), TypeError(2), OSError(3), OSError(4)]) + | ExceptionGroup: eg (1 sub-exception) +-+---------------- 1 ---------------- | ValueError: 1 +------------------------------------ -Any remaining exceptions that were not handled by any "except*" clause -are re-raised at the end, along with all exceptions that were raised -from within the "except*" clauses. If this list contains more than one -exception to reraise, they are combined into an exception group. - -If the raised exception is not an exception group and its type matches -one of the "except*" clauses, it is caught and wrapped by an exception -group with an empty message string. +If the exception raised from the "try" block is not an exception group +and its type matches one of the "except*" clauses, it is caught and +wrapped by an exception group with an empty message string. This +ensures that the type of the target "e" is consistently +"BaseExceptionGroup": >>> try: ... raise BlockingIOError @@ -1917,11 +2114,6 @@ class attributes; they are shared by instances. Instance attributes ... ExceptionGroup('', (BlockingIOError())) -An "except*" clause must have a matching expression; it cannot be -"except*:". Furthermore, this expression cannot contain exception -group types, because that would have ambiguous semantics. - -It is not possible to mix "except" and "except*" in the same "try". "break", "continue" and "return" cannot appear in an "except*" clause. @@ -1938,11 +2130,11 @@ class attributes; they are shared by instances. Instance attributes ---------------- If "finally" is present, it specifies a ‘cleanup’ handler. The "try" -clause is executed, including any "except" and "else" clauses. If an +clause is executed, including any "except" and "else" clauses. If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. The "finally" clause is executed. If there is a saved exception it is re-raised at the end of the "finally" -clause. If the "finally" clause raises another exception, the saved +clause. If the "finally" clause raises another exception, the saved exception is set as the context of the new exception. If the "finally" clause executes a "return", "break" or "continue" statement, the saved exception is discarded. For example, this function returns 42. @@ -2097,9 +2289,9 @@ def foo(): The match statement is used for pattern matching. Syntax: match_stmt: 'match' subject_expr ":" NEWLINE INDENT case_block+ DEDENT - subject_expr: star_named_expression "," star_named_expressions? - | named_expression - case_block: 'case' patterns [guard] ":" block + subject_expr: `!star_named_expression` "," `!star_named_expressions`? + | `!named_expression` + case_block: 'case' patterns [guard] ":" `!block` Note: @@ -2190,7 +2382,7 @@ def foo(): Guards ------ - guard: "if" named_expression + guard: "if" `!named_expression` A "guard" (which is part of the "case") must succeed for code inside the "case" block to execute. It takes the form: "if" followed by an @@ -2329,7 +2521,8 @@ def foo(): The rule "strings" and the token "NUMBER" are defined in the standard Python grammar. Triple-quoted strings are supported. Raw strings and -byte strings are supported. f-strings are not supported. +byte strings are supported. f-strings and t-strings are not +supported. The forms "signed_number '+' NUMBER" and "signed_number '-' NUMBER" are for expressing complex numbers; they require a real number on the @@ -2483,7 +2676,7 @@ def foo(): Note: The length of the subject sequence is obtained via "len()" (i.e. - via the "__len__()" protocol). This length may be cached by the + via the "__len__()" protocol). This length may be cached by the interpreter in a similar manner as value patterns. In simple terms "[P1, P2, P3," … ", P]" matches only if all the @@ -2866,6 +3059,9 @@ class Foo: class Foo(object): pass +There may be one or more base classes; see Multiple inheritance below +for more information. + The class’s suite is then executed in a new execution frame (see Naming and binding), using a newly created local namespace and the original global namespace. (Usually, the suite contains mostly @@ -2931,6 +3127,115 @@ class attributes; they are shared by instances. Instance attributes decorators were introduced in **PEP 318**. +Multiple inheritance +-------------------- + +Python classes may have multiple base classes, a technique known as +*multiple inheritance*. The base classes are specified in the class +definition by listing them in parentheses after the class name, +separated by commas. For example, the following class definition: + + >>> class A: pass + >>> class B: pass + >>> class C(A, B): pass + +defines a class "C" that inherits from classes "A" and "B". + +The *method resolution order* (MRO) is the order in which base classes +are searched when looking up an attribute on a class. See The Python +2.3 Method Resolution Order for a description of how Python determines +the MRO for a class. + +Multiple inheritance is not always allowed. Attempting to define a +class with multiple inheritance will raise an error if one of the +bases does not allow subclassing, if a consistent MRO cannot be +created, if no valid metaclass can be determined, or if there is an +instance layout conflict. We’ll discuss each of these in turn. + +First, all base classes must allow subclassing. While most classes +allow subclassing, some built-in classes do not, such as "bool": + + >>> class SubBool(bool): # TypeError + ... pass + Traceback (most recent call last): + ... + TypeError: type 'bool' is not an acceptable base type + +In the resolved MRO of a class, the class’s bases appear in the order +they were specified in the class’s bases list. Additionally, the MRO +always lists a child class before any of its bases. A class definition +will fail if it is impossible to resolve a consistent MRO that +satisfies these rules from the list of bases provided: + + >>> class Base: pass + >>> class Child(Base): pass + >>> class Grandchild(Base, Child): pass # TypeError + Traceback (most recent call last): + ... + TypeError: Cannot create a consistent method resolution order (MRO) for bases Base, Child + +In the MRO of "Grandchild", "Base" must appear before "Child" because +it is first in the base class list, but it must also appear after +"Child" because it is a parent of "Child". This is a contradiction, so +the class cannot be defined. + +If some of the bases have a custom *metaclass*, the metaclass of the +resulting class is chosen among the metaclasses of the bases and the +explicitly specified metaclass of the child class. It must be a +metaclass that is a subclass of all other candidate metaclasses. If no +such metaclass exists among the candidates, the class cannot be +created, as explained in Determining the appropriate metaclass. + +Finally, the instance layouts of the bases must be compatible. This +means that it must be possible to compute a *solid base* for the +class. Exactly which classes are solid bases depends on the Python +implementation. + +**CPython implementation detail:** In CPython, a class is a solid base +if it has a nonempty "__slots__" definition. Many but not all classes +defined in C are also solid bases, including most builtins (such as +"int" or "BaseException") but excluding most concrete "Exception" +classes. Generally, a C class is a solid base if its underlying struct +is different in size from its base class. + +Every class has a solid base. "object", the base class, has itself as +its solid base. If there is a single base, the child class’s solid +base is that class if it is a solid base, or else the base class’s +solid base. If there are multiple bases, we first find the solid base +for each base class to produce a list of candidate solid bases. If +there is a unique solid base that is a subclass of all others, then +that class is the solid base. Otherwise, class creation fails. + +Example: + + >>> class Solid1: + ... __slots__ = ("solid1",) + >>> + >>> class Solid2: + ... __slots__ = ("solid2",) + >>> + >>> class SolidChild(Solid1): + ... __slots__ = ("solid_child",) + >>> + >>> class C1: # solid base is `object` + ... pass + >>> + >>> # OK: solid bases are `Solid1` and `object`, and `Solid1` is a subclass of `object`. + >>> class C2(Solid1, C1): # solid base is `Solid1` + ... pass + >>> + >>> # OK: solid bases are `SolidChild` and `Solid1`, and `SolidChild` is a subclass of `Solid1`. + >>> class C3(SolidChild, Solid1): # solid base is `SolidChild` + ... pass + >>> + >>> # Error: solid bases are `Solid1` and `Solid2`, but neither is a subclass of the other. + >>> class C4(Solid1, Solid2): # error: no single solid base + ... pass + Traceback (most recent call last): + ... + TypeError: multiple bases have instance lay-out conflict + + Coroutines ========== @@ -3304,7 +3609,7 @@ def f() -> annotation: ... introspects and uses the annotations (such as "dataclasses" or "functools.singledispatch()"). -By default, annotations are lazily evaluated in a annotation scope. +By default, annotations are lazily evaluated in an annotation scope. This means that they are not evaluated when the code containing the annotation is evaluated. Instead, the interpreter saves information that can be used to evaluate the annotation later if requested. The @@ -3318,6 +3623,12 @@ def f() -> annotation: ... >>> f.__annotations__ {'param': 'annotation'} +This future statement will be deprecated and removed in a future +version of Python, but not before Python 3.13 reaches its end of life +(see **PEP 749**). When it is used, introspection tools like +"annotationlib.get_annotations()" and "typing.get_type_hints()" are +less likely to be able to resolve annotations at runtime. + -[ Footnotes ]- [1] The exception is propagated to the invocation stack unless there @@ -3832,7 +4143,7 @@ def double(x): You can also invoke "pdb" from the command line to debug other scripts. For example: - python -m pdb [-c command] (-m module | pyfile) [args ...] + python -m pdb [-c command] (-m module | -p pid | pyfile) [args ...] When invoked as a module, pdb will automatically enter post-mortem debugging if the program being debugged exits abnormally. After post- @@ -3856,6 +4167,23 @@ def double(x): Changed in version 3.7: Added the "-m" option. +-p, --pid + + Attach to the process with the specified PID. + + Added in version 3.14. + +To attach to a running Python process for remote debugging, use the +"-p" or "--pid" option with the target process’s PID: + + python -m pdb -p 1234 + +Note: + + Attaching to a process that is blocked in a system call or waiting + for I/O will only work once the next bytecode instruction is + executed or when the process receives a signal. + Typical usage to execute a statement under control of the debugger is: >>> import pdb @@ -4574,8 +4902,8 @@ def inner(x): Deletion of a name removes the binding of that name from the local or global namespace, depending on whether the name occurs in a "global" -statement in the same code block. If the name is unbound, a -"NameError" exception will be raised. +statement in the same code block. Trying to delete an unbound name +raises a "NameError" exception. Deletion of attribute references, subscriptions and slicings is passed to the primary object involved; deletion of a slicing is in general @@ -4711,11 +5039,6 @@ class of the instance or a *non-virtual base class* thereof. The See also the description of the "try" statement in section The try statement and "raise" statement in section The raise statement. - --[ Footnotes ]- - -[1] This limitation occurs because the code that is executed by these - operations is not available at the time the module is compiled. ''', 'execmodel': r'''Execution model *************** @@ -5069,6 +5392,181 @@ class of the instance or a *non-virtual base class* thereof. The See also the description of the "try" statement in section The try statement and "raise" statement in section The raise statement. + +Runtime Components +================== + + +General Computing Model +----------------------- + +Python’s execution model does not operate in a vacuum. It runs on a +host machine and through that host’s runtime environment, including +its operating system (OS), if there is one. When a program runs, the +conceptual layers of how it runs on the host look something like this: + + **host machine** + **process** (global resources) + **thread** (runs machine code) + +Each process represents a program running on the host. Think of each +process itself as the data part of its program. Think of the process’ +threads as the execution part of the program. This distinction will +be important to understand the conceptual Python runtime. + +The process, as the data part, is the execution context in which the +program runs. It mostly consists of the set of resources assigned to +the program by the host, including memory, signals, file handles, +sockets, and environment variables. + +Processes are isolated and independent from one another. (The same is +true for hosts.) The host manages the process’ access to its assigned +resources, in addition to coordinating between processes. + +Each thread represents the actual execution of the program’s machine +code, running relative to the resources assigned to the program’s +process. It’s strictly up to the host how and when that execution +takes place. + +From the point of view of Python, a program always starts with exactly +one thread. However, the program may grow to run in multiple +simultaneous threads. Not all hosts support multiple threads per +process, but most do. Unlike processes, threads in a process are not +isolated and independent from one another. Specifically, all threads +in a process share all of the process’ resources. + +The fundamental point of threads is that each one does *run* +independently, at the same time as the others. That may be only +conceptually at the same time (“concurrently”) or physically (“in +parallel”). Either way, the threads effectively run at a non- +synchronized rate. + +Note: + + That non-synchronized rate means none of the process’ memory is + guaranteed to stay consistent for the code running in any given + thread. Thus multi-threaded programs must take care to coordinate + access to intentionally shared resources. Likewise, they must take + care to be absolutely diligent about not accessing any *other* + resources in multiple threads; otherwise two threads running at the + same time might accidentally interfere with each other’s use of some + shared data. All this is true for both Python programs and the + Python runtime.The cost of this broad, unstructured requirement is + the tradeoff for the kind of raw concurrency that threads provide. + The alternative to the required discipline generally means dealing + with non-deterministic bugs and data corruption. + + +Python Runtime Model +-------------------- + +The same conceptual layers apply to each Python program, with some +extra data layers specific to Python: + + **host machine** + **process** (global resources) + Python global runtime (*state*) + Python interpreter (*state*) + **thread** (runs Python bytecode and “C-API”) + Python thread *state* + +At the conceptual level: when a Python program starts, it looks +exactly like that diagram, with one of each. The runtime may grow to +include multiple interpreters, and each interpreter may grow to +include multiple thread states. + +Note: + + A Python implementation won’t necessarily implement the runtime + layers distinctly or even concretely. The only exception is places + where distinct layers are directly specified or exposed to users, + like through the "threading" module. + +Note: + + The initial interpreter is typically called the “main” interpreter. + Some Python implementations, like CPython, assign special roles to + the main interpreter.Likewise, the host thread where the runtime was + initialized is known as the “main” thread. It may be different from + the process’ initial thread, though they are often the same. In + some cases “main thread” may be even more specific and refer to the + initial thread state. A Python runtime might assign specific + responsibilities to the main thread, such as handling signals. + +As a whole, the Python runtime consists of the global runtime state, +interpreters, and thread states. The runtime ensures all that state +stays consistent over its lifetime, particularly when used with +multiple host threads. + +The global runtime, at the conceptual level, is just a set of +interpreters. While those interpreters are otherwise isolated and +independent from one another, they may share some data or other +resources. The runtime is responsible for managing these global +resources safely. The actual nature and management of these resources +is implementation-specific. Ultimately, the external utility of the +global runtime is limited to managing interpreters. + +In contrast, an “interpreter” is conceptually what we would normally +think of as the (full-featured) “Python runtime”. When machine code +executing in a host thread interacts with the Python runtime, it calls +into Python in the context of a specific interpreter. + +Note: + + The term “interpreter” here is not the same as the “bytecode + interpreter”, which is what regularly runs in threads, executing + compiled Python code.In an ideal world, “Python runtime” would refer + to what we currently call “interpreter”. However, it’s been called + “interpreter” at least since introduced in 1997 (CPython:a027efa5b). + +Each interpreter completely encapsulates all of the non-process- +global, non-thread-specific state needed for the Python runtime to +work. Notably, the interpreter’s state persists between uses. It +includes fundamental data like "sys.modules". The runtime ensures +multiple threads using the same interpreter will safely share it +between them. + +A Python implementation may support using multiple interpreters at the +same time in the same process. They are independent and isolated from +one another. For example, each interpreter has its own "sys.modules". + +For thread-specific runtime state, each interpreter has a set of +thread states, which it manages, in the same way the global runtime +contains a set of interpreters. It can have thread states for as many +host threads as it needs. It may even have multiple thread states for +the same host thread, though that isn’t as common. + +Each thread state, conceptually, has all the thread-specific runtime +data an interpreter needs to operate in one host thread. The thread +state includes the current raised exception and the thread’s Python +call stack. It may include other thread-specific resources. + +Note: + + The term “Python thread” can sometimes refer to a thread state, but + normally it means a thread created using the "threading" module. + +Each thread state, over its lifetime, is always tied to exactly one +interpreter and exactly one host thread. It will only ever be used in +that thread and with that interpreter. + +Multiple thread states may be tied to the same host thread, whether +for different interpreters or even the same interpreter. However, for +any given host thread, only one of the thread states tied to it can be +used by the thread at a time. + +Thread states are isolated and independent from one another and don’t +share any data, except for possibly sharing an interpreter and objects +or other resources belonging to that interpreter. + +Once a program is running, new Python threads can be created using the +"threading" module (on platforms and Python implementations that +support threads). Additional processes can be created using the "os", +"subprocess", and "multiprocessing" modules. Interpreters can be +created and used with the "interpreters" module. Coroutines (async) +can be run using "asyncio" in each interpreter, typically only in a +single thread (often the main thread). + -[ Footnotes ]- [1] This limitation occurs because the code that is executed by these @@ -5077,7 +5575,7 @@ class of the instance or a *non-virtual base class* thereof. The 'exprlists': r'''Expression lists **************** - starred_expression: ["*"] or_expr + starred_expression: "*" or_expr | expression flexible_expression: assignment_expression | starred_expression flexible_expression_list: flexible_expression ("," flexible_expression)* [","] starred_expression_list: starred_expression ("," starred_expression)* [","] @@ -5109,25 +5607,54 @@ class of the instance or a *non-virtual base class* thereof. The 'floating': r'''Floating-point literals *********************** -Floating-point literals are described by the following lexical -definitions: +Floating-point (float) literals, such as "3.14" or "1.5", denote +approximations of real numbers. - floatnumber: pointfloat | exponentfloat - pointfloat: [digitpart] fraction | digitpart "." - exponentfloat: (digitpart | pointfloat) exponent - digitpart: digit (["_"] digit)* - fraction: "." digitpart - exponent: ("e" | "E") ["+" | "-"] digitpart +They consist of *integer* and *fraction* parts, each composed of +decimal digits. The parts are separated by a decimal point, ".": -Note that the integer and exponent parts are always interpreted using -radix 10. For example, "077e010" is legal, and denotes the same number -as "77e10". The allowed range of floating-point literals is -implementation-dependent. As in integer literals, underscores are -supported for digit grouping. + 2.71828 + 4.0 -Some examples of floating-point literals: +Unlike in integer literals, leading zeros are allowed in the numeric +parts. For example, "077.010" is legal, and denotes the same number as +"77.10". - 3.14 10. .001 1e100 3.14e-10 0e0 3.14_15_93 +As in integer literals, single underscores may occur between digits to +help readability: + + 96_485.332_123 + 3.14_15_93 + +Either of these parts, but not both, can be empty. For example: + + 10. # (equivalent to 10.0) + .001 # (equivalent to 0.001) + +Optionally, the integer and fraction may be followed by an *exponent*: +the letter "e" or "E", followed by an optional sign, "+" or "-", and a +number in the same format as the integer and fraction parts. The "e" +or "E" represents “times ten raised to the power of”: + + 1.0e3 # (represents 1.0×10³, or 1000.0) + 1.166e-5 # (represents 1.166×10⁻⁵, or 0.00001166) + 6.02214076e+23 # (represents 6.02214076×10²³, or 602214076000000000000000.) + +In floats with only integer and exponent parts, the decimal point may +be omitted: + + 1e3 # (equivalent to 1.e3 and 1.0e3) + 0e0 # (equivalent to 0.) + +Formally, floating-point literals are described by the following +lexical definitions: + + floatnumber: + | digitpart "." [digitpart] [exponent] + | "." digitpart [exponent] + | digitpart exponent + digitpart: digit (["_"] digit)* + exponent: ("e" | "E") ["+" | "-"] digitpart Changed in version 3.6: Underscores are now allowed for grouping purposes in literals. @@ -5138,16 +5665,16 @@ class of the instance or a *non-virtual base class* thereof. The The "for" statement is used to iterate over the elements of a sequence (such as a string, tuple or list) or other iterable object: - for_stmt: "for" target_list "in" starred_list ":" suite + for_stmt: "for" target_list "in" starred_expression_list ":" suite ["else" ":" suite] -The "starred_list" expression is evaluated once; it should yield an -*iterable* object. An *iterator* is created for that iterable. The -first item provided by the iterator is then assigned to the target -list using the standard rules for assignments (see Assignment -statements), and the suite is executed. This repeats for each item -provided by the iterator. When the iterator is exhausted, the suite -in the "else" clause, if present, is executed, and the loop +The "starred_expression_list" expression is evaluated once; it should +yield an *iterable* object. An *iterator* is created for that +iterable. The first item provided by the iterator is then assigned to +the target list using the standard rules for assignments (see +Assignment statements), and the suite is executed. This repeats for +each item provided by the iterator. When the iterator is exhausted, +the suite in the "else" clause, if present, is executed, and the loop terminates. A "break" statement executed in the first suite terminates the loop @@ -5181,9 +5708,9 @@ class of the instance or a *non-virtual base class* thereof. The The "str.format()" method and the "Formatter" class share the same syntax for format strings (although in the case of "Formatter", subclasses can define their own format string syntax). The syntax is -related to that of formatted string literals, but it is less -sophisticated and, in particular, does not support arbitrary -expressions. +related to that of formatted string literals and template string +literals, but it is less sophisticated and, in particular, does not +support arbitrary expressions in interpolations. Format strings contain “replacement fields” surrounded by curly braces "{}". Anything that is not contained in braces is considered literal @@ -5283,9 +5810,9 @@ class of the instance or a *non-virtual base class* thereof. The “Format specifications” are used within replacement fields contained within a format string to define how individual values are presented -(see Format String Syntax and f-strings). They can also be passed -directly to the built-in "format()" function. Each formattable type -may define how the format specification is to be interpreted. +(see Format String Syntax, f-strings, and t-strings). They can also be +passed directly to the built-in "format()" function. Each formattable +type may define how the format specification is to be interpreted. Most built-in types implement the following options for format specifications, although some of the formatting options are only @@ -5304,7 +5831,7 @@ class of the instance or a *non-virtual base class* thereof. The sign: "+" | "-" | " " width_and_precision: [width_with_grouping][precision_with_grouping] width_with_grouping: [width][grouping] - precision_with_grouping: "." [precision][grouping] + precision_with_grouping: "." [precision][grouping] | "." grouping width: digit+ precision: digit+ grouping: "," | "_" @@ -5886,9 +6413,15 @@ def whats_on_the_telly(penguin=None): without "global", although free variables may refer to globals without being declared global. -The "global" statement applies to the entire scope of a function or -class body. A "SyntaxError" is raised if a variable is used or -assigned to prior to its global declaration in the scope. +The "global" statement applies to the entire current scope (module, +function body or class definition). A "SyntaxError" is raised if a +variable is used or assigned to prior to its global declaration in the +scope. + +At the module level, all variables are global, so a "global" statement +has no effect. However, variables must still not be used or assigned +to prior to their "global" declaration. This requirement is relaxed in +the interactive prompt (*REPL*). **Programmer’s note:** "global" is a directive to the parser. It applies only to code parsed at the same time as the "global" @@ -5942,73 +6475,92 @@ class body. A "SyntaxError" is raised if a variable is used or to help avoid name clashes between “private” attributes of base and derived classes. See section Identifiers (Names). ''', - 'identifiers': r'''Identifiers and keywords -************************ + 'identifiers': r'''Names (identifiers and keywords) +******************************** -Identifiers (also referred to as *names*) are described by the -following lexical definitions. - -The syntax of identifiers in Python is based on the Unicode standard -annex UAX-31, with elaboration and changes as defined below; see also -**PEP 3131** for further details. +"NAME" tokens represent *identifiers*, *keywords*, and *soft +keywords*. Within the ASCII range (U+0001..U+007F), the valid characters for -identifiers include the uppercase and lowercase letters "A" through -"Z", the underscore "_" and, except for the first character, the -digits "0" through "9". Python 3.0 introduced additional characters -from outside the ASCII range (see **PEP 3131**). For these -characters, the classification uses the version of the Unicode -Character Database as included in the "unicodedata" module. +names include the uppercase and lowercase letters ("A-Z" and "a-z"), +the underscore "_" and, except for the first character, the digits "0" +through "9". -Identifiers are unlimited in length. Case is significant. +Names must contain at least one character, but have no upper length +limit. Case is significant. - identifier: xid_start xid_continue* - id_start: - id_continue: - xid_start: - xid_continue: +Besides "A-Z", "a-z", "_" and "0-9", names can also use “letter-like” +and “number-like” characters from outside the ASCII range, as detailed +below. -The Unicode category codes mentioned above stand for: +All identifiers are converted into the normalization form NFKC while +parsing; comparison of identifiers is based on NFKC. -* *Lu* - uppercase letters +Formally, the first character of a normalized identifier must belong +to the set "id_start", which is the union of: -* *Ll* - lowercase letters +* Unicode category "" - uppercase letters (includes "A" to "Z") -* *Lt* - titlecase letters +* Unicode category "" - lowercase letters (includes "a" to "z") -* *Lm* - modifier letters +* Unicode category "" - titlecase letters -* *Lo* - other letters +* Unicode category "" - modifier letters -* *Nl* - letter numbers +* Unicode category "" - other letters -* *Mn* - nonspacing marks +* Unicode category "" - letter numbers -* *Mc* - spacing combining marks +* {""_""} - the underscore -* *Nd* - decimal numbers +* "" - an explicit set of characters in PropList.txt + to support backwards compatibility -* *Pc* - connector punctuations +The remaining characters must belong to the set "id_continue", which +is the union of: -* *Other_ID_Start* - explicit list of characters in PropList.txt to - support backwards compatibility +* all characters in "id_start" -* *Other_ID_Continue* - likewise +* Unicode category "" - decimal numbers (includes "0" to "9") -All identifiers are converted into the normal form NFKC while parsing; -comparison of identifiers is based on NFKC. +* Unicode category "" - connector punctuations -A non-normative HTML file listing all valid identifier characters for -Unicode 16.0.0 can be found at -https://www.unicode.org/Public/16.0.0/ucd/DerivedCoreProperties.txt +* Unicode category "" - nonspacing marks + +* Unicode category "" - spacing combining marks + +* "" - another explicit set of characters in + PropList.txt to support backwards compatibility + +Unicode categories use the version of the Unicode Character Database +as included in the "unicodedata" module. + +These sets are based on the Unicode standard annex UAX-31. See also +**PEP 3131** for further details. + +Even more formally, names are described by the following lexical +definitions: + + NAME: xid_start xid_continue* + id_start: | | | | | | "_" | + id_continue: id_start | | | | | + xid_start: + xid_continue: + identifier: + +A non-normative listing of all valid identifier characters as defined +by Unicode is available in the DerivedCoreProperties.txt file in the +Unicode Character Database. Keywords ======== -The following identifiers are used as reserved words, or *keywords* of -the language, and cannot be used as ordinary identifiers. They must -be spelled exactly as written here: +The following names are used as reserved words, or *keywords* of the +language, and cannot be used as ordinary identifiers. They must be +spelled exactly as written here: False await else import pass None break except in raise @@ -6024,18 +6576,20 @@ class body. A "SyntaxError" is raised if a variable is used or Added in version 3.10. -Some identifiers are only reserved under specific contexts. These are -known as *soft keywords*. The identifiers "match", "case", "type" and -"_" can syntactically act as keywords in certain contexts, but this -distinction is done at the parser level, not when tokenizing. +Some names are only reserved under specific contexts. These are known +as *soft keywords*: + +* "match", "case", and "_", when used in the "match" statement. + +* "type", when used in the "type" statement. + +These syntactically act as keywords in their specific contexts, but +this distinction is done at the parser level, not when tokenizing. As soft keywords, their use in the grammar is possible while still preserving compatibility with existing code that uses these names as identifier names. -"match", "case", and "_" are used in the "match" statement. "type" is -used in the "type" statement. - Changed in version 3.12: "type" is now a soft keyword. @@ -6101,17 +6655,53 @@ class body. A "SyntaxError" is raised if a variable is used or 'imaginary': r'''Imaginary literals ****************** -Imaginary literals are described by the following lexical definitions: +Python has complex number objects, but no complex literals. Instead, +*imaginary literals* denote complex numbers with a zero real part. + +For example, in math, the complex number 3+4.2*i* is written as the +real number 3 added to the imaginary number 4.2*i*. Python uses a +similar syntax, except the imaginary unit is written as "j" rather +than *i*: + + 3+4.2j + +This is an expression composed of the integer literal "3", the +operator ‘"+"’, and the imaginary literal "4.2j". Since these are +three separate tokens, whitespace is allowed between them: + + 3 + 4.2j + +No whitespace is allowed *within* each token. In particular, the "j" +suffix, may not be separated from the number before it. + +The number before the "j" has the same syntax as a floating-point +literal. Thus, the following are valid imaginary literals: + + 4.2j + 3.14j + 10.j + .001j + 1e100j + 3.14e-10j + 3.14_15_93j + +Unlike in a floating-point literal the decimal point can be omitted if +the imaginary number only has an integer part. The number is still +evaluated as a floating-point number, not an integer: + + 10j + 0j + 1000000000000000000000000j # equivalent to 1e+24j + +The "j" suffix is case-insensitive. That means you can use "J" +instead: + + 3.14J # equivalent to 3.14j + +Formally, imaginary literals are described by the following lexical +definition: imagnumber: (floatnumber | digitpart) ("j" | "J") - -An imaginary literal yields a complex number with a real part of 0.0. -Complex numbers are represented as a pair of floating-point numbers -and have the same restrictions on their range. To create a complex -number with a nonzero real part, add a floating-point number to it, -e.g., "(3+4j)". Some examples of imaginary literals: - - 3.14j 10.j 10j .001j 1e100j 3.14e-10j 3.14_15_93j ''', 'import': r'''The "import" statement ********************** @@ -6353,37 +6943,62 @@ class body. A "SyntaxError" is raised if a variable is used or 'integers': r'''Integer literals **************** -Integer literals are described by the following lexical definitions: +Integer literals denote whole numbers. For example: - integer: decinteger | bininteger | octinteger | hexinteger - decinteger: nonzerodigit (["_"] digit)* | "0"+ (["_"] "0")* + 7 + 3 + 2147483647 + +There is no limit for the length of integer literals apart from what +can be stored in available memory: + + 7922816251426433759354395033679228162514264337593543950336 + +Underscores can be used to group digits for enhanced readability, and +are ignored for determining the numeric value of the literal. For +example, the following literals are equivalent: + + 100_000_000_000 + 100000000000 + 1_00_00_00_00_000 + +Underscores can only occur between digits. For example, "_123", +"321_", and "123__321" are *not* valid literals. + +Integers can be specified in binary (base 2), octal (base 8), or +hexadecimal (base 16) using the prefixes "0b", "0o" and "0x", +respectively. Hexadecimal digits 10 through 15 are represented by +letters "A"-"F", case-insensitive. For example: + + 0b100110111 + 0b_1110_0101 + 0o177 + 0o377 + 0xdeadbeef + 0xDead_Beef + +An underscore can follow the base specifier. For example, "0x_1f" is a +valid literal, but "0_x1f" and "0x__1f" are not. + +Leading zeros in a non-zero decimal number are not allowed. For +example, "0123" is not a valid literal. This is for disambiguation +with C-style octal literals, which Python used before version 3.0. + +Formally, integer literals are described by the following lexical +definitions: + + integer: decinteger | bininteger | octinteger | hexinteger | zerointeger + decinteger: nonzerodigit (["_"] digit)* bininteger: "0" ("b" | "B") (["_"] bindigit)+ octinteger: "0" ("o" | "O") (["_"] octdigit)+ hexinteger: "0" ("x" | "X") (["_"] hexdigit)+ + zerointeger: "0"+ (["_"] "0")* nonzerodigit: "1"..."9" digit: "0"..."9" bindigit: "0" | "1" octdigit: "0"..."7" hexdigit: digit | "a"..."f" | "A"..."F" -There is no limit for the length of integer literals apart from what -can be stored in available memory. - -Underscores are ignored for determining the numeric value of the -literal. They can be used to group digits for enhanced readability. -One underscore can occur between digits, and after base specifiers -like "0x". - -Note that leading zeros in a non-zero decimal number are not allowed. -This is for disambiguation with C-style octal literals, which Python -used before version 3.0. - -Some examples of integer literals: - - 7 2147483647 0o177 0b100110111 - 3 79228162514264337593543950336 0o377 0xdeadbeef - 100_000_000_000 0b_1110_0101 - Changed in version 3.6: Underscores are now allowed for grouping purposes in literals. ''', @@ -6730,14 +7345,190 @@ class body. A "SyntaxError" is raised if a variable is used or 'numbers': r'''Numeric literals **************** -There are three types of numeric literals: integers, floating-point -numbers, and imaginary numbers. There are no complex literals -(complex numbers can be formed by adding a real number and an -imaginary number). +"NUMBER" tokens represent numeric literals, of which there are three +types: integers, floating-point numbers, and imaginary numbers. -Note that numeric literals do not include a sign; a phrase like "-1" -is actually an expression composed of the unary operator ‘"-"’ and the -literal "1". + NUMBER: integer | floatnumber | imagnumber + +The numeric value of a numeric literal is the same as if it were +passed as a string to the "int", "float" or "complex" class +constructor, respectively. Note that not all valid inputs for those +constructors are also valid literals. + +Numeric literals do not include a sign; a phrase like "-1" is actually +an expression composed of the unary operator ‘"-"’ and the literal +"1". + + +Integer literals +================ + +Integer literals denote whole numbers. For example: + + 7 + 3 + 2147483647 + +There is no limit for the length of integer literals apart from what +can be stored in available memory: + + 7922816251426433759354395033679228162514264337593543950336 + +Underscores can be used to group digits for enhanced readability, and +are ignored for determining the numeric value of the literal. For +example, the following literals are equivalent: + + 100_000_000_000 + 100000000000 + 1_00_00_00_00_000 + +Underscores can only occur between digits. For example, "_123", +"321_", and "123__321" are *not* valid literals. + +Integers can be specified in binary (base 2), octal (base 8), or +hexadecimal (base 16) using the prefixes "0b", "0o" and "0x", +respectively. Hexadecimal digits 10 through 15 are represented by +letters "A"-"F", case-insensitive. For example: + + 0b100110111 + 0b_1110_0101 + 0o177 + 0o377 + 0xdeadbeef + 0xDead_Beef + +An underscore can follow the base specifier. For example, "0x_1f" is a +valid literal, but "0_x1f" and "0x__1f" are not. + +Leading zeros in a non-zero decimal number are not allowed. For +example, "0123" is not a valid literal. This is for disambiguation +with C-style octal literals, which Python used before version 3.0. + +Formally, integer literals are described by the following lexical +definitions: + + integer: decinteger | bininteger | octinteger | hexinteger | zerointeger + decinteger: nonzerodigit (["_"] digit)* + bininteger: "0" ("b" | "B") (["_"] bindigit)+ + octinteger: "0" ("o" | "O") (["_"] octdigit)+ + hexinteger: "0" ("x" | "X") (["_"] hexdigit)+ + zerointeger: "0"+ (["_"] "0")* + nonzerodigit: "1"..."9" + digit: "0"..."9" + bindigit: "0" | "1" + octdigit: "0"..."7" + hexdigit: digit | "a"..."f" | "A"..."F" + +Changed in version 3.6: Underscores are now allowed for grouping +purposes in literals. + + +Floating-point literals +======================= + +Floating-point (float) literals, such as "3.14" or "1.5", denote +approximations of real numbers. + +They consist of *integer* and *fraction* parts, each composed of +decimal digits. The parts are separated by a decimal point, ".": + + 2.71828 + 4.0 + +Unlike in integer literals, leading zeros are allowed in the numeric +parts. For example, "077.010" is legal, and denotes the same number as +"77.10". + +As in integer literals, single underscores may occur between digits to +help readability: + + 96_485.332_123 + 3.14_15_93 + +Either of these parts, but not both, can be empty. For example: + + 10. # (equivalent to 10.0) + .001 # (equivalent to 0.001) + +Optionally, the integer and fraction may be followed by an *exponent*: +the letter "e" or "E", followed by an optional sign, "+" or "-", and a +number in the same format as the integer and fraction parts. The "e" +or "E" represents “times ten raised to the power of”: + + 1.0e3 # (represents 1.0×10³, or 1000.0) + 1.166e-5 # (represents 1.166×10⁻⁵, or 0.00001166) + 6.02214076e+23 # (represents 6.02214076×10²³, or 602214076000000000000000.) + +In floats with only integer and exponent parts, the decimal point may +be omitted: + + 1e3 # (equivalent to 1.e3 and 1.0e3) + 0e0 # (equivalent to 0.) + +Formally, floating-point literals are described by the following +lexical definitions: + + floatnumber: + | digitpart "." [digitpart] [exponent] + | "." digitpart [exponent] + | digitpart exponent + digitpart: digit (["_"] digit)* + exponent: ("e" | "E") ["+" | "-"] digitpart + +Changed in version 3.6: Underscores are now allowed for grouping +purposes in literals. + + +Imaginary literals +================== + +Python has complex number objects, but no complex literals. Instead, +*imaginary literals* denote complex numbers with a zero real part. + +For example, in math, the complex number 3+4.2*i* is written as the +real number 3 added to the imaginary number 4.2*i*. Python uses a +similar syntax, except the imaginary unit is written as "j" rather +than *i*: + + 3+4.2j + +This is an expression composed of the integer literal "3", the +operator ‘"+"’, and the imaginary literal "4.2j". Since these are +three separate tokens, whitespace is allowed between them: + + 3 + 4.2j + +No whitespace is allowed *within* each token. In particular, the "j" +suffix, may not be separated from the number before it. + +The number before the "j" has the same syntax as a floating-point +literal. Thus, the following are valid imaginary literals: + + 4.2j + 3.14j + 10.j + .001j + 1e100j + 3.14e-10j + 3.14_15_93j + +Unlike in a floating-point literal the decimal point can be omitted if +the imaginary number only has an integer part. The number is still +evaluated as a floating-point number, not an integer: + + 10j + 0j + 1000000000000000000000000j # equivalent to 1e+24j + +The "j" suffix is case-insensitive. That means you can use "J" +instead: + + 3.14J # equivalent to 3.14j + +Formally, imaginary literals are described by the following lexical +definition: + + imagnumber: (floatnumber | digitpart) ("j" | "J") ''', 'numeric-types': r'''Emulating numeric types *********************** @@ -6807,9 +7598,9 @@ class that has an "__rsub__()" method, "type(y).__rsub__(y, x)" is third argument if the three-argument version of the built-in "pow()" function is to be supported. - Changed in version 3.14.0a7 (unreleased): Three-argument "pow()" - now try calling "__rpow__()" if necessary. Previously it was only - called in two-argument "pow()" and the binary power operator. + Changed in version 3.14: Three-argument "pow()" now try calling + "__rpow__()" if necessary. Previously it was only called in two- + argument "pow()" and the binary power operator. Note: @@ -7283,21 +8074,24 @@ class C: pass # a class with no methods (yet) The "collections.abc" module provides a "MutableMapping" *abstract base class* to help create those methods from a base set of "__getitem__()", "__setitem__()", "__delitem__()", and "keys()". -Mutable sequences should provide methods "append()", "count()", -"index()", "extend()", "insert()", "pop()", "remove()", "reverse()" -and "sort()", like Python standard "list" objects. Finally, sequence + +Mutable sequences should provide methods "append()", "clear()", +"count()", "extend()", "index()", "insert()", "pop()", "remove()", and +"reverse()", like Python standard "list" objects. Finally, sequence types should implement addition (meaning concatenation) and multiplication (meaning repetition) by defining the methods "__add__()", "__radd__()", "__iadd__()", "__mul__()", "__rmul__()" and "__imul__()" described below; they should not define other numerical -operators. It is recommended that both mappings and sequences -implement the "__contains__()" method to allow efficient use of the -"in" operator; for mappings, "in" should search the mapping’s keys; -for sequences, it should search through the values. It is further -recommended that both mappings and sequences implement the -"__iter__()" method to allow efficient iteration through the -container; for mappings, "__iter__()" should iterate through the -object’s keys; for sequences, it should iterate through the values. +operators. + +It is recommended that both mappings and sequences implement the +"__contains__()" method to allow efficient use of the "in" operator; +for mappings, "in" should search the mapping’s keys; for sequences, it +should search through the values. It is further recommended that both +mappings and sequences implement the "__iter__()" method to allow +efficient iteration through the container; for mappings, "__iter__()" +should iterate through the object’s keys; for sequences, it should +iterate through the values. object.__len__(self) @@ -7534,8 +8328,8 @@ class C: pass # a class with no methods (yet) important that the emulation only be implemented to the degree that it makes sense for the object being modelled. For example, some sequences may work well with retrieval of individual elements, but -extracting a slice may not make sense. (One example of this is the -"NodeList" interface in the W3C’s Document Object Model.) +extracting a slice may not make sense. (One example of this is the +NodeList interface in the W3C’s Document Object Model.) Basic customization @@ -7930,6 +8724,9 @@ class instances. Customizing module attribute access ----------------------------------- +module.__getattr__() +module.__dir__() + Special names "__getattr__" and "__dir__" can be also used to customize access to module attributes. The "__getattr__" function at the module level should accept one argument which is the name of an @@ -7945,6 +8742,8 @@ class instances. present, this function overrides the standard "dir()" search on a module. +module.__class__ + For a more fine grained customization of the module behavior (setting attributes, properties, etc.), one can set the "__class__" attribute of a module object to a subclass of "types.ModuleType". For example: @@ -8640,21 +9439,24 @@ class of a class is known as that class’s *metaclass*, and most The "collections.abc" module provides a "MutableMapping" *abstract base class* to help create those methods from a base set of "__getitem__()", "__setitem__()", "__delitem__()", and "keys()". -Mutable sequences should provide methods "append()", "count()", -"index()", "extend()", "insert()", "pop()", "remove()", "reverse()" -and "sort()", like Python standard "list" objects. Finally, sequence + +Mutable sequences should provide methods "append()", "clear()", +"count()", "extend()", "index()", "insert()", "pop()", "remove()", and +"reverse()", like Python standard "list" objects. Finally, sequence types should implement addition (meaning concatenation) and multiplication (meaning repetition) by defining the methods "__add__()", "__radd__()", "__iadd__()", "__mul__()", "__rmul__()" and "__imul__()" described below; they should not define other numerical -operators. It is recommended that both mappings and sequences -implement the "__contains__()" method to allow efficient use of the -"in" operator; for mappings, "in" should search the mapping’s keys; -for sequences, it should search through the values. It is further -recommended that both mappings and sequences implement the -"__iter__()" method to allow efficient iteration through the -container; for mappings, "__iter__()" should iterate through the -object’s keys; for sequences, it should iterate through the values. +operators. + +It is recommended that both mappings and sequences implement the +"__contains__()" method to allow efficient use of the "in" operator; +for mappings, "in" should search the mapping’s keys; for sequences, it +should search through the values. It is further recommended that both +mappings and sequences implement the "__iter__()" method to allow +efficient iteration through the container; for mappings, "__iter__()" +should iterate through the object’s keys; for sequences, it should +iterate through the values. object.__len__(self) @@ -8845,9 +9647,9 @@ class that has an "__rsub__()" method, "type(y).__rsub__(y, x)" is third argument if the three-argument version of the built-in "pow()" function is to be supported. - Changed in version 3.14.0a7 (unreleased): Three-argument "pow()" - now try calling "__rpow__()" if necessary. Previously it was only - called in two-argument "pow()" and the binary power operator. + Changed in version 3.14: Three-argument "pow()" now try calling + "__rpow__()" if necessary. Previously it was only called in two- + argument "pow()" and the binary power operator. Note: @@ -9205,17 +10007,24 @@ class is used in a class pattern with positional arguments, each Since it is already lowercase, "lower()" would do nothing to "'ß'"; "casefold()" converts it to ""ss"". - The casefolding algorithm is described in section 3.13 ‘Default + The casefolding algorithm is described in section 3.13.3 ‘Default Case Folding’ of the Unicode Standard. Added in version 3.3. -str.center(width[, fillchar]) +str.center(width, fillchar=' ', /) Return centered in a string of length *width*. Padding is done using the specified *fillchar* (default is an ASCII space). The original string is returned if *width* is less than or equal to - "len(s)". + "len(s)". For example: + + >>> 'Python'.center(10) + ' Python ' + >>> 'Python'.center(10, '-') + '--Python--' + >>> 'Python'.center(4) + 'Python' str.count(sub[, start[, end]]) @@ -9224,7 +10033,18 @@ class is used in a class pattern with positional arguments, each *end* are interpreted as in slice notation. If *sub* is empty, returns the number of empty strings between - characters which is the length of the string plus one. + characters which is the length of the string plus one. For example: + + >>> 'spam, spam, spam'.count('spam') + 3 + >>> 'spam, spam, spam'.count('spam', 5) + 2 + >>> 'spam, spam, spam'.count('spam', 5, 10) + 1 + >>> 'spam, spam, spam'.count('eggs') + 0 + >>> 'spam, spam, spam'.count('') + 17 str.encode(encoding='utf-8', errors='strict') @@ -9241,7 +10061,13 @@ class is used in a class pattern with positional arguments, each For performance reasons, the value of *errors* is not checked for validity unless an encoding error actually occurs, Python - Development Mode is enabled or a debug build is used. + Development Mode is enabled or a debug build is used. For example: + + >>> encoded_str_to_bytes = 'Python'.encode() + >>> type(encoded_str_to_bytes) + + >>> encoded_str_to_bytes + b'Python' Changed in version 3.1: Added support for keyword arguments. @@ -9254,6 +10080,19 @@ class is used in a class pattern with positional arguments, each otherwise return "False". *suffix* can also be a tuple of suffixes to look for. With optional *start*, test beginning at that position. With optional *end*, stop comparing at that position. + Using *start* and *end* is equivalent to + "str[start:end].endswith(suffix)". For example: + + >>> 'Python'.endswith('on') + True + >>> 'a tuple of suffixes'.endswith(('at', 'in')) + False + >>> 'a tuple of suffixes'.endswith(('at', 'es')) + True + >>> 'Python is amazing'.endswith('is', 0, 9) + True + + See also "startswith()" and "removesuffix()". str.expandtabs(tabsize=8) @@ -9269,19 +10108,29 @@ class is used in a class pattern with positional arguments, each ("\n") or return ("\r"), it is copied and the current column is reset to zero. Any other character is copied unchanged and the current column is incremented by one regardless of how the - character is represented when printed. + character is represented when printed. For example: - >>> '01\t012\t0123\t01234'.expandtabs() - '01 012 0123 01234' - >>> '01\t012\t0123\t01234'.expandtabs(4) - '01 012 0123 01234' + >>> '01\t012\t0123\t01234'.expandtabs() + '01 012 0123 01234' + >>> '01\t012\t0123\t01234'.expandtabs(4) + '01 012 0123 01234' + >>> print('01\t012\n0123\t01234'.expandtabs(4)) + 01 012 + 0123 01234 str.find(sub[, start[, end]]) Return the lowest index in the string where substring *sub* is found within the slice "s[start:end]". Optional arguments *start* and *end* are interpreted as in slice notation. Return "-1" if - *sub* is not found. + *sub* is not found. For example: + + >>> 'spam, spam, spam'.find('sp') + 0 + >>> 'spam, spam, spam'.find('sp', 5) + 6 + + See also "rfind()" and "index()". Note: @@ -9389,7 +10238,7 @@ class is used in a class pattern with positional arguments, each str.isidentifier() Return "True" if the string is a valid identifier according to the - language definition, section Identifiers and keywords. + language definition, section Names (identifiers and keywords). "keyword.iskeyword()" can be used to test whether string "s" is a reserved identifier, such as "def" and "class". @@ -9421,8 +10270,8 @@ class is used in a class pattern with positional arguments, each str.isprintable() - Return true if all characters in the string are printable, false if - it contains at least one non-printable character. + Return "True" if all characters in the string are printable, + "False" if it contains at least one non-printable character. Here “printable” means the character is suitable for "repr()" to use in its output; “non-printable” means that "repr()" on built-in @@ -9467,14 +10316,14 @@ class is used in a class pattern with positional arguments, each >>> ' '.isupper() False -str.join(iterable) +str.join(iterable, /) Return a string which is the concatenation of the strings in *iterable*. A "TypeError" will be raised if there are any non- string values in *iterable*, including "bytes" objects. The separator between elements is the string providing this method. -str.ljust(width[, fillchar]) +str.ljust(width, fillchar=' ', /) Return the string left justified in a string of length *width*. Padding is done using the specified *fillchar* (default is an ASCII @@ -9486,10 +10335,10 @@ class is used in a class pattern with positional arguments, each Return a copy of the string with all the cased characters [4] converted to lowercase. - The lowercasing algorithm used is described in section 3.13 - ‘Default Case Folding’ of the Unicode Standard. + The lowercasing algorithm used is described in section 3.13.2 + ‘Default Case Conversion’ of the Unicode Standard. -str.lstrip([chars]) +str.lstrip(chars=None, /) Return a copy of the string with leading characters removed. The *chars* argument is a string specifying the set of characters to be @@ -9510,7 +10359,8 @@ class is used in a class pattern with positional arguments, each >>> 'Arthur: three!'.removeprefix('Arthur: ') 'three!' -static str.maketrans(x[, y[, z]]) +static str.maketrans(dict, /) +static str.maketrans(from, to, remove='', /) This static method returns a translation table usable for "str.translate()". @@ -9521,12 +10371,12 @@ class is used in a class pattern with positional arguments, each Character keys will then be converted to ordinals. If there are two arguments, they must be strings of equal length, - and in the resulting dictionary, each character in x will be mapped - to the character at the same position in y. If there is a third - argument, it must be a string, whose characters will be mapped to - "None" in the result. + and in the resulting dictionary, each character in *from* will be + mapped to the character at the same position in *to*. If there is + a third argument, it must be a string, whose characters will be + mapped to "None" in the result. -str.partition(sep) +str.partition(sep, /) Split the string at the first occurrence of *sep*, and return a 3-tuple containing the part before the separator, the separator @@ -9560,7 +10410,7 @@ class is used in a class pattern with positional arguments, each Added in version 3.9. -str.replace(old, new, count=-1) +str.replace(old, new, /, count=-1) Return a copy of the string with all occurrences of substring *old* replaced by *new*. If *count* is given, only the first *count* @@ -9582,14 +10432,14 @@ class is used in a class pattern with positional arguments, each Like "rfind()" but raises "ValueError" when the substring *sub* is not found. -str.rjust(width[, fillchar]) +str.rjust(width, fillchar=' ', /) Return the string right justified in a string of length *width*. Padding is done using the specified *fillchar* (default is an ASCII space). The original string is returned if *width* is less than or equal to "len(s)". -str.rpartition(sep) +str.rpartition(sep, /) Split the string at the last occurrence of *sep*, and return a 3-tuple containing the part before the separator, the separator @@ -9606,7 +10456,7 @@ class is used in a class pattern with positional arguments, each from the right, "rsplit()" behaves like "split()" which is described in detail below. -str.rstrip([chars]) +str.rstrip(chars=None, /) Return a copy of the string with trailing characters removed. The *chars* argument is a string specifying the set of characters to be @@ -9669,6 +10519,18 @@ class is used in a class pattern with positional arguments, each >>> ' 1 2 3 '.split() ['1', '2', '3'] + If *sep* is not specified or is "None" and *maxsplit* is "0", only + leading runs of consecutive whitespace are considered. + + For example: + + >>> "".split(None, 0) + [] + >>> " ".split(None, 0) + [] + >>> " foo ".split(maxsplit=0) + ['foo '] + str.splitlines(keepends=False) Return a list of the lines in the string, breaking at line @@ -9737,7 +10599,7 @@ class is used in a class pattern with positional arguments, each With optional *start*, test string beginning at that position. With optional *end*, stop comparing string at that position. -str.strip([chars]) +str.strip(chars=None, /) Return a copy of the string with the leading and trailing characters removed. The *chars* argument is a string specifying the @@ -9801,7 +10663,7 @@ class is used in a class pattern with positional arguments, each >>> titlecase("they're bill's friends.") "They're Bill's Friends." -str.translate(table) +str.translate(table, /) Return a copy of the string in which each character has been mapped through the given translation table. The table must be an object @@ -9826,10 +10688,10 @@ class is used in a class pattern with positional arguments, each category of the resulting character(s) is not “Lu” (Letter, uppercase), but e.g. “Lt” (Letter, titlecase). - The uppercasing algorithm used is described in section 3.13 - ‘Default Case Folding’ of the Unicode Standard. + The uppercasing algorithm used is described in section 3.13.2 + ‘Default Case Conversion’ of the Unicode Standard. -str.zfill(width) +str.zfill(width, /) Return a copy of the string left filled with ASCII "'0'" digits to make a string of length *width*. A leading sign prefix @@ -9847,174 +10709,313 @@ class is used in a class pattern with positional arguments, each 'strings': '''String and Bytes literals ************************* -String literals are described by the following lexical definitions: +String literals are text enclosed in single quotes ("'") or double +quotes ("""). For example: - stringliteral: [stringprefix](shortstring | longstring) - stringprefix: "r" | "u" | "R" | "U" | "f" | "F" - | "fr" | "Fr" | "fR" | "FR" | "rf" | "rF" | "Rf" | "RF" - shortstring: "'" shortstringitem* "'" | '"' shortstringitem* '"' - longstring: "\'\'\'" longstringitem* "\'\'\'" | '"""' longstringitem* '"""' - shortstringitem: shortstringchar | stringescapeseq - longstringitem: longstringchar | stringescapeseq - shortstringchar: - longstringchar: - stringescapeseq: "\\" + "spam" + 'eggs' - bytesliteral: bytesprefix(shortbytes | longbytes) - bytesprefix: "b" | "B" | "br" | "Br" | "bR" | "BR" | "rb" | "rB" | "Rb" | "RB" - shortbytes: "'" shortbytesitem* "'" | '"' shortbytesitem* '"' - longbytes: "\'\'\'" longbytesitem* "\'\'\'" | '"""' longbytesitem* '"""' - shortbytesitem: shortbyteschar | bytesescapeseq - longbytesitem: longbyteschar | bytesescapeseq - shortbyteschar: - longbyteschar: - bytesescapeseq: "\\" +The quote used to start the literal also terminates it, so a string +literal can only contain the other quote (except with escape +sequences, see below). For example: -One syntactic restriction not indicated by these productions is that -whitespace is not allowed between the "stringprefix" or "bytesprefix" -and the rest of the literal. The source character set is defined by -the encoding declaration; it is UTF-8 if no encoding declaration is -given in the source file; see section Encoding declarations. + 'Say "Hello", please.' + "Don't do that!" -In plain English: Both types of literals can be enclosed in matching -single quotes ("'") or double quotes ("""). They can also be enclosed -in matching groups of three single or double quotes (these are -generally referred to as *triple-quoted strings*). The backslash ("\\") -character is used to give special meaning to otherwise ordinary -characters like "n", which means ‘newline’ when escaped ("\\n"). It can -also be used to escape characters that otherwise have a special -meaning, such as newline, backslash itself, or the quote character. -See escape sequences below for examples. +Except for this limitation, the choice of quote character ("'" or """) +does not affect how the literal is parsed. -Bytes literals are always prefixed with "'b'" or "'B'"; they produce -an instance of the "bytes" type instead of the "str" type. They may -only contain ASCII characters; bytes with a numeric value of 128 or -greater must be expressed with escapes. +Inside a string literal, the backslash ("\\") character introduces an +*escape sequence*, which has special meaning depending on the +character after the backslash. For example, "\\"" denotes the double +quote character, and does *not* end the string: -Both string and bytes literals may optionally be prefixed with a -letter "'r'" or "'R'"; such constructs are called *raw string -literals* and *raw bytes literals* respectively and treat backslashes -as literal characters. As a result, in raw string literals, "'\\U'" -and "'\\u'" escapes are not treated specially. + >>> print("Say \\"Hello\\" to everyone!") + Say "Hello" to everyone! + +See escape sequences below for a full list of such sequences, and more +details. + + +Triple-quoted strings +===================== + +Strings can also be enclosed in matching groups of three single or +double quotes. These are generally referred to as *triple-quoted +strings*: + + """This is a triple-quoted string.""" + +In triple-quoted literals, unescaped quotes are allowed (and are +retained), except that three unescaped quotes in a row terminate the +literal, if they are of the same kind ("'" or """) used at the start: + + """This string has "quotes" inside.""" + +Unescaped newlines are also allowed and retained: + + \'\'\'This triple-quoted string + continues on the next line.\'\'\' + + +String prefixes +=============== + +String literals can have an optional *prefix* that influences how the +content of the literal is parsed, for example: + + b"data" + f'{result=}' + +The allowed prefixes are: + +* "b": Bytes literal + +* "r": Raw string + +* "f": Formatted string literal (“f-string”) + +* "t": Template string literal (“t-string”) + +* "u": No effect (allowed for backwards compatibility) + +See the linked sections for details on each type. + +Prefixes are case-insensitive (for example, ‘"B"’ works the same as +‘"b"’). The ‘"r"’ prefix can be combined with ‘"f"’, ‘"t"’ or ‘"b"’, +so ‘"fr"’, ‘"rf"’, ‘"tr"’, ‘"rt"’, ‘"br"’, and ‘"rb"’ are also valid +prefixes. Added in version 3.3: The "'rb'" prefix of raw bytes literals has been added as a synonym of "'br'".Support for the unicode legacy literal ("u'value'") was reintroduced to simplify the maintenance of dual Python 2.x and 3.x codebases. See **PEP 414** for more information. -A string literal with "'f'" or "'F'" in its prefix is a *formatted -string literal*; see f-strings. The "'f'" may be combined with "'r'", -but not with "'b'" or "'u'", therefore raw formatted strings are -possible, but formatted bytes literals are not. -In triple-quoted literals, unescaped newlines and quotes are allowed -(and are retained), except that three unescaped quotes in a row -terminate the literal. (A “quote” is the character used to open the -literal, i.e. either "'" or """.) +Formal grammar +============== + +String literals, except “f-strings” and “t-strings”, are described by +the following lexical definitions. + +These definitions use negative lookaheads ("!") to indicate that an +ending quote ends the literal. + + STRING: [stringprefix] (stringcontent) + stringprefix: <("r" | "u" | "b" | "br" | "rb"), case-insensitive> + stringcontent: + | "\'\'\'" ( !"\'\'\'" longstringitem)* "\'\'\'" + | '"""' ( !'"""' longstringitem)* '"""' + | "'" ( !"'" stringitem)* "'" + | '"' ( !'"' stringitem)* '"' + stringitem: stringchar | stringescapeseq + stringchar: + longstringitem: stringitem | newline + stringescapeseq: "\\" + +Note that as in all lexical definitions, whitespace is significant. In +particular, the prefix (if any) must be immediately followed by the +starting quote. Escape sequences ================ -Unless an "'r'" or "'R'" prefix is present, escape sequences in string +Unless an ‘"r"’ or ‘"R"’ prefix is present, escape sequences in string and bytes literals are interpreted according to rules similar to those used by Standard C. The recognized escape sequences are: -+---------------------------+-----------------------------------+---------+ -| Escape Sequence | Meaning | Notes | -|===========================|===================================|=========| -| "\\" | Backslash and newline ignored | (1) | -+---------------------------+-----------------------------------+---------+ -| "\\\\" | Backslash ("\\") | | -+---------------------------+-----------------------------------+---------+ -| "\\'" | Single quote ("'") | | -+---------------------------+-----------------------------------+---------+ -| "\\"" | Double quote (""") | | -+---------------------------+-----------------------------------+---------+ -| "\\a" | ASCII Bell (BEL) | | -+---------------------------+-----------------------------------+---------+ -| "\\b" | ASCII Backspace (BS) | | -+---------------------------+-----------------------------------+---------+ -| "\\f" | ASCII Formfeed (FF) | | -+---------------------------+-----------------------------------+---------+ -| "\\n" | ASCII Linefeed (LF) | | -+---------------------------+-----------------------------------+---------+ -| "\\r" | ASCII Carriage Return (CR) | | -+---------------------------+-----------------------------------+---------+ -| "\\t" | ASCII Horizontal Tab (TAB) | | -+---------------------------+-----------------------------------+---------+ -| "\\v" | ASCII Vertical Tab (VT) | | -+---------------------------+-----------------------------------+---------+ -| "\\*ooo*" | Character with octal value *ooo* | (2,4) | -+---------------------------+-----------------------------------+---------+ -| "\\x*hh*" | Character with hex value *hh* | (3,4) | -+---------------------------+-----------------------------------+---------+ ++----------------------------------------------------+----------------------------------------------------+ +| Escape Sequence | Meaning | +|====================================================|====================================================| +| "\\" | Ignored end of line | ++----------------------------------------------------+----------------------------------------------------+ +| "\\\\" | Backslash | ++----------------------------------------------------+----------------------------------------------------+ +| "\\'" | Single quote | ++----------------------------------------------------+----------------------------------------------------+ +| "\\"" | Double quote | ++----------------------------------------------------+----------------------------------------------------+ +| "\\a" | ASCII Bell (BEL) | ++----------------------------------------------------+----------------------------------------------------+ +| "\\b" | ASCII Backspace (BS) | ++----------------------------------------------------+----------------------------------------------------+ +| "\\f" | ASCII Formfeed (FF) | ++----------------------------------------------------+----------------------------------------------------+ +| "\\n" | ASCII Linefeed (LF) | ++----------------------------------------------------+----------------------------------------------------+ +| "\\r" | ASCII Carriage Return (CR) | ++----------------------------------------------------+----------------------------------------------------+ +| "\\t" | ASCII Horizontal Tab (TAB) | ++----------------------------------------------------+----------------------------------------------------+ +| "\\v" | ASCII Vertical Tab (VT) | ++----------------------------------------------------+----------------------------------------------------+ +| "\\*ooo*" | Octal character | ++----------------------------------------------------+----------------------------------------------------+ +| "\\x*hh*" | Hexadecimal character | ++----------------------------------------------------+----------------------------------------------------+ +| "\\N{*name*}" | Named Unicode character | ++----------------------------------------------------+----------------------------------------------------+ +| "\\u*xxxx*" | Hexadecimal Unicode character | ++----------------------------------------------------+----------------------------------------------------+ +| "\\U*xxxxxxxx*" | Hexadecimal Unicode character | ++----------------------------------------------------+----------------------------------------------------+ -Escape sequences only recognized in string literals are: -+---------------------------+-----------------------------------+---------+ -| Escape Sequence | Meaning | Notes | -|===========================|===================================|=========| -| "\\N{*name*}" | Character named *name* in the | (5) | -| | Unicode database | | -+---------------------------+-----------------------------------+---------+ -| "\\u*xxxx*" | Character with 16-bit hex value | (6) | -| | *xxxx* | | -+---------------------------+-----------------------------------+---------+ -| "\\U*xxxxxxxx*" | Character with 32-bit hex value | (7) | -| | *xxxxxxxx* | | -+---------------------------+-----------------------------------+---------+ +Ignored end of line +------------------- -Notes: +A backslash can be added at the end of a line to ignore the newline: -1. A backslash can be added at the end of a line to ignore the - newline: + >>> 'This string will not include \\ + ... backslashes or newline characters.' + 'This string will not include backslashes or newline characters.' - >>> 'This string will not include \\ - ... backslashes or newline characters.' - 'This string will not include backslashes or newline characters.' +The same result can be achieved using triple-quoted strings, or +parentheses and string literal concatenation. - The same result can be achieved using triple-quoted strings, or - parentheses and string literal concatenation. -2. As in Standard C, up to three octal digits are accepted. +Escaped characters +------------------ - Changed in version 3.11: Octal escapes with value larger than - "0o377" produce a "DeprecationWarning". +To include a backslash in a non-raw Python string literal, it must be +doubled. The "\\\\" escape sequence denotes a single backslash +character: - Changed in version 3.12: Octal escapes with value larger than - "0o377" produce a "SyntaxWarning". In a future Python version they - will be eventually a "SyntaxError". + >>> print('C:\\\\Program Files') + C:\\Program Files -3. Unlike in Standard C, exactly two hex digits are required. +Similarly, the "\\'" and "\\"" sequences denote the single and double +quote character, respectively: -4. In a bytes literal, hexadecimal and octal escapes denote the byte - with the given value. In a string literal, these escapes denote a - Unicode character with the given value. + >>> print('\\' and \\"') + ' and " -5. Changed in version 3.3: Support for name aliases [1] has been - added. -6. Exactly four hex digits are required. +Octal character +--------------- -7. Any Unicode character can be encoded this way. Exactly eight hex - digits are required. +The sequence "\\*ooo*" denotes a *character* with the octal (base 8) +value *ooo*: -Unlike Standard C, all unrecognized escape sequences are left in the -string unchanged, i.e., *the backslash is left in the result*. (This -behavior is useful when debugging: if an escape sequence is mistyped, -the resulting output is more easily recognized as broken.) It is also -important to note that the escape sequences only recognized in string -literals fall into the category of unrecognized escapes for bytes -literals. + >>> '\\120' + 'P' + +Up to three octal digits (0 through 7) are accepted. + +In a bytes literal, *character* means a *byte* with the given value. +In a string literal, it means a Unicode character with the given +value. + +Changed in version 3.11: Octal escapes with value larger than "0o377" +(255) produce a "DeprecationWarning". + +Changed in version 3.12: Octal escapes with value larger than "0o377" +(255) produce a "SyntaxWarning". In a future Python version they will +raise a "SyntaxError". + + +Hexadecimal character +--------------------- + +The sequence "\\x*hh*" denotes a *character* with the hex (base 16) +value *hh*: + + >>> '\\x50' + 'P' + +Unlike in Standard C, exactly two hex digits are required. + +In a bytes literal, *character* means a *byte* with the given value. +In a string literal, it means a Unicode character with the given +value. + + +Named Unicode character +----------------------- + +The sequence "\\N{*name*}" denotes a Unicode character with the given +*name*: + + >>> '\\N{LATIN CAPITAL LETTER P}' + 'P' + >>> '\\N{SNAKE}' + '🐍' + +This sequence cannot appear in bytes literals. + +Changed in version 3.3: Support for name aliases has been added. + + +Hexadecimal Unicode characters +------------------------------ + +These sequences "\\u*xxxx*" and "\\U*xxxxxxxx*" denote the Unicode +character with the given hex (base 16) value. Exactly four digits are +required for "\\u"; exactly eight digits are required for "\\U". The +latter can encode any Unicode character. + + >>> '\\u1234' + 'ሴ' + >>> '\\U0001f40d' + '🐍' + +These sequences cannot appear in bytes literals. + + +Unrecognized escape sequences +----------------------------- + +Unlike in Standard C, all unrecognized escape sequences are left in +the string unchanged, that is, *the backslash is left in the result*: + + >>> print('\\q') + \\q + >>> list('\\q') + ['\\\\', 'q'] + +Note that for bytes literals, the escape sequences only recognized in +string literals ("\\N...", "\\u...", "\\U...") fall into the category of +unrecognized escapes. Changed in version 3.6: Unrecognized escape sequences produce a "DeprecationWarning". Changed in version 3.12: Unrecognized escape sequences produce a -"SyntaxWarning". In a future Python version they will be eventually a +"SyntaxWarning". In a future Python version they will raise a "SyntaxError". + +Bytes literals +============== + +*Bytes literals* are always prefixed with ‘"b"’ or ‘"B"’; they produce +an instance of the "bytes" type instead of the "str" type. They may +only contain ASCII characters; bytes with a numeric value of 128 or +greater must be expressed with escape sequences (typically Hexadecimal +character or Octal character): + + >>> b'\\x89PNG\\r\\n\\x1a\\n' + b'\\x89PNG\\r\\n\\x1a\\n' + >>> list(b'\\x89PNG\\r\\n\\x1a\\n') + [137, 80, 78, 71, 13, 10, 26, 10] + +Similarly, a zero byte must be expressed using an escape sequence +(typically "\\0" or "\\x00"). + + +Raw string literals +=================== + +Both string and bytes literals may optionally be prefixed with a +letter ‘"r"’ or ‘"R"’; such constructs are called *raw string +literals* and *raw bytes literals* respectively and treat backslashes +as literal characters. As a result, in raw string literals, escape +sequences are not treated specially: + + >>> r'\\d{4}-\\d{2}-\\d{2}' + '\\\\d{4}-\\\\d{2}-\\\\d{2}' + Even in a raw literal, quotes can be escaped with a backslash, but the backslash remains in the result; for example, "r"\\""" is a valid string literal consisting of two characters: a backslash and a double @@ -10024,6 +11025,199 @@ class is used in a class pattern with positional arguments, each the following quote character). Note also that a single backslash followed by a newline is interpreted as those two characters as part of the literal, *not* as a line continuation. + + +f-strings +========= + +Added in version 3.6. + +A *formatted string literal* or *f-string* is a string literal that is +prefixed with ‘"f"’ or ‘"F"’. These strings may contain replacement +fields, which are expressions delimited by curly braces "{}". While +other string literals always have a constant value, formatted strings +are really expressions evaluated at run time. + +Escape sequences are decoded like in ordinary string literals (except +when a literal is also marked as a raw string). After decoding, the +grammar for the contents of the string is: + + f_string: (literal_char | "{{" | "}}" | replacement_field)* + replacement_field: "{" f_expression ["="] ["!" conversion] [":" format_spec] "}" + f_expression: (conditional_expression | "*" or_expr) + ("," conditional_expression | "," "*" or_expr)* [","] + | yield_expression + conversion: "s" | "r" | "a" + format_spec: (literal_char | replacement_field)* + literal_char: + +The parts of the string outside curly braces are treated literally, +except that any doubled curly braces "'{{'" or "'}}'" are replaced +with the corresponding single curly brace. A single opening curly +bracket "'{'" marks a replacement field, which starts with a Python +expression. To display both the expression text and its value after +evaluation, (useful in debugging), an equal sign "'='" may be added +after the expression. A conversion field, introduced by an exclamation +point "'!'" may follow. A format specifier may also be appended, +introduced by a colon "':'". A replacement field ends with a closing +curly bracket "'}'". + +Expressions in formatted string literals are treated like regular +Python expressions surrounded by parentheses, with a few exceptions. +An empty expression is not allowed, and both "lambda" and assignment +expressions ":=" must be surrounded by explicit parentheses. Each +expression is evaluated in the context where the formatted string +literal appears, in order from left to right. Replacement expressions +can contain newlines in both single-quoted and triple-quoted f-strings +and they can contain comments. Everything that comes after a "#" +inside a replacement field is a comment (even closing braces and +quotes). In that case, replacement fields must be closed in a +different line. + + >>> f"abc{a # This is a comment }" + ... + 3}" + 'abc5' + +Changed in version 3.7: Prior to Python 3.7, an "await" expression and +comprehensions containing an "async for" clause were illegal in the +expressions in formatted string literals due to a problem with the +implementation. + +Changed in version 3.12: Prior to Python 3.12, comments were not +allowed inside f-string replacement fields. + +When the equal sign "'='" is provided, the output will have the +expression text, the "'='" and the evaluated value. Spaces after the +opening brace "'{'", within the expression and after the "'='" are all +retained in the output. By default, the "'='" causes the "repr()" of +the expression to be provided, unless there is a format specified. +When a format is specified it defaults to the "str()" of the +expression unless a conversion "'!r'" is declared. + +Added in version 3.8: The equal sign "'='". + +If a conversion is specified, the result of evaluating the expression +is converted before formatting. Conversion "'!s'" calls "str()" on +the result, "'!r'" calls "repr()", and "'!a'" calls "ascii()". + +The result is then formatted using the "format()" protocol. The +format specifier is passed to the "__format__()" method of the +expression or conversion result. An empty string is passed when the +format specifier is omitted. The formatted result is then included in +the final value of the whole string. + +Top-level format specifiers may include nested replacement fields. +These nested fields may include their own conversion fields and format +specifiers, but may not include more deeply nested replacement fields. +The format specifier mini-language is the same as that used by the +"str.format()" method. + +Formatted string literals may be concatenated, but replacement fields +cannot be split across literals. + +Some examples of formatted string literals: + + >>> name = "Fred" + >>> f"He said his name is {name!r}." + "He said his name is 'Fred'." + >>> f"He said his name is {repr(name)}." # repr() is equivalent to !r + "He said his name is 'Fred'." + >>> width = 10 + >>> precision = 4 + >>> value = decimal.Decimal("12.34567") + >>> f"result: {value:{width}.{precision}}" # nested fields + 'result: 12.35' + >>> today = datetime(year=2017, month=1, day=27) + >>> f"{today:%B %d, %Y}" # using date format specifier + 'January 27, 2017' + >>> f"{today=:%B %d, %Y}" # using date format specifier and debugging + 'today=January 27, 2017' + >>> number = 1024 + >>> f"{number:#0x}" # using integer format specifier + '0x400' + >>> foo = "bar" + >>> f"{ foo = }" # preserves whitespace + " foo = 'bar'" + >>> line = "The mill's closed" + >>> f"{line = }" + 'line = "The mill\\'s closed"' + >>> f"{line = :20}" + "line = The mill's closed " + >>> f"{line = !r:20}" + 'line = "The mill\\'s closed" ' + +Reusing the outer f-string quoting type inside a replacement field is +permitted: + + >>> a = dict(x=2) + >>> f"abc {a["x"]} def" + 'abc 2 def' + +Changed in version 3.12: Prior to Python 3.12, reuse of the same +quoting type of the outer f-string inside a replacement field was not +possible. + +Backslashes are also allowed in replacement fields and are evaluated +the same way as in any other context: + + >>> a = ["a", "b", "c"] + >>> print(f"List a contains:\\n{"\\n".join(a)}") + List a contains: + a + b + c + +Changed in version 3.12: Prior to Python 3.12, backslashes were not +permitted inside an f-string replacement field. + +Formatted string literals cannot be used as docstrings, even if they +do not include expressions. + + >>> def foo(): + ... f"Not a docstring" + ... + >>> foo.__doc__ is None + True + +See also **PEP 498** for the proposal that added formatted string +literals, and "str.format()", which uses a related format string +mechanism. + + +t-strings +========= + +Added in version 3.14. + +A *template string literal* or *t-string* is a string literal that is +prefixed with ‘"t"’ or ‘"T"’. These strings follow the same syntax and +evaluation rules as formatted string literals, with the following +differences: + +* Rather than evaluating to a "str" object, template string literals + evaluate to a "string.templatelib.Template" object. + +* The "format()" protocol is not used. Instead, the format specifier + and conversions (if any) are passed to a new "Interpolation" object + that is created for each evaluated expression. It is up to code that + processes the resulting "Template" object to decide how to handle + format specifiers and conversions. + +* Format specifiers containing nested replacement fields are evaluated + eagerly, prior to being passed to the "Interpolation" object. For + instance, an interpolation of the form "{amount:.{precision}f}" will + evaluate the inner expression "{precision}" to determine the value + of the "format_spec" attribute. If "precision" were to be "2", the + resulting format specifier would be "'.2f'". + +* When the equals sign "'='" is provided in an interpolation + expression, the text of the expression is appended to the literal + string that precedes the relevant interpolation. This includes the + equals sign and any surrounding whitespace. The "Interpolation" + instance for the expression will be created as normal, except that + "conversion" will be set to ‘"r"’ ("repr()") by default. If an + explicit conversion or format specifier are provided, this will + override the default behaviour. ''', 'subscriptions': r'''Subscriptions ************* @@ -10218,15 +11412,29 @@ class is used in a class pattern with positional arguments, each "except*" clause ================ -The "except*" clause(s) are used for handling "ExceptionGroup"s. The -exception type for matching is interpreted as in the case of "except", -but in the case of exception groups we can have partial matches when -the type matches some of the exceptions in the group. This means that -multiple "except*" clauses can execute, each handling part of the -exception group. Each clause executes at most once and handles an -exception group of all matching exceptions. Each exception in the -group is handled by at most one "except*" clause, the first that -matches it. +The "except*" clause(s) specify one or more handlers for groups of +exceptions ("BaseExceptionGroup" instances). A "try" statement can +have either "except" or "except*" clauses, but not both. The exception +type for matching is mandatory in the case of "except*", so "except*:" +is a syntax error. The type is interpreted as in the case of "except", +but matching is performed on the exceptions contained in the group +that is being handled. An "TypeError" is raised if a matching type is +a subclass of "BaseExceptionGroup", because that would have ambiguous +semantics. + +When an exception group is raised in the try block, each "except*" +clause splits (see "split()") it into the subgroups of matching and +non-matching exceptions. If the matching subgroup is not empty, it +becomes the handled exception (the value returned from +"sys.exception()") and assigned to the target of the "except*" clause +(if there is one). Then, the body of the "except*" clause executes. If +the non-matching subgroup is not empty, it is processed by the next +"except*" in the same manner. This continues until all exceptions in +the group have been matched, or the last "except*" clause has run. + +After all "except*" clauses execute, the group of unhandled exceptions +is merged with any exceptions that were raised or re-raised from +within "except*" clauses. This merged exception group propagates on.: >>> try: ... raise ExceptionGroup("eg", @@ -10239,20 +11447,19 @@ class is used in a class pattern with positional arguments, each caught with nested (TypeError(2),) caught with nested (OSError(3), OSError(4)) + Exception Group Traceback (most recent call last): - | File "", line 2, in - | ExceptionGroup: eg + | File "", line 2, in + | raise ExceptionGroup("eg", + | [ValueError(1), TypeError(2), OSError(3), OSError(4)]) + | ExceptionGroup: eg (1 sub-exception) +-+---------------- 1 ---------------- | ValueError: 1 +------------------------------------ -Any remaining exceptions that were not handled by any "except*" clause -are re-raised at the end, along with all exceptions that were raised -from within the "except*" clauses. If this list contains more than one -exception to reraise, they are combined into an exception group. - -If the raised exception is not an exception group and its type matches -one of the "except*" clauses, it is caught and wrapped by an exception -group with an empty message string. +If the exception raised from the "try" block is not an exception group +and its type matches one of the "except*" clauses, it is caught and +wrapped by an exception group with an empty message string. This +ensures that the type of the target "e" is consistently +"BaseExceptionGroup": >>> try: ... raise BlockingIOError @@ -10261,11 +11468,6 @@ class is used in a class pattern with positional arguments, each ... ExceptionGroup('', (BlockingIOError())) -An "except*" clause must have a matching expression; it cannot be -"except*:". Furthermore, this expression cannot contain exception -group types, because that would have ambiguous semantics. - -It is not possible to mix "except" and "except*" in the same "try". "break", "continue" and "return" cannot appear in an "except*" clause. @@ -10282,11 +11484,11 @@ class is used in a class pattern with positional arguments, each ================ If "finally" is present, it specifies a ‘cleanup’ handler. The "try" -clause is executed, including any "except" and "else" clauses. If an +clause is executed, including any "except" and "else" clauses. If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. The "finally" clause is executed. If there is a saved exception it is re-raised at the end of the "finally" -clause. If the "finally" clause raises another exception, the saved +clause. If the "finally" clause raises another exception, the saved exception is set as the context of the new exception. If the "finally" clause executes a "return", "break" or "continue" statement, the saved exception is discarded. For example, this function returns 42. @@ -10574,7 +11776,7 @@ def foo(): Sets These represent a mutable set. They are created by the built-in "set()" constructor and can be modified afterwards by several - methods, such as "add()". + methods, such as "add". Frozen sets These represent an immutable set. They are created by the built-in @@ -10979,7 +12181,7 @@ def foo(): "ImportWarning" when falling back to "__package__" during import resolution. - Deprecated since version 3.13, will be removed in version 3.15: + Deprecated since version 3.13, removed in version 3.15: "__package__" will cease to be set or taken into consideration by the import system or standard library. @@ -11053,11 +12255,10 @@ def foo(): It is **strongly** recommended that you use "module.__spec__.cached" instead of "module.__cached__". - Deprecated since version 3.13, will be removed in version 3.15: - Setting "__cached__" on a module while failing to set - "__spec__.cached" is deprecated. In Python 3.15, "__cached__" will - cease to be set or taken into consideration by the import system or - standard library. + Deprecated since version 3.13, removed in version 3.15: Setting + "__cached__" on a module while failing to set "__spec__.cached" is + deprecated. In Python 3.15, "__cached__" will cease to be set or + taken into consideration by the import system or standard library. Other writable attributes on module objects @@ -11168,11 +12369,20 @@ class method object, it is transformed into an instance method object | | collected during class body execution. See also: | | | "__annotations__ attributes". For best practices | | | on working with "__annotations__", please see | -| | "annotationlib". Where possible, use | +| | "annotationlib". Use | | | "annotationlib.get_annotations()" instead of | -| | accessing this attribute directly. Changed in | -| | version 3.14: Annotations are now lazily | -| | evaluated. See **PEP 649**. | +| | accessing this attribute directly. Warning: | +| | Accessing the "__annotations__" attribute directly | +| | on a class object may return annotations for the | +| | wrong class, specifically in certain cases where | +| | the class, its base class, or a metaclass is | +| | defined under "from __future__ import | +| | annotations". See **749** for details.This | +| | attribute does not exist on certain builtin | +| | classes. On user-defined classes without | +| | "__annotations__", it is an empty dictionary. | +| | Changed in version 3.14: Annotations are now | +| | lazily evaluated. See **PEP 649**. | +----------------------------------------------------+----------------------------------------------------+ | type.__annotate__() | The *annotate function* for this class, or "None" | | | if the class has no annotations. See also: | @@ -11510,6 +12720,10 @@ class instance has a namespace implemented as a dictionary which is | | (this is an index into the *bytecode* string of | | | the code object) | +----------------------------------------------------+----------------------------------------------------+ +| frame.f_generator | The *generator* or *coroutine* object that owns | +| | this frame, or "None" if the frame is a normal | +| | function. Added in version 3.14. | ++----------------------------------------------------+----------------------------------------------------+ Special writable attributes @@ -11690,8 +12904,8 @@ class instance has a namespace implemented as a dictionary which is dictionary entry. class dict(**kwargs) -class dict(mapping, **kwargs) -class dict(iterable, **kwargs) +class dict(mapping, /, **kwargs) +class dict(iterable, /, **kwargs) Return a new dictionary initialized from an optional positional argument and a possibly empty set of keyword arguments. @@ -11724,8 +12938,15 @@ class dict(iterable, **kwargs) the keyword argument replaces the value from the positional argument. - To illustrate, the following examples all return a dictionary equal - to "{"one": 1, "two": 2, "three": 3}": + Providing keyword arguments as in the first example only works for + keys that are valid Python identifiers. Otherwise, any valid keys + can be used. + + Dictionaries compare equal if and only if they have the same "(key, + value)" pairs (regardless of ordering). Order comparisons (‘<’, + ‘<=’, ‘>=’, ‘>’) raise "TypeError". To illustrate dictionary + creation and equality, the following examples all return a + dictionary equal to "{"one": 1, "two": 2, "three": 3}": >>> a = dict(one=1, two=2, three=3) >>> b = {'one': 1, 'two': 2, 'three': 3} @@ -11740,6 +12961,29 @@ class dict(iterable, **kwargs) keys that are valid Python identifiers. Otherwise, any valid keys can be used. + Dictionaries preserve insertion order. Note that updating a key + does not affect the order. Keys added after deletion are inserted + at the end. + + >>> d = {"one": 1, "two": 2, "three": 3, "four": 4} + >>> d + {'one': 1, 'two': 2, 'three': 3, 'four': 4} + >>> list(d) + ['one', 'two', 'three', 'four'] + >>> list(d.values()) + [1, 2, 3, 4] + >>> d["one"] = 42 + >>> d + {'one': 42, 'two': 2, 'three': 3, 'four': 4} + >>> del d["two"] + >>> d["two"] = None + >>> d + {'one': 42, 'three': 3, 'four': 4, 'two': None} + + Changed in version 3.7: Dictionary order is guaranteed to be + insertion order. This behavior was an implementation detail of + CPython from 3.6. + These are the operations that dictionaries support (and therefore, custom mapping types should support too): @@ -11777,8 +13021,8 @@ class dict(iterable, **kwargs) 1 The example above shows part of the implementation of - "collections.Counter". A different "__missing__" method is used - by "collections.defaultdict". + "collections.Counter". A different "__missing__()" method is + used by "collections.defaultdict". d[key] = value @@ -11837,7 +13081,8 @@ class dict(iterable, **kwargs) Return a new view of the dictionary’s keys. See the documentation of view objects. - pop(key[, default]) + pop(key, /) + pop(key, default, /) If *key* is in the dictionary, remove it and return its value, else return *default*. If *default* is not given and *key* is @@ -11868,10 +13113,13 @@ class dict(iterable, **kwargs) *key* with a value of *default* and return *default*. *default* defaults to "None". - update([other]) + update(**kwargs) + update(mapping, /, **kwargs) + update(iterable, /, **kwargs) - Update the dictionary with the key/value pairs from *other*, - overwriting existing keys. Return "None". + Update the dictionary with the key/value pairs from *mapping* or + *iterable* and *kwargs*, overwriting existing keys. Return + "None". "update()" accepts either another object with a "keys()" method (in which case "__getitem__()" is called with every key returned @@ -11910,33 +13158,6 @@ class dict(iterable, **kwargs) Added in version 3.9. - Dictionaries compare equal if and only if they have the same "(key, - value)" pairs (regardless of ordering). Order comparisons (‘<’, - ‘<=’, ‘>=’, ‘>’) raise "TypeError". - - Dictionaries preserve insertion order. Note that updating a key - does not affect the order. Keys added after deletion are inserted - at the end. - - >>> d = {"one": 1, "two": 2, "three": 3, "four": 4} - >>> d - {'one': 1, 'two': 2, 'three': 3, 'four': 4} - >>> list(d) - ['one', 'two', 'three', 'four'] - >>> list(d.values()) - [1, 2, 3, 4] - >>> d["one"] = 42 - >>> d - {'one': 42, 'two': 2, 'three': 3, 'four': 4} - >>> del d["two"] - >>> d["two"] = None - >>> d - {'one': 42, 'three': 3, 'four': 4, 'two': None} - - Changed in version 3.7: Dictionary order is guaranteed to be - insertion order. This behavior was an implementation detail of - CPython from 3.6. - Dictionaries and dictionary views are reversible. >>> d = {"one": 1, "two": 2, "three": 3, "four": 4} @@ -12163,7 +13384,7 @@ class dict(iterable, **kwargs) | "s * n" or "n * s" | equivalent to adding *s* to | (2)(7) | | | itself *n* times | | +----------------------------+----------------------------------+------------+ -| "s[i]" | *i*th item of *s*, origin 0 | (3) | +| "s[i]" | *i*th item of *s*, origin 0 | (3)(8) | +----------------------------+----------------------------------+------------+ | "s[i:j]" | slice of *s* from *i* to *j* | (3)(4) | +----------------------------+----------------------------------+------------+ @@ -12176,13 +13397,6 @@ class dict(iterable, **kwargs) +----------------------------+----------------------------------+------------+ | "max(s)" | largest item of *s* | | +----------------------------+----------------------------------+------------+ -| "s.index(x[, i[, j]])" | index of the first occurrence of | (8) | -| | *x* in *s* (at or after index | | -| | *i* and before index *j*) | | -+----------------------------+----------------------------------+------------+ -| "s.count(x)" | total number of occurrences of | | -| | *x* in *s* | | -+----------------------------+----------------------------------+------------+ Sequences of the same type also support comparisons. In particular, tuples and lists are compared lexicographically by comparing @@ -12279,13 +13493,31 @@ class dict(iterable, **kwargs) that follow specific patterns, and hence don’t support sequence concatenation or repetition. -8. "index" raises "ValueError" when *x* is not found in *s*. Not all - implementations support passing the additional arguments *i* and - *j*. These arguments allow efficient searching of subsections of - the sequence. Passing the extra arguments is roughly equivalent to - using "s[i:j].index(x)", only without copying any data and with the - returned index being relative to the start of the sequence rather - than the start of the slice. +8. An "IndexError" is raised if *i* is outside the sequence range. + +-[ Sequence Methods ]- + +Sequence types also support the following methods: + +sequence.count(value, /) + + Return the total number of occurrences of *value* in *sequence*. + +sequence.index(value[, start[, stop]) + + Return the index of the first occurrence of *value* in *sequence*. + + Raises "ValueError" if *value* is not found in *sequence*. + + The *start* or *stop* arguments allow for efficient searching of + subsections of the sequence, beginning at *start* and ending at + *stop*. This is roughly equivalent to "start + + sequence[start:stop].index(value)", only without copying any data. + + Caution: + + Not all sequence types support passing the *start* and *stop* + arguments. Immutable Sequence Types @@ -12321,11 +13553,15 @@ class dict(iterable, **kwargs) | "s[i] = x" | item *i* of *s* is replaced by | | | | *x* | | +--------------------------------+----------------------------------+-----------------------+ +| "del s[i]" | removes item *i* of *s* | | ++--------------------------------+----------------------------------+-----------------------+ | "s[i:j] = t" | slice of *s* from *i* to *j* is | | | | replaced by the contents of the | | | | iterable *t* | | +--------------------------------+----------------------------------+-----------------------+ -| "del s[i:j]" | same as "s[i:j] = []" | | +| "del s[i:j]" | removes the elements of "s[i:j]" | | +| | from the list (same as "s[i:j] = | | +| | []") | | +--------------------------------+----------------------------------+-----------------------+ | "s[i:j:k] = t" | the elements of "s[i:j:k]" are | (1) | | | replaced by those of *t* | | @@ -12333,64 +13569,80 @@ class dict(iterable, **kwargs) | "del s[i:j:k]" | removes the elements of | | | | "s[i:j:k]" from the list | | +--------------------------------+----------------------------------+-----------------------+ -| "s.append(x)" | appends *x* to the end of the | | -| | sequence (same as | | -| | "s[len(s):len(s)] = [x]") | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.clear()" | removes all items from *s* (same | (5) | -| | as "del s[:]") | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.copy()" | creates a shallow copy of *s* | (5) | -| | (same as "s[:]") | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.extend(t)" or "s += t" | extends *s* with the contents of | | +| "s += t" | extends *s* with the contents of | | | | *t* (for the most part the same | | | | as "s[len(s):len(s)] = t") | | +--------------------------------+----------------------------------+-----------------------+ -| "s *= n" | updates *s* with its contents | (6) | +| "s *= n" | updates *s* with its contents | (2) | | | repeated *n* times | | +--------------------------------+----------------------------------+-----------------------+ -| "s.insert(i, x)" | inserts *x* into *s* at the | | -| | index given by *i* (same as | | -| | "s[i:i] = [x]") | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.pop()" or "s.pop(i)" | retrieves the item at *i* and | (2) | -| | also removes it from *s* | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.remove(x)" | removes the first item from *s* | (3) | -| | where "s[i]" is equal to *x* | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.reverse()" | reverses the items of *s* in | (4) | -| | place | | -+--------------------------------+----------------------------------+-----------------------+ Notes: 1. If *k* is not equal to "1", *t* must have the same length as the slice it is replacing. -2. The optional argument *i* defaults to "-1", so that by default the - last item is removed and returned. - -3. "remove()" raises "ValueError" when *x* is not found in *s*. - -4. The "reverse()" method modifies the sequence in place for economy - of space when reversing a large sequence. To remind users that it - operates by side effect, it does not return the reversed sequence. - -5. "clear()" and "copy()" are included for consistency with the - interfaces of mutable containers that don’t support slicing - operations (such as "dict" and "set"). "copy()" is not part of the - "collections.abc.MutableSequence" ABC, but most concrete mutable - sequence classes provide it. - - Added in version 3.3: "clear()" and "copy()" methods. - -6. The value *n* is an integer, or an object implementing +2. The value *n* is an integer, or an object implementing "__index__()". Zero and negative values of *n* clear the sequence. Items in the sequence are not copied; they are referenced multiple times, as explained for "s * n" under Common Sequence Operations. +-[ Mutable Sequence Methods ]- + +Mutable sequence types also support the following methods: + +sequence.append(value, /) + + Append *value* to the end of the sequence This is equivalent to + writing "seq[len(seq):len(seq)] = [value]". + +sequence.clear() + + Added in version 3.3. + + Remove all items from *sequence*. This is equivalent to writing + "del sequence[:]". + +sequence.copy() + + Added in version 3.3. + + Create a shallow copy of *sequence*. This is equivalent to writing + "sequence[:]". + + Hint: + + The "copy()" method is not part of the "MutableSequence" "ABC", + but most concrete mutable sequence types provide it. + +sequence.extend(iterable, /) + + Extend *sequence* with the contents of *iterable*. For the most + part, this is the same as writing "seq[len(seq):len(seq)] = + iterable". + +sequence.insert(index, value, /) + + Insert *value* into *sequence* at the given *index*. This is + equivalent to writing "sequence[index:index] = [value]". + +sequence.pop(index=-1, /) + + Retrieve the item at *index* and also removes it from *sequence*. + By default, the last item in *sequence* is removed and returned. + +sequence.remove(value, /) + + Remove the first item from *sequence* where "sequence[i] == value". + + Raises "ValueError" if *value* is not found in *sequence*. + +sequence.reverse() + + Reverse the items of *sequence* in place. This method maintains + economy of space when reversing a large sequence. To remind users + that it operates by side-effect, it returns "None". + Lists ===== @@ -12399,7 +13651,7 @@ class dict(iterable, **kwargs) homogeneous items (where the precise degree of similarity will vary by application). -class list([iterable]) +class list(iterable=(), /) Lists may be constructed in several ways: @@ -12480,7 +13732,7 @@ class list([iterable]) of homogeneous data is needed (such as allowing storage in a "set" or "dict" instance). -class tuple([iterable]) +class tuple(iterable=(), /) Tuples may be constructed in a number of ways: @@ -12520,8 +13772,8 @@ class tuple([iterable]) The "range" type represents an immutable sequence of numbers and is commonly used for looping a specific number of times in "for" loops. -class range(stop) -class range(start, stop[, step]) +class range(stop, /) +class range(start, stop, step=1, /) The arguments to the range constructor must be integers (either built-in "int" or any object that implements the "__index__()" @@ -12649,11 +13901,15 @@ class range(start, stop[, step]) | "s[i] = x" | item *i* of *s* is replaced by | | | | *x* | | +--------------------------------+----------------------------------+-----------------------+ +| "del s[i]" | removes item *i* of *s* | | ++--------------------------------+----------------------------------+-----------------------+ | "s[i:j] = t" | slice of *s* from *i* to *j* is | | | | replaced by the contents of the | | | | iterable *t* | | +--------------------------------+----------------------------------+-----------------------+ -| "del s[i:j]" | same as "s[i:j] = []" | | +| "del s[i:j]" | removes the elements of "s[i:j]" | | +| | from the list (same as "s[i:j] = | | +| | []") | | +--------------------------------+----------------------------------+-----------------------+ | "s[i:j:k] = t" | the elements of "s[i:j:k]" are | (1) | | | replaced by those of *t* | | @@ -12661,63 +13917,79 @@ class range(start, stop[, step]) | "del s[i:j:k]" | removes the elements of | | | | "s[i:j:k]" from the list | | +--------------------------------+----------------------------------+-----------------------+ -| "s.append(x)" | appends *x* to the end of the | | -| | sequence (same as | | -| | "s[len(s):len(s)] = [x]") | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.clear()" | removes all items from *s* (same | (5) | -| | as "del s[:]") | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.copy()" | creates a shallow copy of *s* | (5) | -| | (same as "s[:]") | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.extend(t)" or "s += t" | extends *s* with the contents of | | +| "s += t" | extends *s* with the contents of | | | | *t* (for the most part the same | | | | as "s[len(s):len(s)] = t") | | +--------------------------------+----------------------------------+-----------------------+ -| "s *= n" | updates *s* with its contents | (6) | +| "s *= n" | updates *s* with its contents | (2) | | | repeated *n* times | | +--------------------------------+----------------------------------+-----------------------+ -| "s.insert(i, x)" | inserts *x* into *s* at the | | -| | index given by *i* (same as | | -| | "s[i:i] = [x]") | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.pop()" or "s.pop(i)" | retrieves the item at *i* and | (2) | -| | also removes it from *s* | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.remove(x)" | removes the first item from *s* | (3) | -| | where "s[i]" is equal to *x* | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.reverse()" | reverses the items of *s* in | (4) | -| | place | | -+--------------------------------+----------------------------------+-----------------------+ Notes: 1. If *k* is not equal to "1", *t* must have the same length as the slice it is replacing. -2. The optional argument *i* defaults to "-1", so that by default the - last item is removed and returned. - -3. "remove()" raises "ValueError" when *x* is not found in *s*. - -4. The "reverse()" method modifies the sequence in place for economy - of space when reversing a large sequence. To remind users that it - operates by side effect, it does not return the reversed sequence. - -5. "clear()" and "copy()" are included for consistency with the - interfaces of mutable containers that don’t support slicing - operations (such as "dict" and "set"). "copy()" is not part of the - "collections.abc.MutableSequence" ABC, but most concrete mutable - sequence classes provide it. - - Added in version 3.3: "clear()" and "copy()" methods. - -6. The value *n* is an integer, or an object implementing +2. The value *n* is an integer, or an object implementing "__index__()". Zero and negative values of *n* clear the sequence. Items in the sequence are not copied; they are referenced multiple times, as explained for "s * n" under Common Sequence Operations. + +-[ Mutable Sequence Methods ]- + +Mutable sequence types also support the following methods: + +sequence.append(value, /) + + Append *value* to the end of the sequence This is equivalent to + writing "seq[len(seq):len(seq)] = [value]". + +sequence.clear() + + Added in version 3.3. + + Remove all items from *sequence*. This is equivalent to writing + "del sequence[:]". + +sequence.copy() + + Added in version 3.3. + + Create a shallow copy of *sequence*. This is equivalent to writing + "sequence[:]". + + Hint: + + The "copy()" method is not part of the "MutableSequence" "ABC", + but most concrete mutable sequence types provide it. + +sequence.extend(iterable, /) + + Extend *sequence* with the contents of *iterable*. For the most + part, this is the same as writing "seq[len(seq):len(seq)] = + iterable". + +sequence.insert(index, value, /) + + Insert *value* into *sequence* at the given *index*. This is + equivalent to writing "sequence[index:index] = [value]". + +sequence.pop(index=-1, /) + + Retrieve the item at *index* and also removes it from *sequence*. + By default, the last item in *sequence* is removed and returned. + +sequence.remove(value, /) + + Remove the first item from *sequence* where "sequence[i] == value". + + Raises "ValueError" if *value* is not found in *sequence*. + +sequence.reverse() + + Reverse the items of *sequence* in place. This method maintains + economy of space when reversing a large sequence. To remind users + that it operates by side-effect, it returns "None". ''', 'unary': r'''Unary arithmetic and bitwise operations *************************************** diff --git a/Misc/NEWS.d/3.15.0a1.rst b/Misc/NEWS.d/3.15.0a1.rst new file mode 100644 index 00000000000..5331f270162 --- /dev/null +++ b/Misc/NEWS.d/3.15.0a1.rst @@ -0,0 +1,6438 @@ +.. date: 2025-10-14-00-17-48 +.. gh-issue: 115119 +.. nonce: 470I1N +.. release date: 2025-10-14 +.. section: macOS + +Update macOS installer to use libmpdecimal 4.0.1. + +.. + +.. date: 2025-10-14-00-08-16 +.. gh-issue: 124111 +.. nonce: 7-j-DQ +.. section: macOS + +Update macOS installer to use Tcl/Tk 9.0.2. + +.. + +.. date: 2025-10-13-23-46-12 +.. gh-issue: 132339 +.. nonce: kAp603 +.. section: macOS + +Update macOS installer version of OpenSSL to 3.5.4. + +.. + +.. date: 2025-08-06-06-29-12 +.. gh-issue: 137450 +.. nonce: JZypb7 +.. section: macOS + +macOS installer shell path management improvements: separate the installer +``Shell profile updater`` postinstall script from the ``Update Shell +Profile.command`` to enable more robust error handling. + +.. + +.. date: 2025-07-27-02-17-40 +.. gh-issue: 137134 +.. nonce: pjgITs +.. section: macOS + +Update macOS installer to ship with SQLite version 3.50.4. + +.. + +.. date: 2025-10-08-22-54-38 +.. gh-issue: 139810 +.. nonce: LAaemi +.. section: Windows + +Installing with ``py install 3[.x]-dev`` will now select final versions as +well as prereleases. + +.. + +.. date: 2025-10-04-12-18-45 +.. gh-issue: 139573 +.. nonce: EO9kVB +.. section: Windows + +Updated bundled version of OpenSSL to 3.0.18. + +.. + +.. date: 2025-09-15-15-34-29 +.. gh-issue: 138896 +.. nonce: lkiF_7 +.. section: Windows + +Fix error installing C runtime on non-updated Windows machines + +.. + +.. date: 2025-09-03-01-07-44 +.. gh-issue: 138314 +.. nonce: IeWQ2i +.. section: Windows + +Add :func:`winreg.DeleteTree`. + +.. + +.. date: 2025-07-27-14-25-11 +.. gh-issue: 137136 +.. nonce: xNthFT +.. section: Windows + +Suppress build warnings when build on Windows with +``--experimental-jit-interpreter``. + +.. + +.. date: 2025-07-27-02-16-53 +.. gh-issue: 137134 +.. nonce: W0WpDF +.. section: Windows + +Update Windows installer to ship with SQLite 3.50.4. + +.. + +.. date: 2025-06-03-18-26-54 +.. gh-issue: 135099 +.. nonce: Q9usKm +.. section: Windows + +Fix a crash that could occur on Windows when a background thread waits on a +:c:type:`PyMutex` while the main thread is shutting down the interpreter. + +.. + +.. date: 2025-05-20-21-43-20 +.. gh-issue: 130727 +.. nonce: -69t4D +.. section: Windows + +Fix a race in internal calls into WMI that can result in an "invalid handle" +exception under high load. Patch by Chris Eibl. + +.. + +.. date: 2025-05-19-03-02-04 +.. gh-issue: 76023 +.. nonce: vHOf6M +.. section: Windows + +Make :func:`os.path.realpath` ignore Windows error 1005 when in non-strict +mode. + +.. + +.. date: 2025-05-13-13-25-27 +.. gh-issue: 133779 +.. nonce: -YcTBz +.. section: Windows + +Reverts the change to generate different :file:`pyconfig.h` files based on +compiler settings, as it was frequently causing extension builds to break. +In particular, the ``Py_GIL_DISABLED`` preprocessor variable must now always +be defined explicitly when compiling for the experimental free-threaded +runtime. The :func:`sysconfig.get_config_var` function can be used to +determine whether the current runtime was compiled with that flag or not. + +.. + +.. date: 2025-05-08-19-07-26 +.. gh-issue: 133626 +.. nonce: yFTKYK +.. section: Windows + +Ensures packages are not accidentally bundled into the traditional +installer. + +.. + +.. date: 2025-05-07-13-04-22 +.. gh-issue: 133580 +.. nonce: jBMujJ +.. section: Windows + +Fix :func:`sys.getwindowsversion` failing without setting an exception when +called on some WinAPI partitions. + +.. + +.. date: 2025-05-07-11-45-30 +.. gh-issue: 133572 +.. nonce: Xc2zxH +.. section: Windows + +Avoid LsaNtStatus to WinError conversion on unsupported WinAPI partitions. + +.. + +.. date: 2025-05-07-11-25-29 +.. gh-issue: 133568 +.. nonce: oYV0d8 +.. section: Windows + +Fix compile error when using a WinAPI partition that doesn't support the RPC +runtime library. + +.. + +.. date: 2025-05-07-09-02-19 +.. gh-issue: 133562 +.. nonce: lqqNW1 +.. section: Windows + +Disable handling of security descriptors by :func:`os.mkdir` with mode +``0o700`` on WinAPI partitions that do not support it. This only affects +custom builds for specialized targets. + +.. + +.. date: 2025-05-07-08-19-15 +.. gh-issue: 133537 +.. nonce: yzf963 +.. section: Windows + +Avoid using console I/O in WinAPI partitions that don’t support it + +.. + +.. date: 2025-03-31-15-37-57 +.. gh-issue: 131942 +.. nonce: jip_aL +.. section: Windows + +Use the Python-specific :c:macro:`Py_DEBUG` macro rather than +:c:macro:`!_DEBUG` in Windows-related C code. Patch by Xuehai Pan. + +.. + +.. date: 2025-09-25-10-31-02 +.. gh-issue: 139330 +.. nonce: 5WWkY0 +.. section: Tools/Demos + +SBOM generation tool didn't cross-check the version and checksum values +against the ``Modules/expat/refresh.sh`` script, leading to the values +becoming out-of-date during routine updates. + +.. + +.. date: 2025-08-28-06-22-26 +.. gh-issue: 132006 +.. nonce: eZQmc6 +.. section: Tools/Demos + +XCframeworks now include privacy manifests to satisfy Apple App Store +submission requirements. + +.. + +.. date: 2025-08-27-11-14-53 +.. gh-issue: 138171 +.. nonce: Suz8ob +.. section: Tools/Demos + +A script for building an iOS XCframework was added. As part of this change, +the top level ``iOS`` folder has been moved to be a subdirectory of the +``Apple`` folder. + +.. + +.. date: 2025-08-21-14-04-50 +.. gh-issue: 137873 +.. nonce: qxffLt +.. section: Tools/Demos + +The iOS test runner has been simplified, resolving some issues that have +been observed using the runner in GitHub Actions and Azure Pipelines test +environments. + +.. + +.. date: 2025-08-06-11-54-55 +.. gh-issue: 137484 +.. nonce: 8iFAQs +.. section: Tools/Demos + +Have ``Tools/wasm/wasi`` put the build Python into a directory named after +the build triple instead of "build". + +.. + +.. date: 2025-08-01-13-27-43 +.. gh-issue: 137025 +.. nonce: ubuhQC +.. section: Tools/Demos + +The ``wasm_build.py`` script has been removed. ``Tools/wasm/emscripten`` +and ``Tools/wasm/wasi`` should be used instead, as described in the `Dev +Guide `__. + +.. + +.. date: 2025-07-30-11-15-47 +.. gh-issue: 137248 +.. nonce: 8IxwY3 +.. section: Tools/Demos + +Add a ``--logdir`` option to ``Tools/wasm/wasi`` for specifying where to +write log files. + +.. + +.. date: 2025-07-30-10-28-35 +.. gh-issue: 137243 +.. nonce: NkdUqH +.. section: Tools/Demos + +Have Tools/wasm/wasi detect a WASI SDK install in /opt when it was directly +extracted from a release tarball. + +.. + +.. date: 2025-07-05-15-10-42 +.. gh-issue: 136251 +.. nonce: GRM6o8 +.. section: Tools/Demos + +Fixes and usability improvements for ``Tools/wasm/emscripten/web_example`` + +.. + +.. date: 2025-06-26-15-58-13 +.. gh-issue: 135968 +.. nonce: C4v_-W +.. section: Tools/Demos + +Stubs for ``strip`` are now provided as part of an iOS install. + +.. + +.. date: 2025-06-11-12-14-06 +.. gh-issue: 135379 +.. nonce: 25ttXq +.. section: Tools/Demos + +The cases generator no longer accepts type annotations on stack items. +Conversions to non-default types are now done explicitly in bytecodes.c and +optimizer_bytecodes.c. This will simplify code generation for top-of-stack +caching and other future features. + +.. + +.. date: 2025-05-19-14-57-46 +.. gh-issue: 134215 +.. nonce: sbdDK6 +.. section: Tools/Demos + +:term:`REPL` import autocomplete only suggests private modules when +explicitly specified. + +.. + +.. date: 2025-09-22-15-40-09 +.. gh-issue: 139208 +.. nonce: Tc13dl +.. section: Tests + +Fix regrtest ``--fast-ci --verbose``: don't ignore the ``--verbose`` option +anymore. Patch by Victor Stinner. + +.. + +.. date: 2025-09-21-16-00-30 +.. gh-issue: 138313 +.. nonce: lBx2en +.. section: Tests + +Restore skipped test and add janky workaround to prevent select buildbots +from failing with a ResourceWarning. + +.. + +.. date: 2025-06-26-15-15-35 +.. gh-issue: 135966 +.. nonce: EBpF8Y +.. section: Tests + +The iOS testbed now handles the ``app_packages`` folder as a site directory. + +.. + +.. date: 2025-06-19-15-29-38 +.. gh-issue: 135494 +.. nonce: FVl9a0 +.. section: Tests + +Fix regrtest to support excluding tests from ``--pgo`` tests. Patch by +Victor Stinner. + +.. + +.. date: 2025-06-17-08-48-08 +.. gh-issue: 132815 +.. nonce: CY1Esu +.. section: Tests + +Fix test__opcode: add ``JUMP_BACKWARD`` to specialization stats. + +.. + +.. date: 2025-06-14-13-20-17 +.. gh-issue: 135489 +.. nonce: Uh0yVO +.. section: Tests + +Show verbose output for failing tests during PGO profiling step with +--enable-optimizations. + +.. + +.. date: 2025-06-11-16-52-49 +.. gh-issue: 135401 +.. nonce: ccMXmL +.. section: Tests + +Add a new GitHub CI job to test the :mod:`ssl` module with `AWS-LC +`_ as the backing cryptography and TLS +library. + +.. + +.. date: 2025-06-04-13-07-44 +.. gh-issue: 135120 +.. nonce: NapnZT +.. section: Tests + +Add :func:`!test.support.subTests`. + +.. + +.. date: 2025-05-23-09-19-52 +.. gh-issue: 134567 +.. nonce: hwEIMb +.. section: Tests + +Expose log formatter to users in TestCase.assertLogs. +:func:`unittest.TestCase.assertLogs` will now optionally accept a formatter +that will be used to format the strings in output if provided. + +.. + +.. date: 2025-05-09-14-54-48 +.. gh-issue: 133744 +.. nonce: LCquu0 +.. section: Tests + +Fix multiprocessing interrupt test. Add an event to synchronize the parent +process with the child process: wait until the child process starts +sleeping. Patch by Victor Stinner. + +.. + +.. date: 2025-05-09-04-11-06 +.. gh-issue: 133682 +.. nonce: -_lwo3 +.. section: Tests + +Fixed test case ``test.test_annotationlib.TestStringFormat.test_displays`` +which ensures proper handling of complex data structures (lists, sets, +dictionaries, and tuples) in string annotations. + +.. + +.. date: 2025-05-08-15-06-01 +.. gh-issue: 133639 +.. nonce: 50-kbV +.. section: Tests + +Fix ``TestPyReplAutoindent.test_auto_indent_default()`` doesn't run +``input_code``. + +.. + +.. date: 2025-10-07-19-31-34 +.. gh-issue: 139700 +.. nonce: vNHU1O +.. section: Security + +Check consistency of the zip64 end of central directory record. Support +records with "zip64 extensible data" if there are no bytes prepended to the +ZIP file. + +.. + +.. date: 2025-09-29-00-01-28 +.. gh-issue: 139400 +.. nonce: X2T-jO +.. section: Security + +:mod:`xml.parsers.expat`: Make sure that parent Expat parsers are only +garbage-collected once they are no longer referenced by subparsers created +by :meth:`~xml.parsers.expat.xmlparser.ExternalEntityParserCreate`. Patch by +Sebastian Pipping. + +.. + +.. date: 2025-09-24-13-39-56 +.. gh-issue: 139283 +.. nonce: jODz_q +.. section: Security + +:mod:`sqlite3`: correctly handle maximum number of rows to fetch in +:meth:`Cursor.fetchmany ` and reject negative +values for :attr:`Cursor.arraysize `. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-06-27-21-23-19 +.. gh-issue: 136053 +.. nonce: QZxcee +.. section: Security + +:mod:`marshal`: fix a possible crash when deserializing :class:`slice` +objects. + +.. + +.. date: 2025-06-25-14-13-39 +.. gh-issue: 135661 +.. nonce: idjQ0B +.. section: Security + +Fix parsing start and end tags in :class:`html.parser.HTMLParser` according +to the HTML5 standard. + +* Whitespaces no longer accepted between ```` does not end the script section. + +* Vertical tabulation (``\v``) and non-ASCII whitespaces no longer recognized + as whitespaces. The only whitespaces are ``\t\n\r\f`` and space. + +* Null character (U+0000) no longer ends the tag name. + +* Attributes and slashes after the tag name in end tags are now ignored, + instead of terminating after the first ``>`` in quoted attribute value. + E.g. ````. + +* Multiple slashes and whitespaces between the last attribute and closing ``>`` + are now ignored in both start and end tags. E.g. ````. + +* Multiple ``=`` between attribute name and value are no longer collapsed. + E.g. ```` produces attribute "foo" with value "=bar". + +.. + +.. date: 2025-06-18-13-34-55 +.. gh-issue: 135661 +.. nonce: NZlpWf +.. section: Security + +Fix CDATA section parsing in :class:`html.parser.HTMLParser` according to +the HTML5 standard: ``] ]>`` and ``]] >`` no longer end the CDATA section. +Add private method ``_set_support_cdata()`` which can be used to specify how +to parse ``<[CDATA[`` --- as a CDATA section in foreign content (SVG or +MathML) or as a bogus comment in the HTML namespace. + +.. + +.. date: 2025-06-18-13-28-08 +.. gh-issue: 102555 +.. nonce: nADrzJ +.. section: Security + +Fix comment parsing in :class:`html.parser.HTMLParser` according to the +HTML5 standard. ``--!>`` now ends the comment. ``-- >`` no longer ends the +comment. Support abnormally ended empty comments ``<-->`` and ``<--->``. + +.. + +.. date: 2025-06-13-15-55-22 +.. gh-issue: 135462 +.. nonce: KBeJpc +.. section: Security + +Fix quadratic complexity in processing specially crafted input in +:class:`html.parser.HTMLParser`. End-of-file errors are now handled +according to the HTML5 specs -- comments and declarations are automatically +closed, tags are ignored. + +.. + +.. date: 2025-06-09-20-38-25 +.. gh-issue: 118350 +.. nonce: KgWCcP +.. section: Security + +Fix support of escapable raw text mode (elements "textarea" and "title") in +:class:`html.parser.HTMLParser`. + +.. + +.. date: 2025-06-02-11-32-23 +.. gh-issue: 135034 +.. nonce: RLGjbp +.. section: Security + +Fixes multiple issues that allowed ``tarfile`` extraction filters +(``filter="data"`` and ``filter="tar"``) to be bypassed using crafted +symlinks and hard links. + +Addresses :cve:`2024-12718`, :cve:`2025-4138`, :cve:`2025-4330`, and +:cve:`2025-4517`. + +.. + +.. date: 2025-05-09-20-22-54 +.. gh-issue: 133767 +.. nonce: kN2i3Q +.. section: Security + +Fix use-after-free in the "unicode-escape" decoder with a non-"strict" error +handler. + +.. + +.. date: 2025-05-07-22-49-27 +.. gh-issue: 133623 +.. nonce: fgWkBm +.. section: Security + +Indicate through :data:`ssl.HAS_PSK_TLS13` whether the :mod:`ssl` module +supports "External PSKs" in TLSv1.3, as described in RFC 9258. Patch by Will +Childs-Klein. + +.. + +.. date: 2025-01-14-11-19-07 +.. gh-issue: 128840 +.. nonce: M1doZW +.. section: Security + +Short-circuit the processing of long IPv6 addresses early in +:mod:`ipaddress` to prevent excessive memory consumption and a minor +denial-of-service. + +.. + +.. date: 2025-10-11-20-03-13 +.. gh-issue: 139482 +.. nonce: du2Stg +.. section: Library + +Optimize :data:`os.environ.clear() ` by calling +:manpage:`clearenv(3)` when this function is available. Patch by Victor +Stinner. + +.. + +.. date: 2025-10-11-17-41-26 +.. gh-issue: 139958 +.. nonce: AnCakj +.. section: Library + +The ``application/toml`` mime type is now supported by :mod:`mimetypes`. +Patch by Gil Forcada. + +.. + +.. date: 2025-10-11-14-37-42 +.. gh-issue: 139823 +.. nonce: uGF4oh +.. section: Library + +:mod:`ensurepip` now fails with a nicer error message when the :mod:`zlib` +module is not available. + +.. + +.. date: 2025-10-11-10-02-56 +.. gh-issue: 139905 +.. nonce: UyJIR_ +.. section: Library + +Add suggestion to error message for :class:`typing.Generic` subclasses when +``cls.__parameters__`` is missing due to a parent class failing to call +:meth:`super().__init_subclass__() ` in its +``__init_subclass__``. + +.. + +.. date: 2025-10-10-11-22-50 +.. gh-issue: 139894 +.. nonce: ECAXqj +.. section: Library + +Fix incorrect sharing of current task with the child process while forking +in :mod:`asyncio`. Patch by Kumar Aditya. + +.. + +.. date: 2025-10-09-21-37-20 +.. gh-issue: 139845 +.. nonce: dzx5UP +.. section: Library + +Fix to not print KeyboardInterrupt twice in default asyncio REPL. + +.. + +.. date: 2025-10-09-13-48-28 +.. gh-issue: 139783 +.. nonce: __NUgo +.. section: Library + +Fix :func:`inspect.getsourcelines` for the case when a decorator is followed +by a comment or an empty line. + +.. + +.. date: 2025-10-09-03-06-19 +.. gh-issue: 139809 +.. nonce: lzHJNu +.. section: Library + +Prevent premature colorization of subparser ``prog`` in +:meth:`argparse.ArgumentParser.add_subparsers` to respect color environment +variable changes after parser creation. + +.. + +.. date: 2025-10-08-00-06-30 +.. gh-issue: 139736 +.. nonce: baPeBd +.. section: Library + +Fix excessive indentation in the default :mod:`argparse` +:class:`!HelpFormatter`. Patch by Alexander Edland. + +.. + +.. date: 2025-10-02-17-40-10 +.. gh-issue: 70765 +.. nonce: zVlLZn +.. section: Library + +:mod:`http.server`: fix default handling of HTTP/0.9 requests in +:class:`~http.server.BaseHTTPRequestHandler`. Previously, +:meth:`!BaseHTTPRequestHandler.parse_request` incorrectly waited for headers +in the request although those are not supported in HTTP/0.9. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-10-02-15-45-08 +.. gh-issue: 139322 +.. nonce: rouPGj +.. section: Library + +Fix :func:`os.getlogin` error handling: fix the error number. Patch by +Victor Stinner. + +.. + +.. date: 2025-10-01-20-30-03 +.. gh-issue: 135953 +.. nonce: NAofJl +.. section: Library + +Add a Gecko format output to the tachyon profiler via ``--gecko``. + +.. + +.. date: 2025-09-29-14-15-20 +.. gh-issue: 139184 +.. nonce: dNl9O4 +.. section: Library + +:func:`os.forkpty` does now make the returned file descriptor +non-inheritable. + +.. + +.. date: 2025-09-28-16-34-11 +.. gh-issue: 139391 +.. nonce: nRFnmx +.. section: Library + +Fix an issue when, on non-Windows platforms, it was not possible to +gracefully exit a ``python -m asyncio`` process suspended by Ctrl+Z and +later resumed by :manpage:`fg` other than with :manpage:`kill`. + +.. + +.. date: 2025-09-27-08-26-31 +.. gh-issue: 139374 +.. nonce: hfh-dl +.. section: Library + +:mod:`timeit`: Add color to error tracebacks. + +.. + +.. date: 2025-09-26-18-04-28 +.. gh-issue: 90949 +.. nonce: YHjSzX +.. section: Library + +Add +:meth:`~xml.parsers.expat.xmlparser.SetBillionLaughsAttackProtectionActivationThreshold` +and +:meth:`~xml.parsers.expat.xmlparser.SetBillionLaughsAttackProtectionMaximumAmplification` +to :ref:`xmlparser ` objects to tune protections against +`billion laughs `_ +attacks. Patch by Bénédikt Tran. + +.. + +.. date: 2025-09-25-07-33-43 +.. gh-issue: 139312 +.. nonce: ygE8AC +.. section: Library + +Upgrade bundled libexpat to 2.7.3 + +.. + +.. date: 2025-09-24-14-17-34 +.. gh-issue: 139289 +.. nonce: Vmk25k +.. section: Library + +Do a real lazy-import on :mod:`rlcompleter` in :mod:`pdb` and restore the +existing completer after importing :mod:`rlcompleter`. + +.. + +.. date: 2025-09-22-14-40-11 +.. gh-issue: 90949 +.. nonce: UM35nb +.. section: Library + +Add :meth:`~xml.parsers.expat.xmlparser.SetAllocTrackerActivationThreshold` +and :meth:`~xml.parsers.expat.xmlparser.SetAllocTrackerMaximumAmplification` +to :ref:`xmlparser ` objects to tune protections against +disproportional amounts of dynamic memory usage from within an Expat parser. +Patch by Bénédikt Tran. + +.. + +.. date: 2025-09-22-11-30-45 +.. gh-issue: 67795 +.. nonce: fROoZt +.. section: Library + +Functions that take timestamp or timeout arguments now accept any real +numbers (such as :class:`~decimal.Decimal` and +:class:`~fractions.Fraction`), not only integers or floats, although this +does not improve precision. + +.. + +.. date: 2025-09-22-11-19-05 +.. gh-issue: 95953 +.. nonce: 7oLoag +.. section: Library + +A CSS class, ``diff_changed``, was added to the changed lines in the +``make_table`` output of :class:`difflib.HtmlDiff`. Patch by Katie Gardner. + +.. + +.. date: 2025-09-21-15-58-57 +.. gh-issue: 139210 +.. nonce: HGbMvz +.. section: Library + +Fix use-after-free when reporting unknown event in +:func:`xml.etree.ElementTree.iterparse`. Patch by Ken Jin. + +.. + +.. date: 2025-09-20-17-50-31 +.. gh-issue: 138860 +.. nonce: Y9JXap +.. section: Library + +Lazy import :mod:`rlcompleter` in :mod:`pdb` to avoid deadlock in +subprocess. + +.. + +.. date: 2025-09-19-09-36-42 +.. gh-issue: 112729 +.. nonce: mmty0_ +.. section: Library + +Fix crash when calling :func:`concurrent.interpreters.create` when the +process is out of memory. + +.. + +.. date: 2025-09-19-07-41-52 +.. gh-issue: 126016 +.. nonce: Uz9W6h +.. section: Library + +Fix an assertion failure when sending :exc:`KeyboardInterrupt` to a Python +process running a subinterpreter in a separate thread. + +.. + +.. date: 2025-09-18-14-21-57 +.. gh-issue: 118803 +.. nonce: 2JPbto +.. section: Library + +:class:`collections.abc.ByteString` has been removed from +``collections.abc.__all__``, and :class:`typing.ByteString` has been removed +from ``typing.__all__``. The former has been deprecated since Python 3.12, +and the latter has been deprecated since Python 3.9. Both classes are +scheduled for removal in Python 3.17. + +Additionally, the following statements now cause ``DeprecationWarning``\ s +to be emitted at runtime: ``from collections.abc import ByteString``, ``from +typing import ByteString``, ``import collections.abc; +collections.abc.ByteString`` and ``import typing; typing.ByteString``. Both +classes already caused ``DeprecationWarning``\ s to be emitted if they were +subclassed or used as the second argument to ``isinstance()`` or +``issubclass()``, but they did not previously lead to +``DeprecationWarning``\ s if they were merely imported or accessed from +their respective modules. + +.. + +.. date: 2025-09-18-05-32-18 +.. gh-issue: 135729 +.. nonce: 8AmMza +.. section: Library + +Fix unraisable exception during finalization when using +:mod:`concurrent.interpreters` in the REPL. + +.. + +.. date: 2025-09-17-21-54-53 +.. gh-issue: 139076 +.. nonce: 2eX9lG +.. section: Library + +Fix a bug in the :mod:`pydoc` module that was hiding functions in a Python +module if they were implemented in an extension module and the module did +not have ``__all__``. + +.. + +.. date: 2025-09-17-21-52-30 +.. gh-issue: 139090 +.. nonce: W7vbhF +.. section: Library + +Add :data:`os.RWF_DONTCACHE` constant for Linux 6.14+. + +.. + +.. date: 2025-09-17-19-08-34 +.. gh-issue: 139065 +.. nonce: Hu8fM5 +.. section: Library + +Fix trailing space before a wrapped long word if the line length is exactly +*width* in :mod:`textwrap`. + +.. + +.. date: 2025-09-17-12-07-21 +.. gh-issue: 139001 +.. nonce: O6tseN +.. section: Library + +Fix race condition in :class:`pathlib.Path` on the internal ``_raw_paths`` +field. + +.. + +.. date: 2025-09-17-08-32-43 +.. gh-issue: 138813 +.. nonce: LHkHjX +.. section: Library + +:class:`!multiprocessing.BaseProcess` defaults ``kwargs`` to ``None`` +instead of a shared dictionary. + +.. + +.. date: 2025-09-16-19-05-29 +.. gh-issue: 138998 +.. nonce: URl0Y_ +.. section: Library + +Update bundled libexpat to 2.7.2 + +.. + +.. date: 2025-09-16-16-46-58 +.. gh-issue: 138993 +.. nonce: -8s8_T +.. section: Library + +Dedent :data:`credits` text. + +.. + +.. date: 2025-09-16-15-56-29 +.. gh-issue: 118803 +.. nonce: aOPtmL +.. section: Library + +Add back :class:`collections.abc.ByteString` and :class:`typing.ByteString`. +Both had been removed in prior alpha, beta and release candidates for Python +3.14, but their removal has now been postponed to Python 3.17. + +.. + +.. date: 2025-09-15-19-29-12 +.. gh-issue: 130567 +.. nonce: shDEnT +.. section: Library + +Fix possible crash in :func:`locale.strxfrm` due to a platform bug on macOS. + +.. + +.. date: 2025-09-15-13-09-19 +.. gh-issue: 137226 +.. nonce: HH3_ik +.. section: Library + +Fix :func:`typing.get_type_hints` calls on generic :class:`typing.TypedDict` +classes defined with string annotations. + +.. + +.. date: 2025-09-15-08-57-39 +.. gh-issue: 138899 +.. nonce: Uh6fvY +.. section: Library + +Executing ``quit`` command in :mod:`pdb` will raise :exc:`bdb.BdbQuit` when +:mod:`pdb` is started from an asyncio console using :func:`breakpoint` or +:func:`pdb.set_trace`. + +.. + +.. date: 2025-09-12-01-01-05 +.. gh-issue: 138804 +.. nonce: 46ZukT +.. section: Library + +Raise :exc:`TypeError` instead of :exc:`AttributeError` when an argument of +incorrect type is passed to :func:`shlex.quote`. This restores the behavior +of the function prior to 3.14. + +.. + +.. date: 2025-09-11-11-09-28 +.. gh-issue: 138779 +.. nonce: TNZnLr +.. section: Library + +Support device numbers larger than ``2**63-1`` for the +:attr:`~os.stat_result.st_rdev` field of the :class:`os.stat_result` +structure. + +.. + +.. date: 2025-09-10-13-32-25 +.. gh-issue: 138682 +.. nonce: iExqx1 +.. section: Library + +Added symmetric difference support to :class:`collections.Counter` objects. + +.. + +.. date: 2025-09-10-10-11-59 +.. gh-issue: 138712 +.. nonce: avrPG5 +.. section: Library + +Add :const:`os.NODEV`. + +.. + +.. date: 2025-09-10-10-02-59 +.. gh-issue: 128636 +.. nonce: ldRKGZ +.. section: Library + +Fix crash in PyREPL when os.environ is overwritten with an invalid value for +mac + +.. + +.. date: 2025-09-09-17-57-49 +.. gh-issue: 138720 +.. nonce: hAtsm- +.. section: Library + +Fix an issue where :class:`io.BufferedWriter` and :class:`io.BufferedRandom` +had different definitions of "closed" for :meth:`~io.IOBase.close` and +:meth:`~io.IOBase.flush` which resulted in an exception when close called +flush but flush thought the file was already closed. + +.. + +.. date: 2025-09-09-10-48-26 +.. gh-issue: 138706 +.. nonce: xB--LX +.. section: Library + +Update :mod:`unicodedata` database to Unicode 17.0.0. + +.. + +.. date: 2025-09-08-17-32-02 +.. gh-issue: 76007 +.. nonce: peEgcr +.. section: Library + +Deprecate ``__version__`` from a number of standard library modules. Patch +by Hugo van Kemenade. + +.. + +.. date: 2025-09-06-20-09-32 +.. gh-issue: 138535 +.. nonce: mlntEe +.. section: Library + +Speed up :func:`os.stat` for files with reasonable timestamps. Contributed +by Jeffrey Bosboom. + +.. + +.. date: 2025-09-06-14-56-40 +.. gh-issue: 116946 +.. nonce: GGIeyO +.. section: Library + +:mod:`curses.panel`: the type of :func:`curses.panel.new_panel` is now +immutable. Patch by Bénédikt Tran. + +.. + +.. date: 2025-09-06-14-54-01 +.. gh-issue: 116946 +.. nonce: hzQEWI +.. section: Library + +:mod:`zlib`: the types of :func:`zlib.compressobj` and +:func:`zlib.decompressobj` are now immutable. Patch by Bénédikt Tran. + +.. + +.. date: 2025-09-06-14-53-19 +.. gh-issue: 116946 +.. nonce: c-npxd +.. section: Library + +:mod:`os`: the :class:`os.DirEntry` type and the type of :func:`os.scandir` +are now immutable. Patch by Bénédikt Tran. + +.. + +.. date: 2025-09-06-14-47-23 +.. gh-issue: 116946 +.. nonce: hj_u1t +.. section: Library + +:mod:`tkinter`: the types :class:`!_tkinter.Tcl_Obj` (wrapper for Tcl +objects), :class:`!_tkinter.tktimertoken` (obtained by calling +``createtimerhandler()`` on a :attr:`Tk ` application) and +:class:`!_tkinter.tkapp` (the runtime type of Tk applications) are now +immutable. Patch by Bénédikt Tran. + +.. + +.. date: 2025-09-06-11-26-21 +.. gh-issue: 138514 +.. nonce: 66ltOb +.. section: Library + +Raise :exc:`ValueError` when a multi-character string is passed to the +*echo_char* parameter of :func:`getpass.getpass`. Patch by Benjamin Johnson. + +.. + +.. date: 2025-09-05-21-10-24 +.. gh-issue: 137706 +.. nonce: 0EztiJ +.. section: Library + +Fix the partial evaluation of annotations that use ``typing.Annotated[T, +x]`` where ``T`` is a forward reference. + +.. + +.. date: 2025-09-05-15-35-59 +.. gh-issue: 88375 +.. nonce: dC491a +.. section: Library + +Fix normalization of the ``robots.txt`` rules and URLs in the +:mod:`urllib.robotparser` module. No longer ignore trailing ``?``. +Distinguish raw special characters ``?``, ``=`` and ``&`` from the +percent-encoded ones. + +.. + +.. date: 2025-09-05-07-50-18 +.. gh-issue: 138515 +.. nonce: E3M-pu +.. section: Library + +:mod:`email` is added to Emscripten build. + +.. + +.. date: 2025-09-05-05-53-43 +.. gh-issue: 99948 +.. nonce: KMSlG6 +.. section: Library + +:func:`ctypes.util.find_library` now works in Emscripten build. + +.. + +.. date: 2025-09-04-15-18-11 +.. gh-issue: 111788 +.. nonce: tuTEM5 +.. section: Library + +Fix parsing errors in the :mod:`urllib.robotparser` module. Don't fail +trying to parse weird paths. Don't fail trying to decode non-UTF-8 +``robots.txt`` files. + +.. + +.. date: 2025-09-03-15-20-10 +.. gh-issue: 138432 +.. nonce: RMc7UX +.. section: Library + +:meth:`zoneinfo.reset_tzpath` will now convert any :class:`os.PathLike` +objects it receives into strings before adding them to ``TZPATH``. It will +raise ``TypeError`` if anything other than a string is found after this +conversion. If given an :class:`os.PathLike` object that represents a +relative path, it will now raise ``ValueError`` instead of ``TypeError``, +and present a more informative error message. + +.. + +.. date: 2025-09-03-09-03-11 +.. gh-issue: 132657 +.. nonce: cbAIDh +.. section: Library + +Improve the scaling of :func:`copy.copy` and :func:`copy.deepcopy` in the +free-threading build. + +.. + +.. date: 2025-09-02-10-27-21 +.. gh-issue: 116946 +.. nonce: VxXNGD +.. section: Library + +The types of :func:`select.poll` and :func:`select.epoll` objects are now +immutable. Patch by Bénédikt Tran. + +.. + +.. date: 2025-09-02-10-23-09 +.. gh-issue: 116946 +.. nonce: U6RpwK +.. section: Library + +The :class:`!_random.Random` C type is now immutable. Patch by Bénédikt +Tran. + +.. + +.. date: 2025-08-31-22-10-22 +.. gh-issue: 57911 +.. nonce: N_Ixtv +.. section: Library + +When extracting tar files on Windows, slashes in symlink targets will be +replaced by backslashes to prevent corrupted links. + +.. + +.. date: 2025-08-31-12-34-02 +.. gh-issue: 138205 +.. nonce: iHXb1z +.. section: Library + +Removed the :meth:`~mmap.mmap.resize` method on platforms that don't support +the underlying syscall, instead of raising a :exc:`SystemError`. + +.. + +.. date: 2025-08-31-09-06-49 +.. gh-issue: 138008 +.. nonce: heOvsU +.. section: Library + +Fix segmentation faults in the :mod:`ctypes` module due to invalid +:attr:`~ctypes._CFuncPtr.argtypes`. Patch by Dung Nguyen. + +.. + +.. date: 2025-08-30-17-58-04 +.. gh-issue: 138252 +.. nonce: CDiEby +.. section: Library + +:mod:`ssl`: :class:`~ssl.SSLContext` objects can now set client and server +TLS signature algorithms. If Python has been built with OpenSSL 3.5 or +later, :class:`~ssl.SSLSocket` objects can return the signature algorithms +selected on a connection. + +.. + +.. date: 2025-08-30-10-58-15 +.. gh-issue: 138253 +.. nonce: 9Ehj-N +.. section: Library + +Add the *block* parameter in the :meth:`!put` and :meth:`!get` methods of +the :mod:`concurrent.interpreters` queues for compatibility with the +:class:`queue.Queue` interface. + +.. + +.. date: 2025-08-30-10-04-28 +.. gh-issue: 60462 +.. nonce: yh_vDc +.. section: Library + +Fix :func:`locale.strxfrm` on Solaris (and possibly other platforms). + +.. + +.. date: 2025-08-29-12-56-55 +.. gh-issue: 138239 +.. nonce: uthZFI +.. section: Library + +The REPL now highlights :keyword:`type` as a soft keyword in :ref:`type +statements `. + +.. + +.. date: 2025-08-29-12-05-33 +.. gh-issue: 78502 +.. nonce: VpIMxg +.. section: Library + +:class:`mmap.mmap` now has a *trackfd* parameter on Windows; if it is +``False``, the file handle corresponding to *fileno* will not be duplicated. + +.. + +.. date: 2025-08-28-13-20-09 +.. gh-issue: 138204 +.. nonce: 8oLOud +.. section: Library + +Forbid expansion of shared anonymous :mod:`memory maps ` on Linux, +which caused a bus error. + +.. + +.. date: 2025-08-27-17-05-36 +.. gh-issue: 138010 +.. nonce: ZZJmPL +.. section: Library + +Fix an issue where defining a class with a +:deco:`warnings.deprecated`-decorated base class may not invoke the correct +:meth:`~object.__init_subclass__` method in cases involving multiple +inheritance. Patch by Brian Schubert. + +.. + +.. date: 2025-08-25-22-38-03 +.. gh-issue: 134716 +.. nonce: kyYKeX +.. section: Library + +Add support of regular expressions in the :option:`-W` option and the +:envvar:`PYTHONWARNINGS` environment variable. + +.. + +.. date: 2025-08-25-18-06-04 +.. gh-issue: 138133 +.. nonce: Zh9rGo +.. section: Library + +Prevent infinite traceback loop when sending CTRL^C to Python through +``strace``. + +.. + +.. date: 2025-08-25-16-22-32 +.. gh-issue: 138122 +.. nonce: eMNDZ1 +.. section: Library + +Implement :pep:`799` -- A dedicated profiling package for organizing Python +profiling tools. Patch by Pablo Galindo. + +.. + +.. date: 2025-08-24-02-04-32 +.. gh-issue: 138092 +.. nonce: V4-wTO +.. section: Library + +Fixed a bug in :meth:`mmap.mmap.flush` where calling with only an offset +parameter would fail. + +.. + +.. date: 2025-08-22-12-48-14 +.. gh-issue: 138044 +.. nonce: lEQULC +.. section: Library + +Remove compatibility shim for deprecated parameter *package* in +:func:`importlib.resources.files`. Patch by Semyon Moroz. + +.. + +.. date: 2025-08-22-09-53-45 +.. gh-issue: 86819 +.. nonce: ECxvwx +.. section: Library + +:mod:`socket`: Add missing constants for ISO-TP sockets. + +.. + +.. date: 2025-08-19-00-12-57 +.. gh-issue: 137884 +.. nonce: 4faCA_ +.. section: Library + +Add :func:`threading.get_native_id` support for Illumos/Solaris. Patch by +Yüce Tekol. + +.. + +.. date: 2025-08-18-16-02-51 +.. gh-issue: 134869 +.. nonce: GnAjnU +.. section: Library + +Fix an issue where pressing Ctrl+C during tab completion in the REPL would +leave the autocompletion menu in a corrupted state. + +.. + +.. date: 2025-08-18-07-10-55 +.. gh-issue: 137840 +.. nonce: 9b7AnG +.. section: Library + +:class:`typing.TypedDict` now supports the ``closed`` and ``extra_items`` +keyword arguments (as described in :pep:`728`) to control whether additional +non-required keys are allowed and to specify their value type. + +.. + +.. date: 2025-08-17-10-22-31 +.. gh-issue: 132947 +.. nonce: XR4MJ8 +.. section: Library + +Applied changes to ``importlib.metadata`` from `importlib_metadata 8.7 +`_, +including ``dist`` now disallowed for ``EntryPoints.select``; deferred +imports for faster import times; added support for metadata with newlines +(python/cpython#119650); and ``metadata()`` function now returns ``None`` +when a metadata directory is present but no metadata is present. + +.. + +.. date: 2025-08-16-18-11-41 +.. gh-issue: 90548 +.. nonce: q3aJUK +.. section: Library + +Fix ``musl`` detection for :func:`platform.libc_ver` on Alpine Linux if +compiled with --strip-all. + +.. + +.. date: 2025-08-16-16-04-15 +.. gh-issue: 137317 +.. nonce: Dl13B5 +.. section: Library + +:func:`inspect.signature` now correctly handles classes that use a +descriptor on a wrapped :meth:`!__init__` or :meth:`!__new__` method. +Contributed by Yongyu Yan. + +.. + +.. date: 2025-08-16-09-02-11 +.. gh-issue: 137754 +.. nonce: mCev1Y +.. section: Library + +Fix import of the :mod:`zoneinfo` module if the C implementation of the +:mod:`datetime` module is not available. + +.. + +.. date: 2025-08-14-10-27-07 +.. gh-issue: 125854 +.. nonce: vDzFcZ +.. section: Library + +Improve error messages for invalid category in :func:`warnings.warn`. + +.. + +.. date: 2025-08-14-00-00-12 +.. gh-issue: 137729 +.. nonce: i9NSKP +.. section: Library + +:func:`locale.setlocale` now supports language codes with ``@``-modifiers. +``@``-modifier are no longer silently removed in :func:`locale.getlocale`, +but included in the language code. + +.. + +.. date: 2025-08-13-10-50-22 +.. gh-issue: 73487 +.. nonce: DUHbBq +.. section: Library + +Speedup processing arguments (up to 1.5x) in the :mod:`decimal` module +methods, that now using :c:macro:`METH_FASTCALL` calling convention. Patch +by Sergey B Kirpichev. + +.. + +.. date: 2025-08-11-14-18-32 +.. gh-issue: 137634 +.. nonce: M7iBG6 +.. section: Library + +Calendar pages generated by the :class:`calendar.HTMLCalendar` class now +support dark mode and have been migrated to the HTML5 standard for improved +accessibility. + +.. + +.. date: 2025-08-11-05-05-08 +.. gh-issue: 137630 +.. nonce: 9lmqyc +.. section: Library + +The :mod:`!_interpreters` module now uses Argument Clinic to parse +arguments. Patch by Adam Turner. + +.. + +.. date: 2025-08-09-08-53-32 +.. gh-issue: 137583 +.. nonce: s6OZud +.. section: Library + +Fix a deadlock introduced in 3.13.6 when a call to :meth:`ssl.SSLSocket.recv +` was blocked in one thread, and then another method on +the object (such as :meth:`ssl.SSLSocket.send `) was +subsequently called in another thread. + +.. + +.. date: 2025-08-08-21-20-14 +.. gh-issue: 92936 +.. nonce: rOgG1S +.. section: Library + +Update regex used by ``http.cookies.SimpleCookie`` to handle values +containing double quotes. + +.. + +.. date: 2025-08-08-15-00-38 +.. gh-issue: 137426 +.. nonce: lW-Rk2 +.. section: Library + +Remove the code deprecation of ``importlib.abc.ResourceLoader``. It is +documented as deprecated, but left for backwards compatibility with other +classes in ``importlib.abc``. + +.. + +.. date: 2025-08-07-17-18-57 +.. gh-issue: 137490 +.. nonce: s89ieZ +.. section: Library + +Handle :data:`~errno.ECANCELED` in the same way as :data:`~errno.EINTR` in +:func:`signal.sigwaitinfo` on NetBSD. + +.. + +.. date: 2025-08-07-15-07-44 +.. gh-issue: 137512 +.. nonce: j2or5h +.. section: Library + +Add new constants in the :mod:`resource` module: +:data:`~resource.RLIMIT_NTHR`, :data:`~resource.RLIMIT_UMTXP`, +:data:`~resource.RLIMIT_PIPEBUF`, :data:`~resource.RLIMIT_THREADS`, +:data:`~resource.RLIM_SAVED_CUR`, and :data:`~resource.RLIM_SAVED_MAX`. + +.. + +.. date: 2025-08-07-12-32-23 +.. gh-issue: 137044 +.. nonce: abNoIy +.. section: Library + +:data:`resource.RLIM_INFINITY` is now always a positive integer. On all +supported platforms, it is larger than any limited resource value, which +simplifies comparison of the resource values. Previously, it could be +negative, such as -1 or -3, depending on platform. + +.. + +.. date: 2025-08-06-23-16-42 +.. gh-issue: 137477 +.. nonce: bk6BDV +.. section: Library + +Fix :func:`!inspect.getblock`, :func:`inspect.getsourcelines` and +:func:`inspect.getsource` for generator expressions. + +.. + +.. date: 2025-08-06-16-54-22 +.. gh-issue: 137481 +.. nonce: eSTkK0 +.. section: Library + +Calendar uses the lengths of the locale's weekdays to decide if the width +requires abbreviation. + +.. + +.. date: 2025-08-06-16-13-47 +.. gh-issue: 137466 +.. nonce: Whv0-A +.. section: Library + +Remove undocumented :func:`!glob.glob0` and :func:`!glob.glob1` functions, +which have been deprecated since Python 3.13. Use :func:`glob.glob` and pass +a directory to its *root_dir* argument instead. + +.. + +.. date: 2025-08-03-13-16-39 +.. gh-issue: 137044 +.. nonce: 0hPVL_ +.. section: Library + +Return large limit values as positive integers instead of negative integers +in :func:`resource.getrlimit`. Accept large values and reject negative +values (except :data:`~resource.RLIM_INFINITY`) for limits in +:func:`resource.setrlimit`. + +.. + +.. date: 2025-08-03-00-36-57 +.. gh-issue: 115766 +.. nonce: nJCFkW +.. section: Library + +Fix :attr:`!ipaddress.IPv4Interface.is_unspecified`. + +.. + +.. date: 2025-08-01-23-52-49 +.. gh-issue: 75989 +.. nonce: 5aYXNJ +.. section: Library + +:func:`tarfile.TarFile.extractall` and :func:`tarfile.TarFile.extract` now +overwrite symlinks when extracting hardlinks. (Contributed by Alexander +Enrique Urieles Nieto in :gh:`75989`.) + +.. + +.. date: 2025-08-01-23-11-25 +.. gh-issue: 137017 +.. nonce: 0yGcNc +.. section: Library + +Fix :obj:`threading.Thread.is_alive` to remain ``True`` until the underlying +OS thread is fully cleaned up. This avoids false negatives in edge cases +involving thread monitoring or premature :obj:`threading.Thread.is_alive` +calls. + +.. + +.. date: 2025-08-01-15-07-59 +.. gh-issue: 137273 +.. nonce: 4V8Xmv +.. section: Library + +Fix debug assertion failure in :func:`locale.setlocale` on Windows. + +.. + +.. date: 2025-07-31-16-43-16 +.. gh-issue: 137191 +.. nonce: FIogE8 +.. section: Library + +Fix how type parameters are collected, when :class:`typing.Protocol` are +specified with explicit parameters. Now, :class:`typing.Generic` and +:class:`typing.Protocol` always dictate the parameter number and parameter +ordering of types. Previous behavior was a bug. + +.. + +.. date: 2025-07-31-10-31-56 +.. gh-issue: 137282 +.. nonce: GOCwIC +.. section: Library + +Fix tab completion and :func:`dir` on :mod:`concurrent.futures`. + +.. + +.. date: 2025-07-30-18-07-33 +.. gh-issue: 137257 +.. nonce: XBtzf2 +.. section: Library + +Bump the version of pip bundled in ensurepip to version 25.2 + +.. + +.. date: 2025-07-30-17-42-36 +.. gh-issue: 137239 +.. nonce: qSpj32 +.. section: Library + +:mod:`heapq`: Update :data:`!heapq.__all__` with ``*_max`` functions. + +.. + +.. date: 2025-07-30-11-12-22 +.. gh-issue: 124503 +.. nonce: d4hc7b +.. section: Library + +:func:`ast.literal_eval` is 10-20% faster for small inputs. + +.. + +.. date: 2025-07-29-21-18-31 +.. gh-issue: 137226 +.. nonce: B_4lpu +.. section: Library + +Fix behavior of :meth:`annotationlib.ForwardRef.evaluate` when the +*type_params* parameter is passed and the name of a type param is also +present in an enclosing scope. + +.. + +.. date: 2025-07-29-05-12-50 +.. gh-issue: 137197 +.. nonce: bMK3sO +.. section: Library + +:class:`~ssl.SSLContext` objects can now set TLS 1.3 cipher suites via +:meth:`~ssl.SSLContext.set_ciphersuites`. + +.. + +.. date: 2025-07-28-23-11-29 +.. gh-issue: 81325 +.. nonce: jMJFBe +.. section: Library + +:class:`tarfile.TarFile` now accepts a :term:`path-like ` +when working on a tar archive. (Contributed by Alexander Enrique Urieles +Nieto in :gh:`81325`.) + +.. + +.. date: 2025-07-28-20-48-32 +.. gh-issue: 137185 +.. nonce: fgI7-B +.. section: Library + +Fix a potential async-signal-safety issue in :mod:`faulthandler` when +printing C stack traces. + +.. + +.. date: 2025-07-27-17-03-17 +.. gh-issue: 133951 +.. nonce: 7kwt78 +.. section: Library + +Remove lib64-lib symlink creation when creating new virtual environments in +:mod:`venv` module + +.. + +.. date: 2025-07-25-09-21-56 +.. gh-issue: 130522 +.. nonce: Crwq68 +.. section: Library + +Fix unraisable :exc:`TypeError` raised during :term:`interpreter shutdown` +in the :mod:`threading` module. + +.. + +.. date: 2025-07-24-00-38-07 +.. gh-issue: 137059 +.. nonce: fr64oW +.. section: Library + +Fix handling of file URLs with a Windows drive letter in the URL authority +by :func:`urllib.request.url2pathname`. This fixes a regression in earlier +pre-releases of Python 3.14. + +.. + +.. date: 2025-07-23-11-59-48 +.. gh-issue: 136980 +.. nonce: BIJzkB +.. section: Library + +Remove unused C tracing code in bdb for event type ``c_call``, ``c_return`` +and ``c_exception`` + +.. + +.. date: 2025-07-23-00-35-29 +.. gh-issue: 130577 +.. nonce: c7EITy +.. section: Library + +:mod:`tarfile` now validates archives to ensure member offsets are +non-negative. (Contributed by Alexander Enrique Urieles Nieto in +:gh:`130577`.) + +.. + +.. date: 2025-07-21-22-35-50 +.. gh-issue: 136170 +.. nonce: QUlc78 +.. section: Library + +Removed the unreleased ``zipfile.ZipFile.data_offset`` property added in +3.14.0a7 as it wasn't fully clear which behavior it should have in some +situations so the result was not always what a user might expect. + +.. + +.. date: 2025-07-21-20-00-42 +.. gh-issue: 121237 +.. nonce: DyxNqo +.. section: Library + +Support ``%:z`` directive for :meth:`datetime.datetime.strptime`, +:meth:`datetime.time.strptime` and :func:`time.strptime`. Patch by Lucas +Esposito and Semyon Moroz. + +.. + +.. date: 2025-07-21-16-13-20 +.. gh-issue: 136929 +.. nonce: obKZ2S +.. section: Library + +Ensure that hash functions guaranteed to be always *available* exist as +attributes of :mod:`hashlib` even if they will not work at runtime due to +missing backend implementations. For instance, ``hashlib.md5`` will no +longer raise :exc:`AttributeError` if OpenSSL is not available and Python +has been built without MD5 support. Patch by Bénédikt Tran. + +.. + +.. date: 2025-07-21-16-10-24 +.. gh-issue: 124621 +.. nonce: wyoWc1 +.. section: Library + +pyrepl now works in Emscripten. + +.. + +.. date: 2025-07-21-15-40-00 +.. gh-issue: 136914 +.. nonce: -GNG-d +.. section: Library + +Fix retrieval of :attr:`doctest.DocTest.lineno` for objects decorated with +:func:`functools.cache` or :class:`functools.cached_property`. + +.. + +.. date: 2025-07-21-11-56-47 +.. gh-issue: 136912 +.. nonce: zWosAL +.. section: Library + +:func:`hmac.digest` now properly handles large keys and messages by falling +back to the pure Python implementation when necessary. Patch by Bénédikt +Tran. + +.. + +.. date: 2025-07-21-01-16-32 +.. gh-issue: 83424 +.. nonce: Y3tEV4 +.. section: Library + +Allows creating a :class:`ctypes.CDLL` without name when passing a handle as +an argument. + +.. + +.. date: 2025-07-20-16-56-55 +.. gh-issue: 135228 +.. nonce: n_XIao +.. section: Library + +When :mod:`dataclasses` replaces a class with a slotted dataclass, the +original class can now be garbage collected again. Earlier changes in Python +3.14 caused this class to always remain in existence together with the +replacement class synthesized by :mod:`dataclasses`. + +.. + +.. date: 2025-07-20-16-02-00 +.. gh-issue: 136874 +.. nonce: cLC3o1 +.. section: Library + +Discard URL query and fragment in :func:`urllib.request.url2pathname`. + +.. + +.. date: 2025-07-20-10-21-49 +.. gh-issue: 136787 +.. nonce: _0Rbp_ +.. section: Library + +:mod:`hashlib`: improve exception messages when a hash algorithm is not +recognized, blocked by the current security policy or incompatible with the +desired operation (for instance, using HMAC with SHAKE). Patch by Bénédikt +Tran. + +.. + +.. date: 2025-07-19-16-20-54 +.. gh-issue: 130645 +.. nonce: O-dYcN +.. section: Library + +Enable color help by default in :mod:`argparse`. + +.. + +.. date: 2025-07-19-15-40-47 +.. gh-issue: 131724 +.. nonce: LS59nA +.. section: Library + +In :mod:`http.client`, a new *max_response_headers* keyword-only parameter +has been added to :class:`~http.client.HTTPConnection` and +:class:`~http.client.HTTPSConnection` constructors. This parameter sets the +maximum number of allowed response headers, helping to prevent +denial-of-service attacks. + +.. + +.. date: 2025-07-19-11-53-19 +.. gh-issue: 135427 +.. nonce: iJM_X2 +.. section: Library + +With :option:`-Werror <-W>`, the DeprecationWarning emitted by +:py:func:`os.fork` and :py:func:`os.forkpty` in mutli-threaded processes is +now raised as an exception. Previously it was silently ignored. Patch by +Rani Pinchuk. + +.. + +.. date: 2025-07-17-16-12-23 +.. gh-issue: 136234 +.. nonce: VmTxtj +.. section: Library + +Fix :meth:`asyncio.WriteTransport.writelines` to be robust to connection +failure, by using the same behavior as +:meth:`~asyncio.WriteTransport.write`. + +.. + +.. date: 2025-07-16-09-45-58 +.. gh-issue: 53144 +.. nonce: mrKwMW +.. section: Library + +:mod:`!encodings.aliases`: Add ``latin_N`` aliases + +.. + +.. date: 2025-07-15-16-37-34 +.. gh-issue: 136669 +.. nonce: Yexwah +.. section: Library + +:mod:`!_asyncio` is now statically linked for improved performance. + +.. + +.. date: 2025-07-13-13-31-22 +.. gh-issue: 136134 +.. nonce: mh6VjS +.. section: Library + +:meth:`!SMTP.auth_cram_md5` now raises an :exc:`~smtplib.SMTPException` +instead of a :exc:`ValueError` if Python has been built without MD5 support. +In particular, :class:`~smtplib.SMTP` clients will not attempt to use this +method even if the remote server is assumed to support it. Patch by Bénédikt +Tran. + +.. + +.. date: 2025-07-13-11-20-05 +.. gh-issue: 136134 +.. nonce: xhh0Kq +.. section: Library + +:meth:`IMAP4.login_cram_md5 ` now raises an +:exc:`IMAP4.error ` if CRAM-MD5 authentication is not +supported. Patch by Bénédikt Tran. + +.. + +.. date: 2025-07-12-18-05-37 +.. gh-issue: 136591 +.. nonce: ujXmSN +.. section: Library + +:mod:`!_hashlib`: avoid using deprecated functions +:manpage:`ERR_func_error_string` and :manpage:`EVP_MD_CTX_md` when using +OpenSSL 3.0 and later. Patch by Bénédikt Tran. + +.. + +.. date: 2025-07-12-14-15-47 +.. gh-issue: 136571 +.. nonce: muHmBv +.. section: Library + +:meth:`datetime.date.fromisocalendar` can now raise OverflowError for out of +range arguments. + +.. + +.. date: 2025-07-11-23-04-39 +.. gh-issue: 136549 +.. nonce: oAi8u4 +.. section: Library + +Fix signature of :func:`threading.excepthook`. + +.. + +.. date: 2025-07-11-10-23-44 +.. gh-issue: 136492 +.. nonce: BVi5h0 +.. section: Library + +Expose :pep:`667`'s :data:`~types.FrameLocalsProxyType` in the :mod:`types` +module. + +.. + +.. date: 2025-07-11-08-15-17 +.. gh-issue: 83336 +.. nonce: ptpmq7 +.. section: Library + +``utf8_sig`` is now aliased to :mod:`encodings.utf_8_sig` + +.. + +.. date: 2025-07-11-03-39-15 +.. gh-issue: 136523 +.. nonce: s7caKL +.. section: Library + +Fix :class:`wave.Wave_write` emitting an unraisable when open raises. + +.. + +.. date: 2025-07-10-21-02-43 +.. gh-issue: 136507 +.. nonce: pnEuGS +.. section: Library + +Fix mimetypes CLI to handle multiple file parameters. + +.. + +.. date: 2025-07-10-10-18-19 +.. gh-issue: 52876 +.. nonce: 9Vjrd8 +.. section: Library + +Add missing ``keepends`` (default ``True``) parameter to +:meth:`!codecs.StreamReaderWriter.readline` and +:meth:`!codecs.StreamReaderWriter.readlines`. + +.. + +.. date: 2025-07-10-00-47-37 +.. gh-issue: 136470 +.. nonce: KlUEUG +.. section: Library + +Correct :class:`concurrent.futures.InterpreterPoolExecutor`'s default thread +name. + +.. + +.. date: 2025-07-09-20-29-30 +.. gh-issue: 136476 +.. nonce: HyLLzh +.. section: Library + +Fix a bug that was causing the ``get_async_stack_trace`` function to miss +some frames in the stack trace. + +.. + +.. date: 2025-07-08-20-58-01 +.. gh-issue: 136434 +.. nonce: uuJsjS +.. section: Library + +Fix docs generation of ``UnboundItem`` in :mod:`concurrent.interpreters` +when running with :option:`-OO`. + +.. + +.. date: 2025-07-07-22-12-32 +.. gh-issue: 136380 +.. nonce: 1b_nXl +.. section: Library + +Raises :exc:`AttributeError` when accessing +:class:`concurrent.futures.InterpreterPoolExecutor` and subinterpreters are +not available. + +.. + +.. date: 2025-07-07-16-46-55 +.. gh-issue: 72327 +.. nonce: wLvRuj +.. section: Library + +Suggest using the system command prompt when ``pip install`` is typed into +the REPL. Patch by Tom Viner, Richard Si, and Brian Schubert. + +.. + +.. date: 2025-07-06-18-38-10 +.. gh-issue: 135953 +.. nonce: Z29DCz +.. section: Library + +Implement a new high-frequency runtime profiler that leverages the existing +remote debugging functionality to collect detailed execution statistics from +running Python processes. This tool is exposed in the ``profile.sample`` +module and enables non-intrusive observation of production applications by +attaching to already-running processes without requiring any code +modifications, restarts, or special startup flags. The observer can perform +extremely high-frequency sampling of stack traces and interpreter state, +providing detailed runtime execution analysis of live applications. + +.. + +.. date: 2025-07-06-10-18-48 +.. gh-issue: 136021 +.. nonce: f-FJYT +.. section: Library + +Make ``type_params`` parameter required in :func:`!typing._eval_type` after +a deprecation period for not providing this parameter. Also remove the +:exc:`DeprecationWarning` for the old behavior. + +.. + +.. date: 2025-07-05-09-45-04 +.. gh-issue: 136286 +.. nonce: N67Amr +.. section: Library + +Fix pickling failures for protocols 0 and 1 for many objects related to +subinterpreters. + +.. + +.. date: 2025-07-05-06-59-46 +.. gh-issue: 136047 +.. nonce: qWvycf +.. section: Library + +Fix issues with :mod:`typing` when the C implementation of :mod:`abc` is not +available. + +.. + +.. date: 2025-07-05-06-56-16 +.. gh-issue: 136316 +.. nonce: 3zj_Do +.. section: Library + +Improve support for evaluating nested forward references in +:func:`typing.evaluate_forward_ref`. + +.. + +.. date: 2025-07-04-23-45-00 +.. gh-issue: 136306 +.. nonce: O1YLIU +.. section: Library + +:mod:`ssl` can now get and set groups used for key agreement. + +.. + +.. date: 2025-07-04-12-53-02 +.. gh-issue: 136156 +.. nonce: OYlXoz +.. section: Library + +:func:`tempfile.TemporaryFile` no longer uses :data:`os.O_EXCL` with +:data:`os.O_TMPFILE`, so it's possible to use ``linkat()`` on the file +descriptor. Patch by Victor Stinner. + +.. + +.. date: 2025-07-02-18-41-45 +.. gh-issue: 133982 +.. nonce: 7qqAn6 +.. section: Library + +Update Python implementation of :class:`io.BytesIO` to be thread safe. + +.. + +.. date: 2025-07-02-10-48-21 +.. gh-issue: 136193 +.. nonce: xfvras +.. section: Library + +Improve :exc:`TypeError` error message, when richcomparing two +:class:`types.SimpleNamespace` objects. + +.. + +.. date: 2025-07-01-14-44-03 +.. gh-issue: 136097 +.. nonce: bI1n14 +.. section: Library + +Fix potential infinite recursion and KeyError in ``sysconfig +--generate-posix-vars``. + +.. + +.. date: 2025-06-30-11-12-24 +.. gh-issue: 85702 +.. nonce: 0Lrbwu +.. section: Library + +If ``zoneinfo._common.load_tzdata`` is given a package without a resource a +:exc:`zoneinfo.ZoneInfoNotFoundError` is raised rather than a +:exc:`PermissionError`. Patch by Victor Stinner. + +.. + +.. date: 2025-06-29-15-22-13 +.. gh-issue: 90733 +.. nonce: NiquaA +.. section: Library + +Improve error messages when reporting invalid parameters in +:func:`hashlib.scrypt`. Patch by Bénédikt Tran. + +.. + +.. date: 2025-06-28-11-32-57 +.. gh-issue: 134759 +.. nonce: AjjKcG +.. section: Library + +Fix :exc:`UnboundLocalError` in :func:`email.message.Message.get_payload` +when the payload to decode is a :class:`bytes` object. Patch by Kliment +Lamonov. + +.. + +.. date: 2025-06-27-13-34-28 +.. gh-issue: 136028 +.. nonce: RY727g +.. section: Library + +Fix parsing month names containing "İ" (U+0130, LATIN CAPITAL LETTER I WITH +DOT ABOVE) in :func:`time.strptime`. This affects locales az_AZ, ber_DZ, +ber_MA and crh_UA. + +.. + +.. date: 2025-06-27-09-26-04 +.. gh-issue: 87135 +.. nonce: 33z0UW +.. section: Library + +Acquiring a :class:`threading.Lock` or :class:`threading.RLock` at +interpreter shutdown will raise :exc:`PythonFinalizationError` if Python can +determine that it would otherwise deadlock. + +.. + +.. date: 2025-06-26-17-28-49 +.. gh-issue: 135995 +.. nonce: pPrDCt +.. section: Library + +In the palmos encoding, make byte ``0x9b`` decode to ``›`` (U+203A - SINGLE +RIGHT-POINTING ANGLE QUOTATION MARK). + +.. + +.. date: 2025-06-26-17-19-36 +.. gh-issue: 105456 +.. nonce: eR9oHB +.. section: Library + +Removed :mod:`!sre_compile`, :mod:`!sre_constants` and :mod:`!sre_parse` +modules. + +.. + +.. date: 2025-06-26-11-52-40 +.. gh-issue: 53203 +.. nonce: TMigBr +.. section: Library + +Fix :func:`time.strptime` for ``%c`` and ``%x`` formats on locales byn_ER, +wal_ET and lzh_TW, and for ``%X`` format on locales ar_SA, bg_BG and lzh_TW. + +.. + +.. date: 2025-06-24-14-43-24 +.. gh-issue: 135878 +.. nonce: Db4roX +.. section: Library + +Fixes a crash of :class:`types.SimpleNamespace` on :term:`free threading` +builds, when several threads were calling its :meth:`~object.__repr__` +method at the same time. + +.. + +.. date: 2025-06-24-13-30-47 +.. gh-issue: 135853 +.. nonce: 7ejTvK +.. section: Library + +Add :func:`math.fmax` and :func:`math.fmin` to get the larger and smaller of +two floating-point values. Patch by Bénédikt Tran. + +.. + +.. date: 2025-06-24-10-52-35 +.. gh-issue: 135836 +.. nonce: s37351 +.. section: Library + +Fix :exc:`IndexError` in :meth:`asyncio.loop.create_connection` that could +occur when non-\ :exc:`OSError` exception is raised during connection and +socket's ``close()`` raises :exc:`!OSError`. + +.. + +.. date: 2025-06-24-10-23-37 +.. gh-issue: 135853 +.. nonce: 6xDNOG +.. section: Library + +:mod:`math`: expose C99 :func:`~math.signbit` function to determine whether +the sign bit of a floating-point value is set. Patch by Bénédikt Tran. + +.. + +.. date: 2025-06-23-13-02-08 +.. gh-issue: 134531 +.. nonce: yUmj07 +.. section: Library + +:mod:`hmac`: use the :manpage:`EVP_MAC(3ssl)` interface for HMAC when Python +is built with OpenSSL 3.0 and later instead of the deprecated +:manpage:`HMAC_CTX(3ssl) ` interface. Patch by Bénédikt Tran. + +.. + +.. date: 2025-06-23-11-04-25 +.. gh-issue: 135836 +.. nonce: -C-c4v +.. section: Library + +Fix :exc:`IndexError` in :meth:`asyncio.loop.create_connection` that could +occur when the Happy Eyeballs algorithm resulted in an empty exceptions list +during connection attempts. + +.. + +.. date: 2025-06-23-10-19-11 +.. gh-issue: 135855 +.. nonce: -J0AGF +.. section: Library + +Raise :exc:`TypeError` instead of :exc:`SystemError` when +:func:`!_interpreters.set___main___attrs` is passed a non-dict object. Patch +by Brian Schubert. + +.. + +.. date: 2025-06-22-22-03-06 +.. gh-issue: 135823 +.. nonce: iDBg97 +.. section: Library + +:mod:`netrc`: improve the error message when the security check for the +ownership of the default configuration file ``~/.netrc`` fails. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-06-22-16-23-44 +.. gh-issue: 135815 +.. nonce: 0DandH +.. section: Library + +:mod:`netrc`: skip security checks if :func:`os.getuid` is missing. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-06-22-02-16-17 +.. gh-issue: 135640 +.. nonce: FXyFL6 +.. section: Library + +Address bug where it was possible to call +:func:`xml.etree.ElementTree.ElementTree.write` on an ElementTree object +with an invalid root element. This behavior blanked the file passed to +``write`` if it already existed. + +.. + +.. date: 2025-06-20-17-06-59 +.. gh-issue: 90117 +.. nonce: GYWVrn +.. section: Library + +Speed up :mod:`pprint` for :class:`list` and :class:`tuple`. + +.. + +.. date: 2025-06-20-16-28-47 +.. gh-issue: 135759 +.. nonce: jne0Zi +.. section: Library + +:mod:`hashlib`: reject negative digest lengths in OpenSSL-based SHAKE +objects by raising a :exc:`ValueError`. Previously, negative lengths were +implicitly rejected by raising a :exc:`MemoryError` or a :exc:`SystemError`. +Patch by Bénédikt Tran. + +.. + +.. date: 2025-06-18-19-25-32 +.. gh-issue: 123471 +.. nonce: lx1Xbt +.. section: Library + +Make concurrent iterations over :class:`itertools.chain` safe under +:term:`free threading`. + +.. + +.. date: 2025-06-18-13-58-13 +.. gh-issue: 135645 +.. nonce: 109nff +.. section: Library + +Added ``supports_isolated_interpreters`` field to +:data:`sys.implementation`. + +.. + +.. date: 2025-06-18-11-43-17 +.. gh-issue: 135646 +.. nonce: r7ekEn +.. section: Library + +Raise consistent :exc:`NameError` exceptions in +:func:`annotationlib.ForwardRef.evaluate` + +.. + +.. date: 2025-06-17-23-13-56 +.. gh-issue: 135557 +.. nonce: Bfcy4v +.. section: Library + +Fix races on :mod:`heapq` updates and :class:`list` reads on the :term:`free +threaded ` build. + +.. + +.. date: 2025-06-17-22-44-19 +.. gh-issue: 119180 +.. nonce: Ogv8Nj +.. section: Library + +Only fetch globals and locals if necessary in +:func:`annotationlib.get_annotations` + +.. + +.. date: 2025-06-16-15-03-03 +.. gh-issue: 135561 +.. nonce: mJCN8D +.. section: Library + +Fix a crash on DEBUG builds when an HACL* HMAC routine fails. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-06-16-15-00-13 +.. gh-issue: 135386 +.. nonce: lNrxLc +.. section: Library + +Fix opening a :mod:`dbm.sqlite3` database for reading from read-only file or +directory. + +.. + +.. date: 2025-06-16-12-37-02 +.. gh-issue: 135444 +.. nonce: An2eeA +.. section: Library + +Fix :meth:`asyncio.DatagramTransport.sendto` to account for datagram header +size when data cannot be sent. + +.. + +.. date: 2025-06-15-03-03-22 +.. gh-issue: 65697 +.. nonce: COdwZd +.. section: Library + +:class:`configparser`'s error message when attempting to write an invalid +key is now more helpful. + +.. + +.. date: 2025-06-14-14-19-13 +.. gh-issue: 135497 +.. nonce: 1pzwdA +.. section: Library + +Fix :func:`os.getlogin` failing for longer usernames on BSD-based platforms. + +.. + +.. date: 2025-06-14-12-06-55 +.. gh-issue: 135487 +.. nonce: KdVFff +.. section: Library + +Fix :meth:`!reprlib.Repr.repr_int` when given integers with more than +:func:`sys.get_int_max_str_digits` digits. Patch by Bénédikt Tran. + +.. + +.. date: 2025-06-12-18-15-31 +.. gh-issue: 135429 +.. nonce: mch75_ +.. section: Library + +Fix the argument mismatch in ``_lsprof`` for ``PY_THROW`` event. + +.. + +.. date: 2025-06-12-10-45-02 +.. gh-issue: 135368 +.. nonce: OjWVHL +.. section: Library + +Fix :class:`unittest.mock.Mock` generation on :func:`dataclasses.dataclass` +objects. Now all special attributes are set as it was before :gh:`124429`. + +.. + +.. date: 2025-06-11-15-08-02 +.. gh-issue: 135336 +.. nonce: 6Gq6MI +.. section: Library + +:mod:`json` now encodes strings up to 2.2x faster if they consist solely of +characters that don’t require escaping. + +.. + +.. date: 2025-06-10-21-42-04 +.. gh-issue: 135335 +.. nonce: WnUqb_ +.. section: Library + +:mod:`multiprocessing`: Flush ``stdout`` and ``stderr`` after preloading +modules in the ``forkserver``. + +.. + +.. date: 2025-06-10-21-00-48 +.. gh-issue: 126631 +.. nonce: eITVJd +.. section: Library + +Fix :mod:`multiprocessing` ``forkserver`` bug which prevented ``__main__`` +from being preloaded. + +.. + +.. date: 2025-06-10-16-11-00 +.. gh-issue: 133967 +.. nonce: P0c24q +.. section: Library + +Do not normalize :mod:`locale` name 'C.UTF-8' to 'en_US.UTF-8'. + +.. + +.. date: 2025-06-10-10-22-18 +.. gh-issue: 130870 +.. nonce: JipqbO +.. section: Library + +Preserve :class:`types.GenericAlias` subclasses in +:func:`typing.get_type_hints` + +.. + +.. date: 2025-06-10-00-42-30 +.. gh-issue: 135321 +.. nonce: UHh9jT +.. section: Library + +Raise a correct exception for values greater than 0x7fffffff for the +``BINSTRING`` opcode in the C implementation of :mod:`pickle`. + +.. + +.. date: 2025-06-09-10-16-55 +.. gh-issue: 121914 +.. nonce: G6Avkq +.. section: Library + +Changed the names of the symbol tables for lambda expressions and generator +expressions to "" and "" respectively to avoid conflicts +with user-defined names. + +.. + +.. date: 2025-06-08-14-50-34 +.. gh-issue: 135276 +.. nonce: ZLUhV1 +.. section: Library + +Synchronized zipfile.Path with zipp 3.23, including improved performance of +:meth:`zipfile.Path.open` for non-reading modes, rely on +:func:`functools.cached_property` to cache values on the instance. Rely on +``save_method_args`` to save the initialization method arguments. Fixed +``.name``, ``.stem`` and other basename-based properties on Windows when +working with a zipfile on disk. + +.. + +.. date: 2025-06-08-11-11-07 +.. gh-issue: 135234 +.. nonce: wJCdh0 +.. section: Library + +:mod:`hashlib`: improve exception messages when an OpenSSL function failed. +When memory allocation fails on OpenSSL's side, a :exc:`MemoryError` is +raised instead of a :exc:`ValueError`. Patch by Bénédikt Tran. + +.. + +.. date: 2025-06-08-10-22-22 +.. gh-issue: 135244 +.. nonce: Y2SOTJ +.. section: Library + +:mod:`uuid`: when the MAC address cannot be determined, the 48-bit node ID +is now generated with a cryptographically-secure pseudo-random number +generator (CSPRNG) as per :rfc:`RFC 9562, §6.10.3 <9562#section-6.10-3>`. +This affects :func:`~uuid.uuid1` and :func:`~uuid.uuid6`. + +.. + +.. date: 2025-06-08-01-10-34 +.. gh-issue: 135241 +.. nonce: 5j18IW +.. section: Library + +The :code:`INT` opcode of the C accelerator :mod:`!_pickle` module was +updated to look only for "00" and "01" to push booleans onto the stack, +aligning with the Python :mod:`pickle` module. + +.. + +.. date: 2025-06-06-17-34-18 +.. gh-issue: 133934 +.. nonce: yT1r68 +.. section: Library + +Improve :mod:`sqlite3` CLI's ``.help`` message. + +.. + +.. date: 2025-06-03-12-59-17 +.. gh-issue: 135069 +.. nonce: xop30V +.. section: Library + +Fix the "Invalid error handling" exception in +:class:`!encodings.idna.IncrementalDecoder` to correctly replace the +'errors' parameter. + +.. + +.. date: 2025-06-02-14-36-28 +.. gh-issue: 130662 +.. nonce: Gpr2GB +.. section: Library + ++Accept leading zeros in precision and width fields for ++:class:`~decimal.Decimal` formatting, for example ``format(Decimal(1.25), +'.016f')``. + +.. + +.. date: 2025-06-02-14-28-30 +.. gh-issue: 130662 +.. nonce: EIgIR8 +.. section: Library + +Accept leading zeros in precision and width fields for +:class:`~fractions.Fraction` formatting, for example ``format(Fraction(1, +3), '.016f')``. + +.. + +.. date: 2025-06-01-14-18-48 +.. gh-issue: 135004 +.. nonce: cq3-fp +.. section: Library + +Rewrite and cleanup the internal :mod:`!_blake2` module. Some exception +messages were changed but their types were left untouched. Patch by Bénédikt +Tran. + +.. + +.. date: 2025-06-01-11-14-00 +.. gh-issue: 134953 +.. nonce: ashdfs +.. section: Library + +Expand ``_colorize`` theme with ``keyword_constant`` and implement in +:term:`repl`. + +.. + +.. date: 2025-05-31-15-49-46 +.. gh-issue: 134978 +.. nonce: mXXuvW +.. section: Library + +:mod:`hashlib`: Supporting the ``string`` keyword parameter in hash function +constructors such as :func:`~hashlib.new` or the direct hash-named +constructors such as :func:`~hashlib.md5` and :func:`~hashlib.sha256` is now +deprecated and slated for removal in Python 3.19. Prefer passing the initial +data as a positional argument for maximum backwards compatibility. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-05-31-12-08-12 +.. gh-issue: 134970 +.. nonce: lgSaxq +.. section: Library + +Fix the "unknown action" exception in +:meth:`argparse.ArgumentParser.add_argument_group` to correctly replace the +action class. + +.. + +.. date: 2025-05-30-18-13-48 +.. gh-issue: 134718 +.. nonce: 5FEspx +.. section: Library + +By default, omit optional ``Load()`` values in :func:`ast.dump`. + +.. + +.. date: 2025-05-30-13-07-29 +.. gh-issue: 134718 +.. nonce: 9Qvhxn +.. section: Library + +:func:`ast.dump` now only omits ``None`` and ``[]`` values if they are +default values. + +.. + +.. date: 2025-05-30-09-46-21 +.. gh-issue: 134939 +.. nonce: Pu3nnm +.. section: Library + +Add the :mod:`concurrent.interpreters` module. See :pep:`734`. + +.. + +.. date: 2025-05-29-17-39-13 +.. gh-issue: 108885 +.. nonce: MegCRA +.. section: Library + +Run each example as a subtest in unit tests synthesized by +:func:`doctest.DocFileSuite` and :func:`doctest.DocTestSuite`. Add the +:meth:`doctest.DocTestRunner.report_skip` method. + +.. + +.. date: 2025-05-29-06-53-40 +.. gh-issue: 134885 +.. nonce: -_L22o +.. section: Library + +Fix possible crash in the :mod:`compression.zstd` module related to setting +parameter types. Patch by Jelle Zijlstra. + +.. + +.. date: 2025-05-28-20-49-29 +.. gh-issue: 134857 +.. nonce: dVYXVO +.. section: Library + +Improve error report for :mod:`doctest`\ s run with :mod:`unittest`. Remove +:mod:`!doctest` module frames from tracebacks and redundant newline +character from a failure message. + +.. + +.. date: 2025-05-28-15-53-27 +.. gh-issue: 128840 +.. nonce: Nur2pB +.. section: Library + +Fix parsing long IPv6 addresses with embedded IPv4 address. + +.. + +.. date: 2025-05-27-11-24-38 +.. gh-issue: 133579 +.. nonce: WGPUC1 +.. section: Library + +:mod:`curses`: Consistently report failures of curses C API calls in +module-level methods by raising a :exc:`curses.error`. This affects +:func:`~curses.assume_default_colors`, :func:`~curses.baudrate`, +:func:`~curses.cbreak`, :func:`~curses.echo`, :func:`~curses.longname`, +:func:`~curses.initscr`, :func:`~curses.nl`, :func:`~curses.raw`, +:func:`~curses.termattrs`, :func:`~curses.termname` and +:func:`~curses.unctrl`. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-27-11-18-13 +.. gh-issue: 133579 +.. nonce: ohtgdC +.. section: Library + +:meth:`curses.window.refresh` and :meth:`curses.window.noutrefresh` now +raise a :exc:`TypeError` instead of :exc:`curses.error` when called with an +incorrect number of arguments for :ref:`pads `. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-05-27-11-13-51 +.. gh-issue: 133579 +.. nonce: KY9M6S +.. section: Library + +:ref:`curses.window `: Consistently report failures +of curses C API calls in Window methods by raising a :exc:`curses.error`. +This affects :meth:`~curses.window.addch`, :meth:`~curses.window.addnstr`, +:meth:`~curses.window.addstr`, :meth:`~curses.window.border`, +:meth:`~curses.window.box`, :meth:`~curses.window.chgat`, +:meth:`~curses.window.getbkgd`, :meth:`~curses.window.inch`, +:meth:`~curses.window.insstr` and :meth:`~curses.window.insnstr`. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-05-26-22-18-32 +.. gh-issue: 134771 +.. nonce: RKXpLT +.. section: Library + +The ``time_clockid_converter()`` function now selects correct type for +``clockid_t`` on Cygwin which fixes a build error. + +.. + +.. date: 2025-05-26-17-06-40 +.. gh-issue: 134637 +.. nonce: 9-3zRL +.. section: Library + +Fix performance regression in calling a :mod:`ctypes` function pointer in +:term:`free threading`. + +.. + +.. date: 2025-05-26-14-04-39 +.. gh-issue: 134696 +.. nonce: P04xUa +.. section: Library + +Built-in HACL* and OpenSSL implementations of hash function constructors now +correctly accept the same *documented* named arguments. For instance, +:func:`~hashlib.md5` could be previously invoked as ``md5(data=data)`` or +``md5(string=string)`` depending on the underlying implementation but these +calls were not compatible. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-26-12-31-08 +.. gh-issue: 132710 +.. nonce: ApU3TZ +.. section: Library + +If possible, ensure that :func:`uuid.getnode` returns the same result even +across different processes. Previously, the result was constant only within +the same process. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-26-11-01-54 +.. gh-issue: 134531 +.. nonce: my1Fzt +.. section: Library + +:mod:`!_hashlib`: Rename internal C functions for :class:`!_hashlib.HASH` +and :class:`!_hashlib.HASHXOF` objects. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-26-10-52-27 +.. gh-issue: 134698 +.. nonce: aJ1mZ1 +.. section: Library + +Fix a crash when calling methods of :class:`ssl.SSLContext` or +:class:`ssl.SSLSocket` across multiple threads. + +.. + +.. date: 2025-05-25-23-23-05 +.. gh-issue: 134151 +.. nonce: 13Wwsb +.. section: Library + +:mod:`email`: Fix :exc:`TypeError` in :func:`email.utils.decode_params` when +sorting :rfc:`2231` continuations that contain an unnumbered section. + +.. + +.. date: 2025-05-25-13-46-37 +.. gh-issue: 134635 +.. nonce: ZlPrlX +.. section: Library + +:mod:`zlib`: Allow to combine Adler-32 and CRC-32 checksums via +:func:`~zlib.adler32_combine` and :func:`~zlib.crc32_combine`. Patch by +Callum Attryde and Bénédikt Tran. + +.. + +.. date: 2025-05-25-11-02-05 +.. gh-issue: 134657 +.. nonce: 3YFhR9 +.. section: Library + +:mod:`asyncio`: Remove some private names from ``asyncio.__all__``. + +.. + +.. date: 2025-05-24-13-10-35 +.. gh-issue: 134210 +.. nonce: 0IuMY2 +.. section: Library + +:func:`curses.window.getch` now correctly handles signals. Patch by Bénédikt +Tran. + +.. + +.. date: 2025-05-24-03-10-36 +.. gh-issue: 80334 +.. nonce: z21cMa +.. section: Library + +:func:`multiprocessing.freeze_support` now checks for work on any "spawn" +start method platform rather than only on Windows. + +.. + +.. date: 2025-05-23-23-43-39 +.. gh-issue: 134582 +.. nonce: 9POq3l +.. section: Library + +Fix tokenize.untokenize() round-trip errors related to t-strings braces +escaping + +.. + +.. date: 2025-05-23-20-01-52 +.. gh-issue: 134580 +.. nonce: xnaJ70 +.. section: Library + +Improved the styling of HTML diff pages generated by the +:class:`difflib.HtmlDiff` class, and migrated the output to the HTML5 +standard. + +.. + +.. date: 2025-05-23-10-15-36 +.. gh-issue: 134565 +.. nonce: zmb66C +.. section: Library + +:func:`unittest.doModuleCleanups` no longer swallows all but first exception +raised in the cleanup code, but raises a :exc:`ExceptionGroup` if multiple +errors occurred. + +.. + +.. date: 2025-05-22-18-14-13 +.. gh-issue: 134546 +.. nonce: fjLVzK +.. section: Library + +Ensure :mod:`pdb` remote debugging script is readable by remote Python +process. + +.. + +.. date: 2025-05-22-14-12-53 +.. gh-issue: 134451 +.. nonce: M1rD-j +.. section: Library + +Converted ``asyncio.tools.CycleFoundException`` from dataclass to a regular +exception type. + +.. + +.. date: 2025-05-22-13-10-32 +.. gh-issue: 114177 +.. nonce: 3TYUJ3 +.. section: Library + +Fix :mod:`asyncio` to not close subprocess pipes which would otherwise error +out when the event loop is already closed. + +.. + +.. date: 2025-05-20-21-45-58 +.. gh-issue: 90871 +.. nonce: Gkvtp6 +.. section: Library + +Fixed an off by one error concerning the backlog parameter in +:meth:`~asyncio.loop.create_unix_server`. Contributed by Christian Harries. + +.. + +.. date: 2025-05-20-19-16-30 +.. gh-issue: 134323 +.. nonce: ZQZGvw +.. section: Library + +Fix the :meth:`threading.RLock.locked` method. + +.. + +.. date: 2025-05-20-15-13-43 +.. gh-issue: 86802 +.. nonce: trF7TM +.. section: Library + +Fixed asyncio memory leak in cancelled shield tasks. For shielded tasks +where the shield was cancelled, log potential exceptions through the +exception handler. Contributed by Christian Harries. + +.. + +.. date: 2025-05-20-11-51-17 +.. gh-issue: 71189 +.. nonce: 0LpTB1 +.. section: Library + +Add support of the all-but-last mode in :func:`os.path.realpath`. + +.. + +.. date: 2025-05-20-11-35-08 +.. gh-issue: 72902 +.. nonce: jzEI-E +.. section: Library + +Improve speed (x1.1-1.8) of the :class:`~fractions.Fraction` constructor for +typical inputs (:class:`float`'s, :class:`~decimal.Decimal`'s or strings). + +.. + +.. date: 2025-05-19-20-59-06 +.. gh-issue: 134209 +.. nonce: anhTcF +.. section: Library + +:mod:`curses`: The :meth:`curses.window.instr` and +:meth:`curses.window.getstr` methods now allocate their internal buffer on +the heap instead of the stack; in addition, the max buffer size is increased +from 1023 to 2047. + +.. + +.. date: 2025-05-19-18-12-42 +.. gh-issue: 88994 +.. nonce: 7avvVu +.. section: Library + +Change :func:`datetime.datetime.now` to half-even rounding for consistency +with :func:`datetime.datetime.fromtimestamp`. Patch by John Keith Hohm. + +.. + +.. date: 2025-05-19-17-27-21 +.. gh-issue: 80184 +.. nonce: LOkbaw +.. section: Library + +The default queue size is now ``socket.SOMAXCONN`` for +:class:`socketserver.TCPServer`. + +.. + +.. date: 2025-05-19-15-30-00 +.. gh-issue: 132983 +.. nonce: asdsfs +.. section: Library + +Add :mod:`!compression.zstd` version information to ``test.pythoninfo``. + +.. + +.. date: 2025-05-19-15-05-24 +.. gh-issue: 134235 +.. nonce: pz9PwV +.. section: Library + +Updated tab completion on REPL to include builtin modules. Contributed by +Tom Wang, Hunter Young + +.. + +.. date: 2025-05-19-10-32-11 +.. gh-issue: 134152 +.. nonce: INJC2j +.. section: Library + +Fixed :exc:`UnboundLocalError` that could occur during :mod:`email` header +parsing if an expected trailing delimiter is missing in some contexts. + +.. + +.. date: 2025-05-18-23-46-21 +.. gh-issue: 134152 +.. nonce: 30HwbX +.. section: Library + +:mod:`email`: Fix parsing of email message ID with invalid domain. + +.. + +.. date: 2025-05-18-13-23-29 +.. gh-issue: 134168 +.. nonce: hgx3Xg +.. section: Library + +:mod:`http.server`: Fix IPv6 address binding and :option:`--directory +` handling when using HTTPS. + +.. + +.. date: 2025-05-18-12-48-39 +.. gh-issue: 62184 +.. nonce: y11l10 +.. section: Library + +Remove import of C implementation of :class:`io.FileIO` from Python +implementation which has its own implementation + +.. + +.. date: 2025-05-18-12-23-07 +.. gh-issue: 134087 +.. nonce: HilZWl +.. section: Library + +Remove support for arbitrary positional or keyword arguments in the C +implementation of :class:`threading.RLock` objects. This was deprecated +since Python 3.14. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-18-07-25-15 +.. gh-issue: 134173 +.. nonce: 53oOoF +.. section: Library + +Speed up :mod:`asyncio` performance of transferring state from thread pool +:class:`concurrent.futures.Future` by up to 4.4x. Patch by J. Nick Koston. + +.. + +.. date: 2025-05-17-20-23-57 +.. gh-issue: 133982 +.. nonce: smS7au +.. section: Library + +Emit :exc:`RuntimeWarning` in the Python implementation of :mod:`io` when +the :term:`file-like object ` is not closed explicitly in the +presence of multiple I/O layers. + +.. + +.. date: 2025-05-17-18-08-35 +.. gh-issue: 133890 +.. nonce: onn9_X +.. section: Library + +The :mod:`tarfile` module now handles :exc:`UnicodeEncodeError` in the same +way as :exc:`OSError` when cannot extract a member. + +.. + +.. date: 2025-05-17-13-46-20 +.. gh-issue: 134097 +.. nonce: fgkjE1 +.. section: Library + +Fix interaction of the new :term:`REPL` and :option:`-X showrefcount <-X>` +command line option. + +.. + +.. date: 2025-05-17-12-40-12 +.. gh-issue: 133889 +.. nonce: Eh-zO4 +.. section: Library + +The generated directory listing page in +:class:`http.server.SimpleHTTPRequestHandler` now only shows the decoded +path component of the requested URL, and not the query and fragment. + +.. + +.. date: 2025-05-16-20-10-25 +.. gh-issue: 134098 +.. nonce: YyTkKr +.. section: Library + +Fix handling paths that end with a percent-encoded slash (``%2f`` or +``%2F``) in :class:`http.server.SimpleHTTPRequestHandler`. + +.. + +.. date: 2025-05-16-12-40-37 +.. gh-issue: 132124 +.. nonce: T_5Odx +.. section: Library + +On POSIX-compliant systems, :func:`!multiprocessing.util.get_temp_dir` now +ignores :envvar:`TMPDIR` (and similar environment variables) if the path +length of ``AF_UNIX`` socket files exceeds the platform-specific maximum +length when using the :ref:`forkserver +` start method. Patch by Bénédikt +Tran. + +.. + +.. date: 2025-05-15-14-27-01 +.. gh-issue: 134062 +.. nonce: fRbJet +.. section: Library + +:mod:`ipaddress`: fix collisions in :meth:`~object.__hash__` for +:class:`~ipaddress.IPv4Network` and :class:`~ipaddress.IPv6Network` objects. + +.. + +.. date: 2025-05-15-00-27-09 +.. gh-issue: 134004 +.. nonce: e8k4-R +.. section: Library + +:mod:`shelve` as well as underlying :mod:`!dbm.dumb` and :mod:`!dbm.sqlite` +now have :meth:`!reorganize` methods to recover unused free space previously +occupied by deleted entries. + +.. + +.. date: 2025-05-13-18-54-56 +.. gh-issue: 133970 +.. nonce: 6G-Oi6 +.. section: Library + +Make :class:`!string.templatelib.Template` and +:class:`!string.templatelib.Interpolation` generic. + +.. + +.. date: 2025-05-13-18-21-59 +.. gh-issue: 71253 +.. nonce: -3Sf_K +.. section: Library + +Raise :exc:`ValueError` in :func:`open` if *opener* returns a negative +file-descriptor in the Python implementation of :mod:`io` to match the C +implementation. + +.. + +.. date: 2025-05-12-20-38-57 +.. gh-issue: 133960 +.. nonce: Aee79f +.. section: Library + +Simplify and improve :func:`typing.evaluate_forward_ref`. It now no longer +raises errors on certain invalid types. In several situations, it is now +able to evaluate forward references that were previously unsupported. + +.. + +.. date: 2025-05-12-06-52-10 +.. gh-issue: 133925 +.. nonce: elInBY +.. section: Library + +Make the private class ``typing._UnionGenericAlias`` hashable. + +.. + +.. date: 2025-05-11-12-56-52 +.. gh-issue: 133604 +.. nonce: kFxhc8 +.. section: Library + +Remove :func:`!platform.java_ver` which was deprecated since Python 3.13. + +.. + +.. date: 2025-05-11-11-39-05 +.. gh-issue: 133875 +.. nonce: pUar3l +.. section: Library + +Removed deprecated :meth:`!pathlib.PurePath.is_reserved`. Use +:func:`os.path.isreserved` to detect reserved paths on Windows. + +.. + +.. date: 2025-05-11-10-28-11 +.. gh-issue: 133873 +.. nonce: H03nov +.. section: Library + +Remove the deprecated ``getmark()``, ``setmark()`` and ``getmarkers()`` +methods of the :class:`~wave.Wave_read` and :class:`~wave.Wave_write` +classes, which were deprecated since Python 3.13. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-11-10-01-48 +.. gh-issue: 133866 +.. nonce: g3dHP_ +.. section: Library + +Remove the undocumented function :func:`!ctypes.SetPointerType`, which has +been deprecated since Python 3.13. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-11-08-48-55 +.. gh-issue: 133823 +.. nonce: F8udQy +.. section: Library + +Remove support for ``TD = TypedDict("TD")`` and ``TD = TypedDict("TD", +None)`` calls for constructing :class:`typing.TypedDict` objects with zero +field. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-10-17-42-03 +.. gh-issue: 125996 +.. nonce: vaQp0- +.. section: Library + +Fix thread safety of :class:`collections.OrderedDict`. Patch by Kumar +Aditya. + +.. + +.. date: 2025-05-10-12-07-54 +.. gh-issue: 133817 +.. nonce: 4GMtKV +.. section: Library + +Remove support for creating :class:`~typing.NamedTuple` classes via the +undocumented keyword argument syntax. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-10-12-06-55 +.. gh-issue: 133653 +.. nonce: Gb2aG4 +.. section: Library + +Fix :class:`argparse.ArgumentParser` with the *formatter_class* argument. +Fix TypeError when *formatter_class* is a custom subclass of +:class:`!HelpFormatter`. Fix TypeError when *formatter_class* is not a +subclass of :class:`!HelpFormatter` and non-standard *prefix_char* is used. +Fix support of colorizing when *formatter_class* is not a subclass of +:class:`!HelpFormatter`. + +.. + +.. date: 2025-05-10-11-04-47 +.. gh-issue: 133810 +.. nonce: 03WhnK +.. section: Library + +Remove :class:`!http.server.CGIHTTPRequestHandler` and ``--cgi`` flag from +the :program:`python -m http.server` command-line interface. They were +deprecated in Python 3.13. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-09-20-59-24 +.. gh-issue: 132641 +.. nonce: 3qTw44 +.. section: Library + +Fixed a race in :func:`functools.lru_cache` under free-threading. + +.. + +.. date: 2025-05-09-19-05-24 +.. gh-issue: 133783 +.. nonce: 1voCnR +.. section: Library + +Fix bug with applying :func:`copy.replace` to :mod:`ast` objects. Attributes +that default to ``None`` were incorrectly treated as required for manually +created AST nodes. + +.. + +.. date: 2025-05-09-18-29-25 +.. gh-issue: 133684 +.. nonce: Y1DFSt +.. section: Library + +Fix bug where :func:`annotationlib.get_annotations` would return the wrong +result for certain classes that are part of a class hierarchy where ``from +__future__ import annotations`` is used. + +.. + +.. date: 2025-05-09-15-50-00 +.. gh-issue: 77057 +.. nonce: fV8SU- +.. section: Library + +Fix handling of invalid markup declarations in +:class:`html.parser.HTMLParser`. + +.. + +.. date: 2025-05-09-09-10-34 +.. gh-issue: 130328 +.. nonce: s9h4By +.. section: Library + +Speedup pasting in ``PyREPL`` on Windows in a legacy console. Patch by Chris +Eibl. + +.. + +.. date: 2025-05-09-08-49-03 +.. gh-issue: 133701 +.. nonce: KI8tGz +.. section: Library + +Fix bug where :class:`typing.TypedDict` classes defined under ``from +__future__ import annotations`` and inheriting from another ``TypedDict`` +had an incorrect ``__annotations__`` attribute. + +.. + +.. date: 2025-05-08-20-45-35 +.. gh-issue: 133656 +.. nonce: cxZODA +.. section: Library + +Remove deprecated :meth:`!zipimport.zipimporter.load_module`. Use +:meth:`zipimport.zipimporter.exec_module` instead. + +.. + +.. date: 2025-05-08-20-03-20 +.. gh-issue: 133722 +.. nonce: 1-B82a +.. section: Library + +Added a *color* option to :func:`difflib.unified_diff` that colors output +similar to :program:`git diff`. + +.. + +.. date: 2025-05-08-13-43-19 +.. gh-issue: 133489 +.. nonce: 9eGS1Z +.. section: Library + +:func:`random.getrandbits` can now generate more that 2\ :sup:`31` bits. +:func:`random.randbytes` can now generate more that 256 MiB. + +.. + +.. date: 2025-05-07-22-15-15 +.. gh-issue: 133595 +.. nonce: c3U88r +.. section: Library + +Clean up :class:`sqlite3.Connection` APIs. All parameters of +:func:`sqlite3.connect` except *database* are now keyword-only. The first +three parameters of methods :meth:`~sqlite3.Connection.create_function` and +:meth:`~sqlite3.Connection.create_aggregate` are now positional-only. The +first parameter of methods :meth:`~sqlite3.Connection.set_authorizer`, +:meth:`~sqlite3.Connection.set_progress_handler` and +:meth:`~sqlite3.Connection.set_trace_callback` is now positional-only. + +.. + +.. date: 2025-05-07-19-16-41 +.. gh-issue: 133581 +.. nonce: kERUCJ +.. section: Library + +Improve unparsing of t-strings in :func:`ast.unparse` and ``from __future__ +import annotations``. Empty t-strings now round-trip correctly and +formatting in interpolations is preserved. Patch by Jelle Zijlstra. + +.. + +.. date: 2025-05-07-14-36-30 +.. gh-issue: 133577 +.. nonce: BggPk9 +.. section: Library + +Add parameter ``formatter`` to :func:`logging.basicConfig`. + +.. + +.. date: 2025-05-07-13-31-06 +.. gh-issue: 92897 +.. nonce: ubeqGE +.. section: Library + +Removed the ``check_home`` parameter from :func:`sysconfig.is_python_build`, +deprecated since Python 3.12. + +.. + +.. date: 2025-05-06-22-54-37 +.. gh-issue: 133551 +.. nonce: rfy1tJ +.. section: Library + +Support t-strings (:pep:`750`) in :mod:`annotationlib`. Patch by Jelle +Zijlstra. + +.. + +.. date: 2025-05-06-14-44-55 +.. gh-issue: 133517 +.. nonce: Ca6NgW +.. section: Library + +Remove :func:`os.listdrives`, :func:`os.listvolumes` and +:func:`os.listmounts` in non Windows desktop builds since the underlying +functionality is missing. + +.. + +.. date: 2025-05-05-22-11-24 +.. gh-issue: 133439 +.. nonce: LpmyFz +.. section: Library + +Fix dot commands with trailing spaces are mistaken for multi-line SQL +statements in the sqlite3 command-line interface. + +.. + +.. date: 2025-05-05-18-50-00 +.. gh-issue: 133447 +.. nonce: ajshdb +.. section: Library + +Add basic color to :mod:`sqlite3` CLI interface. + +.. + +.. date: 2025-05-05-10-41-41 +.. gh-issue: 133253 +.. nonce: J5-xDD +.. section: Library + +Fix thread-safety issues in :mod:`linecache`. + +.. + +.. date: 2025-05-05-03-14-08 +.. gh-issue: 133390 +.. nonce: AuTggn +.. section: Library + +Support keyword completion in the :mod:`sqlite3` command-line interface and +add :data:`sqlite3.SQLITE_KEYWORDS` constant. + +.. + +.. date: 2025-05-04-17-04-55 +.. gh-issue: 132493 +.. nonce: huirKi +.. section: Library + +Avoid accessing ``__annotations__`` unnecessarily in +:func:`inspect.signature`. + +.. + +.. date: 2025-05-01-16-03-11 +.. gh-issue: 133017 +.. nonce: k7RLQp +.. section: Library + +Improve the error message of :func:`multiprocessing.sharedctypes.Array`, +:func:`multiprocessing.sharedctypes.RawArray`, +:func:`multiprocessing.sharedctypes.Value` and +:func:`multiprocessing.sharedctypes.RawValue` when an invalid typecode is +passed. Patch by Tomas Roun + +.. + +.. date: 2025-05-01-10-56-44 +.. gh-issue: 132813 +.. nonce: rKurvp +.. section: Library + +Improve error messages for incorrect types and values of +:class:`csv.Dialect` attributes. + +.. + +.. date: 2025-04-30-19-32-18 +.. gh-issue: 132969 +.. nonce: EagQ3G +.. section: Library + +Prevent the :class:`~concurrent.futures.ProcessPoolExecutor` executor +thread, which remains running when :meth:`shutdown(wait=False) +`, from attempting to adjust the +pool's worker processes after the object state has already been reset during +shutdown. A combination of conditions, including a worker process having +terminated abormally, resulted in an exception and a potential hang when the +still-running executor thread attempted to replace dead workers within the +pool. + +.. + +.. date: 2025-04-29-11-48-46 +.. gh-issue: 132876 +.. nonce: lyTQGZ +.. section: Library + +``ldexp()`` on Windows doesn't round subnormal results before Windows 11, +but should. Python's :func:`math.ldexp` wrapper now does round them, so +results may change slightly, in rare cases of very small results, on Windows +versions before 11. + +.. + +.. date: 2025-04-26-15-50-12 +.. gh-issue: 133009 +.. nonce: etBuz5 +.. section: Library + +:mod:`xml.etree.ElementTree`: Fix a crash in :meth:`Element.__deepcopy__ +` when the element is concurrently mutated. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-04-25-16-06-53 +.. gh-issue: 132908 +.. nonce: wV5rja +.. section: Library + +Add :func:`math.isnormal` and :func:`math.issubnormal` functions. Patch by +Sergey B Kirpichev. + +.. + +.. date: 2025-04-25-11-53-37 +.. gh-issue: 95380 +.. nonce: 7dvPe- +.. section: Library + +:func:`fcntl.fcntl` and :func:`fcntl.ioctl`: Remove the 1024 bytes limit on +the size of not mutated bytes-like argument. + +.. + +.. date: 2025-04-25-11-48-00 +.. gh-issue: 122781 +.. nonce: ajsdns +.. section: Library + +Fix ``%z`` directive in :func:`datetime.datetime.strptime` to allow for no +provided offset as was documented. + +.. + +.. date: 2025-04-22-21-00-23 +.. gh-issue: 123471 +.. nonce: asOLA2 +.. section: Library + +Make concurrent iterations over :class:`itertools.combinations` and +:class:`itertools.product` safe under free-threading. + +.. + +.. date: 2025-04-21-01-05-14 +.. gh-issue: 127081 +.. nonce: Egrpq7 +.. section: Library + +Fix libc thread safety issues with :mod:`dbm` by performing stateful +operations in critical sections. + +.. + +.. date: 2025-04-21-01-03-15 +.. gh-issue: 127081 +.. nonce: WXRliX +.. section: Library + +Fix libc thread safety issues with :mod:`os` by replacing ``getlogin`` with +``getlogin_r`` re-entrant version. + +.. + +.. date: 2025-04-21-00-58-04 +.. gh-issue: 127081 +.. nonce: 3DCl92 +.. section: Library + +Fix libc thread safety issues with :mod:`pwd` by locking access to +``getpwall``. + +.. + +.. date: 2025-04-16-21-02-57 +.. gh-issue: 132551 +.. nonce: Psa7pL +.. section: Library + +Make :class:`io.BytesIO` safe in :term:`free-threaded ` +build. + +.. + +.. date: 2025-04-08-07-25-10 +.. gh-issue: 107583 +.. nonce: JGfbhq +.. section: Library + +Fix :class:`!Flag` inversion when flag set has missing values +(:class:`!IntFlag` still flips all bits); fix negative assigned values +during flag creation (both :class:`!Flag` and :class:`!IntFlag` ignore +missing values). + +.. + +.. date: 2025-04-07-10-20-16 +.. gh-issue: 87790 +.. nonce: X2SjJe +.. section: Library + +Support underscore and comma as thousands separators in the fractional part +for :class:`~fractions.Fraction`'s formatting. Patch by Sergey B Kirpichev. + +.. + +.. date: 2025-04-07-09-53-54 +.. gh-issue: 87790 +.. nonce: 6nj3zQ +.. section: Library + +Support underscore and comma as thousands separators in the fractional part +for :class:`~decimal.Decimal`'s formatting. Patch by Sergey B Kirpichev. + +.. + +.. date: 2025-04-07-06-41-54 +.. gh-issue: 131884 +.. nonce: ym9BJN +.. section: Library + +Fix formatting issues in :func:`json.dump` when both *indent* and *skipkeys* +are used. + +.. + +.. date: 2025-03-27-08-13-32 +.. gh-issue: 131788 +.. nonce: 0RWiFc +.. section: Library + +Make ``ResourceTracker.send`` from :mod:`multiprocessing` re-entrant safe + +.. + +.. date: 2025-03-19-12-41-42 +.. gh-issue: 91349 +.. nonce: 8eTOCP +.. section: Library + +Adjust default ``compressionlevel=`` to 6 (down from 9) in :mod:`gzip` and +:mod:`tarfile`. It is the default level used by most compression tools and a +better tradeoff between speed and performance. + +.. + +.. date: 2025-03-17-21-21-06 +.. gh-issue: 131146 +.. nonce: A5Obgv +.. section: Library + +Fix :class:`calendar.TextCalendar`, :class:`calendar.HTMLCalendar`, and the +:mod:`calendar` CLI to display month names in the nominative case by adding +:data:`calendar.standalone_month_name` and +:data:`calendar.standalone_month_abbr`, which provide month names and +abbreviations in the grammatical form used when a month name stands by +itself, if the locale supports it. + +.. + +.. date: 2025-03-13-20-48-58 +.. gh-issue: 123471 +.. nonce: cM4w4f +.. section: Library + +Make concurrent iterations over :class:`itertools.cycle` safe under +free-threading. + +.. + +.. date: 2025-03-11-05-24-14 +.. gh-issue: 130664 +.. nonce: g0yNMm +.. section: Library + +Handle corner-case for :class:`~fractions.Fraction`'s formatting: treat +zero-padding (preceding the width field by a zero (``'0'``) character) as an +equivalent to a fill character of ``'0'`` with an alignment type of ``'='``, +just as in case of :class:`float`'s. + +.. + +.. date: 2025-03-09-03-13-41 +.. gh-issue: 130999 +.. nonce: tBRBVB +.. section: Library + +Avoid exiting the new REPL and offer suggestions even if there are +non-string candidates when errors occur. + +.. + +.. date: 2025-03-08-17-07-00 +.. gh-issue: 88473 +.. nonce: qg23g8 +.. section: Library + +Implement a fast path for :class:`datetime.date` objects in +:func:`datetime.date.today` which results in a 5x performance gain while +proper subclasses retain their previous performance. + +.. + +.. date: 2024-11-25-10-22-08 +.. gh-issue: 126883 +.. nonce: MAEF7g +.. section: Library + +Add check that timezone fields are in range for +:meth:`datetime.datetime.fromisoformat` and +:meth:`datetime.time.fromisoformat`. Patch by Semyon Moroz. + +.. + +.. date: 2024-10-28-06-54-22 +.. gh-issue: 125028 +.. nonce: GEY8Ws +.. section: Library + +:data:`functools.Placeholder` cannot be passed to :func:`functools.partial` +as a keyword argument. + +.. + +.. date: 2024-10-22-16-21-55 +.. gh-issue: 125843 +.. nonce: 2ttzYo +.. section: Library + +If possible, indicate which :mod:`curses` C function or macro is responsible +for raising a :exc:`curses.error` exception. Patch by Bénédikt Tran. + +.. + +.. date: 2024-10-17-01-12-22 +.. gh-issue: 119109 +.. nonce: u4hcvb +.. section: Library + +:func:`functools.partial` calls are now faster when keyword arguments are +used. + +.. + +.. date: 2024-09-13-09-48-25 +.. gh-issue: 124033 +.. nonce: WNudS0 +.. section: Library + +``SimplePath`` is now presented in ``importlib.metadata.__all__``. + +.. + +.. date: 2024-09-13-09-46-47 +.. gh-issue: 91216 +.. nonce: LuOsF4 +.. section: Library + +``importlib.metadata`` now raises a ``KeyError`` instead of returning +``None`` when a key is missing from the metadata. + +.. + +.. date: 2024-09-13-09-43-15 +.. gh-issue: 120492 +.. nonce: Mm6CJ6 +.. section: Library + +``importlib.metadata`` now prioritizes valid dists to invalid dists when +retrieving by name. + +.. + +.. date: 2024-07-16-00-01-04 +.. gh-issue: 99631 +.. nonce: GWD4fD +.. section: Library + +The :mod:`shelve` module now accepts custom serialization and +deserialization functions. + +.. + +.. date: 2024-07-06-14-32-30 +.. gh-issue: 119186 +.. nonce: E5B1HQ +.. section: Library + +Slightly speed up :func:`os.walk` by calling :func:`os.path.join` less +often. + +.. + +.. date: 2024-06-06-17-49-07 +.. gh-issue: 120170 +.. nonce: DUxhmT +.. section: Library + +Fix an issue in the :mod:`!_pickle` extension module in which importing +:mod:`multiprocessing` could change how pickle identifies which module an +object belongs to, potentially breaking the unpickling of those objects. + +.. + +.. date: 2024-05-13-09-50-31 +.. gh-issue: 118981 +.. nonce: zgOQPv +.. section: Library + +Fix potential hang in ``multiprocessing.popen_spawn_posix`` that can happen +when the child proc dies early by closing the child fds right away. + +.. + +.. date: 2023-07-05-14-34-10 +.. gh-issue: 105497 +.. nonce: HU5u89 +.. section: Library + +Fix flag mask inversion when unnamed flags exist. + +.. + +.. date: 2023-03-13-22-51-40 +.. gh-issue: 99813 +.. nonce: 40TV02 +.. section: Library + +:mod:`ssl` now uses ``SSL_sendfile`` internally when it is possible (see +:data:`~ssl.OP_ENABLE_KTLS`). The function sends a file more efficiently +because it performs TLS encryption in the kernel to avoid additional context +switches. Patch by Illia Volochii. + +.. + +.. date: 2023-02-13-21-56-38 +.. gh-issue: 62824 +.. nonce: CBZzX3 +.. section: Library + +Fix aliases for ``iso8859_8`` encoding. Patch by Dave Goncalves. + +.. + +.. date: 2023-02-13-21-41-34 +.. gh-issue: 86155 +.. nonce: ppIGSC +.. section: Library + +:meth:`html.parser.HTMLParser.close` no longer loses data when the +````. - -* Multiple slashes and whitespaces between the last attribute and closing ``>`` - are now ignored in both start and end tags. E.g. ````. - -* Multiple ``=`` between attribute name and value are no longer collapsed. - E.g. ```` produces attribute "foo" with value "=bar". diff --git a/Misc/NEWS.d/next/Security/2025-06-27-21-23-19.gh-issue-136053.QZxcee.rst b/Misc/NEWS.d/next/Security/2025-06-27-21-23-19.gh-issue-136053.QZxcee.rst deleted file mode 100644 index 93caed3aa3b..00000000000 --- a/Misc/NEWS.d/next/Security/2025-06-27-21-23-19.gh-issue-136053.QZxcee.rst +++ /dev/null @@ -1 +0,0 @@ -:mod:`marshal`: fix a possible crash when deserializing :class:`slice` objects. diff --git a/Misc/NEWS.d/next/Security/2025-09-24-13-39-56.gh-issue-139283.jODz_q.rst b/Misc/NEWS.d/next/Security/2025-09-24-13-39-56.gh-issue-139283.jODz_q.rst deleted file mode 100644 index a8fd83bca52..00000000000 --- a/Misc/NEWS.d/next/Security/2025-09-24-13-39-56.gh-issue-139283.jODz_q.rst +++ /dev/null @@ -1,4 +0,0 @@ -:mod:`sqlite3`: correctly handle maximum number of rows to fetch in -:meth:`Cursor.fetchmany ` and reject negative -values for :attr:`Cursor.arraysize `. Patch by -Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Security/2025-09-29-00-01-28.gh-issue-139400.X2T-jO.rst b/Misc/NEWS.d/next/Security/2025-09-29-00-01-28.gh-issue-139400.X2T-jO.rst deleted file mode 100644 index a5dea3b5f81..00000000000 --- a/Misc/NEWS.d/next/Security/2025-09-29-00-01-28.gh-issue-139400.X2T-jO.rst +++ /dev/null @@ -1,4 +0,0 @@ -:mod:`xml.parsers.expat`: Make sure that parent Expat parsers are only -garbage-collected once they are no longer referenced by subparsers created -by :meth:`~xml.parsers.expat.xmlparser.ExternalEntityParserCreate`. -Patch by Sebastian Pipping. diff --git a/Misc/NEWS.d/next/Security/2025-10-07-19-31-34.gh-issue-139700.vNHU1O.rst b/Misc/NEWS.d/next/Security/2025-10-07-19-31-34.gh-issue-139700.vNHU1O.rst deleted file mode 100644 index a8e7a1f1878..00000000000 --- a/Misc/NEWS.d/next/Security/2025-10-07-19-31-34.gh-issue-139700.vNHU1O.rst +++ /dev/null @@ -1,3 +0,0 @@ -Check consistency of the zip64 end of central directory record. Support -records with "zip64 extensible data" if there are no bytes prepended to the -ZIP file. diff --git a/Misc/NEWS.d/next/Tests/2025-05-08-15-06-01.gh-issue-133639.50-kbV.rst b/Misc/NEWS.d/next/Tests/2025-05-08-15-06-01.gh-issue-133639.50-kbV.rst deleted file mode 100644 index 68826cd95fa..00000000000 --- a/Misc/NEWS.d/next/Tests/2025-05-08-15-06-01.gh-issue-133639.50-kbV.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix ``TestPyReplAutoindent.test_auto_indent_default()`` doesn't run -``input_code``. diff --git a/Misc/NEWS.d/next/Tests/2025-05-09-04-11-06.gh-issue-133682.-_lwo3.rst b/Misc/NEWS.d/next/Tests/2025-05-09-04-11-06.gh-issue-133682.-_lwo3.rst deleted file mode 100644 index ebd17f73ca5..00000000000 --- a/Misc/NEWS.d/next/Tests/2025-05-09-04-11-06.gh-issue-133682.-_lwo3.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed test case ``test.test_annotationlib.TestStringFormat.test_displays`` which ensures proper handling of complex data structures (lists, sets, dictionaries, and tuples) in string annotations. diff --git a/Misc/NEWS.d/next/Tests/2025-05-09-14-54-48.gh-issue-133744.LCquu0.rst b/Misc/NEWS.d/next/Tests/2025-05-09-14-54-48.gh-issue-133744.LCquu0.rst deleted file mode 100644 index f19186db1ad..00000000000 --- a/Misc/NEWS.d/next/Tests/2025-05-09-14-54-48.gh-issue-133744.LCquu0.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix multiprocessing interrupt test. Add an event to synchronize the parent -process with the child process: wait until the child process starts -sleeping. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2025-05-23-09-19-52.gh-issue-134567.hwEIMb.rst b/Misc/NEWS.d/next/Tests/2025-05-23-09-19-52.gh-issue-134567.hwEIMb.rst deleted file mode 100644 index 42e4a01c0cc..00000000000 --- a/Misc/NEWS.d/next/Tests/2025-05-23-09-19-52.gh-issue-134567.hwEIMb.rst +++ /dev/null @@ -1,2 +0,0 @@ -Expose log formatter to users in TestCase.assertLogs. -:func:`unittest.TestCase.assertLogs` will now optionally accept a formatter that will be used to format the strings in output if provided. diff --git a/Misc/NEWS.d/next/Tests/2025-06-04-13-07-44.gh-issue-135120.NapnZT.rst b/Misc/NEWS.d/next/Tests/2025-06-04-13-07-44.gh-issue-135120.NapnZT.rst deleted file mode 100644 index 772173774b1..00000000000 --- a/Misc/NEWS.d/next/Tests/2025-06-04-13-07-44.gh-issue-135120.NapnZT.rst +++ /dev/null @@ -1 +0,0 @@ -Add :func:`!test.support.subTests`. diff --git a/Misc/NEWS.d/next/Tests/2025-06-11-16-52-49.gh-issue-135401.ccMXmL.rst b/Misc/NEWS.d/next/Tests/2025-06-11-16-52-49.gh-issue-135401.ccMXmL.rst deleted file mode 100644 index 6885fba30db..00000000000 --- a/Misc/NEWS.d/next/Tests/2025-06-11-16-52-49.gh-issue-135401.ccMXmL.rst +++ /dev/null @@ -1 +0,0 @@ -Add a new GitHub CI job to test the :mod:`ssl` module with `AWS-LC `_ as the backing cryptography and TLS library. diff --git a/Misc/NEWS.d/next/Tests/2025-06-14-13-20-17.gh-issue-135489.Uh0yVO.rst b/Misc/NEWS.d/next/Tests/2025-06-14-13-20-17.gh-issue-135489.Uh0yVO.rst deleted file mode 100644 index 2c9ecc51829..00000000000 --- a/Misc/NEWS.d/next/Tests/2025-06-14-13-20-17.gh-issue-135489.Uh0yVO.rst +++ /dev/null @@ -1 +0,0 @@ -Show verbose output for failing tests during PGO profiling step with --enable-optimizations. diff --git a/Misc/NEWS.d/next/Tests/2025-06-17-08-48-08.gh-issue-132815.CY1Esu.rst b/Misc/NEWS.d/next/Tests/2025-06-17-08-48-08.gh-issue-132815.CY1Esu.rst deleted file mode 100644 index 5b7485ce2d6..00000000000 --- a/Misc/NEWS.d/next/Tests/2025-06-17-08-48-08.gh-issue-132815.CY1Esu.rst +++ /dev/null @@ -1 +0,0 @@ -Fix test__opcode: add ``JUMP_BACKWARD`` to specialization stats. diff --git a/Misc/NEWS.d/next/Tests/2025-06-19-15-29-38.gh-issue-135494.FVl9a0.rst b/Misc/NEWS.d/next/Tests/2025-06-19-15-29-38.gh-issue-135494.FVl9a0.rst deleted file mode 100644 index 832d1fe033e..00000000000 --- a/Misc/NEWS.d/next/Tests/2025-06-19-15-29-38.gh-issue-135494.FVl9a0.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix regrtest to support excluding tests from ``--pgo`` tests. Patch by -Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2025-06-26-15-15-35.gh-issue-135966.EBpF8Y.rst b/Misc/NEWS.d/next/Tests/2025-06-26-15-15-35.gh-issue-135966.EBpF8Y.rst deleted file mode 100644 index 8dc007431f3..00000000000 --- a/Misc/NEWS.d/next/Tests/2025-06-26-15-15-35.gh-issue-135966.EBpF8Y.rst +++ /dev/null @@ -1 +0,0 @@ -The iOS testbed now handles the ``app_packages`` folder as a site directory. diff --git a/Misc/NEWS.d/next/Tests/2025-09-21-16-00-30.gh-issue-138313.lBx2en.rst b/Misc/NEWS.d/next/Tests/2025-09-21-16-00-30.gh-issue-138313.lBx2en.rst deleted file mode 100644 index b70c59201df..00000000000 --- a/Misc/NEWS.d/next/Tests/2025-09-21-16-00-30.gh-issue-138313.lBx2en.rst +++ /dev/null @@ -1,2 +0,0 @@ -Restore skipped test and add janky workaround to prevent select buildbots -from failing with a ResourceWarning. diff --git a/Misc/NEWS.d/next/Tests/2025-09-22-15-40-09.gh-issue-139208.Tc13dl.rst b/Misc/NEWS.d/next/Tests/2025-09-22-15-40-09.gh-issue-139208.Tc13dl.rst deleted file mode 100644 index b8672ac83e1..00000000000 --- a/Misc/NEWS.d/next/Tests/2025-09-22-15-40-09.gh-issue-139208.Tc13dl.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix regrtest ``--fast-ci --verbose``: don't ignore the ``--verbose`` option -anymore. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-05-19-14-57-46.gh-issue-134215.sbdDK6.rst b/Misc/NEWS.d/next/Tools-Demos/2025-05-19-14-57-46.gh-issue-134215.sbdDK6.rst deleted file mode 100644 index 546ed2a56b6..00000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2025-05-19-14-57-46.gh-issue-134215.sbdDK6.rst +++ /dev/null @@ -1 +0,0 @@ -:term:`REPL` import autocomplete only suggests private modules when explicitly specified. diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-06-11-12-14-06.gh-issue-135379.25ttXq.rst b/Misc/NEWS.d/next/Tools-Demos/2025-06-11-12-14-06.gh-issue-135379.25ttXq.rst deleted file mode 100644 index ebe3ab0e7d1..00000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2025-06-11-12-14-06.gh-issue-135379.25ttXq.rst +++ /dev/null @@ -1,4 +0,0 @@ -The cases generator no longer accepts type annotations on stack items. -Conversions to non-default types are now done explicitly in bytecodes.c and -optimizer_bytecodes.c. This will simplify code generation for top-of-stack -caching and other future features. diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-06-26-15-58-13.gh-issue-135968.C4v_-W.rst b/Misc/NEWS.d/next/Tools-Demos/2025-06-26-15-58-13.gh-issue-135968.C4v_-W.rst deleted file mode 100644 index 1c0b3825c71..00000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2025-06-26-15-58-13.gh-issue-135968.C4v_-W.rst +++ /dev/null @@ -1 +0,0 @@ -Stubs for ``strip`` are now provided as part of an iOS install. diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-07-05-15-10-42.gh-issue-136251.GRM6o8.rst b/Misc/NEWS.d/next/Tools-Demos/2025-07-05-15-10-42.gh-issue-136251.GRM6o8.rst deleted file mode 100644 index 6a35afe15e3..00000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2025-07-05-15-10-42.gh-issue-136251.GRM6o8.rst +++ /dev/null @@ -1 +0,0 @@ -Fixes and usability improvements for ``Tools/wasm/emscripten/web_example`` diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-07-30-10-28-35.gh-issue-137243.NkdUqH.rst b/Misc/NEWS.d/next/Tools-Demos/2025-07-30-10-28-35.gh-issue-137243.NkdUqH.rst deleted file mode 100644 index c9c6c2ca287..00000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2025-07-30-10-28-35.gh-issue-137243.NkdUqH.rst +++ /dev/null @@ -1,2 +0,0 @@ -Have Tools/wasm/wasi detect a WASI SDK install in /opt when it was directly -extracted from a release tarball. diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-07-30-11-15-47.gh-issue-137248.8IxwY3.rst b/Misc/NEWS.d/next/Tools-Demos/2025-07-30-11-15-47.gh-issue-137248.8IxwY3.rst deleted file mode 100644 index 311ade0c8f3..00000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2025-07-30-11-15-47.gh-issue-137248.8IxwY3.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add a ``--logdir`` option to ``Tools/wasm/wasi`` for specifying where to -write log files. diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-08-01-13-27-43.gh-issue-137025.ubuhQC.rst b/Misc/NEWS.d/next/Tools-Demos/2025-08-01-13-27-43.gh-issue-137025.ubuhQC.rst deleted file mode 100644 index 73c79564006..00000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2025-08-01-13-27-43.gh-issue-137025.ubuhQC.rst +++ /dev/null @@ -1,3 +0,0 @@ -The ``wasm_build.py`` script has been removed. ``Tools/wasm/emscripten`` -and ``Tools/wasm/wasi`` should be used instead, as described in the `Dev -Guide `__. diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-08-06-11-54-55.gh-issue-137484.8iFAQs.rst b/Misc/NEWS.d/next/Tools-Demos/2025-08-06-11-54-55.gh-issue-137484.8iFAQs.rst deleted file mode 100644 index bd7bc0984ec..00000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2025-08-06-11-54-55.gh-issue-137484.8iFAQs.rst +++ /dev/null @@ -1,2 +0,0 @@ -Have ``Tools/wasm/wasi`` put the build Python into a directory named after -the build triple instead of "build". diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-08-21-14-04-50.gh-issue-137873.qxffLt.rst b/Misc/NEWS.d/next/Tools-Demos/2025-08-21-14-04-50.gh-issue-137873.qxffLt.rst deleted file mode 100644 index 5b75858560c..00000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2025-08-21-14-04-50.gh-issue-137873.qxffLt.rst +++ /dev/null @@ -1,3 +0,0 @@ -The iOS test runner has been simplified, resolving some issues that have -been observed using the runner in GitHub Actions and Azure Pipelines test -environments. diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-08-27-11-14-53.gh-issue-138171.Suz8ob.rst b/Misc/NEWS.d/next/Tools-Demos/2025-08-27-11-14-53.gh-issue-138171.Suz8ob.rst deleted file mode 100644 index 0a933ec754c..00000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2025-08-27-11-14-53.gh-issue-138171.Suz8ob.rst +++ /dev/null @@ -1,3 +0,0 @@ -A script for building an iOS XCframework was added. As part of this change, -the top level ``iOS`` folder has been moved to be a subdirectory of the -``Apple`` folder. diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-08-28-06-22-26.gh-issue-132006.eZQmc6.rst b/Misc/NEWS.d/next/Tools-Demos/2025-08-28-06-22-26.gh-issue-132006.eZQmc6.rst deleted file mode 100644 index 8e791076864..00000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2025-08-28-06-22-26.gh-issue-132006.eZQmc6.rst +++ /dev/null @@ -1,2 +0,0 @@ -XCframeworks now include privacy manifests to satisfy Apple App Store -submission requirements. diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-09-25-10-31-02.gh-issue-139330.5WWkY0.rst b/Misc/NEWS.d/next/Tools-Demos/2025-09-25-10-31-02.gh-issue-139330.5WWkY0.rst deleted file mode 100644 index 77e74bafaec..00000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2025-09-25-10-31-02.gh-issue-139330.5WWkY0.rst +++ /dev/null @@ -1,3 +0,0 @@ -SBOM generation tool didn't cross-check the version and checksum values -against the ``Modules/expat/refresh.sh`` script, leading to the values -becoming out-of-date during routine updates. diff --git a/Misc/NEWS.d/next/Windows/2025-03-31-15-37-57.gh-issue-131942.jip_aL.rst b/Misc/NEWS.d/next/Windows/2025-03-31-15-37-57.gh-issue-131942.jip_aL.rst deleted file mode 100644 index 837f7265bba..00000000000 --- a/Misc/NEWS.d/next/Windows/2025-03-31-15-37-57.gh-issue-131942.jip_aL.rst +++ /dev/null @@ -1 +0,0 @@ -Use the Python-specific :c:macro:`Py_DEBUG` macro rather than :c:macro:`!_DEBUG` in Windows-related C code. Patch by Xuehai Pan. diff --git a/Misc/NEWS.d/next/Windows/2025-05-07-08-19-15.gh-issue-133537.yzf963.rst b/Misc/NEWS.d/next/Windows/2025-05-07-08-19-15.gh-issue-133537.yzf963.rst deleted file mode 100644 index 94e45f9d8ee..00000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-07-08-19-15.gh-issue-133537.yzf963.rst +++ /dev/null @@ -1 +0,0 @@ -Avoid using console I/O in WinAPI partitions that don’t support it diff --git a/Misc/NEWS.d/next/Windows/2025-05-07-09-02-19.gh-issue-133562.lqqNW1.rst b/Misc/NEWS.d/next/Windows/2025-05-07-09-02-19.gh-issue-133562.lqqNW1.rst deleted file mode 100644 index 884425b839a..00000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-07-09-02-19.gh-issue-133562.lqqNW1.rst +++ /dev/null @@ -1 +0,0 @@ -Disable handling of security descriptors by :func:`os.mkdir` with mode ``0o700`` on WinAPI partitions that do not support it. This only affects custom builds for specialized targets. diff --git a/Misc/NEWS.d/next/Windows/2025-05-07-11-25-29.gh-issue-133568.oYV0d8.rst b/Misc/NEWS.d/next/Windows/2025-05-07-11-25-29.gh-issue-133568.oYV0d8.rst deleted file mode 100644 index 1e2a5b582cb..00000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-07-11-25-29.gh-issue-133568.oYV0d8.rst +++ /dev/null @@ -1 +0,0 @@ -Fix compile error when using a WinAPI partition that doesn't support the RPC runtime library. diff --git a/Misc/NEWS.d/next/Windows/2025-05-07-11-45-30.gh-issue-133572.Xc2zxH.rst b/Misc/NEWS.d/next/Windows/2025-05-07-11-45-30.gh-issue-133572.Xc2zxH.rst deleted file mode 100644 index 993eceab0b7..00000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-07-11-45-30.gh-issue-133572.Xc2zxH.rst +++ /dev/null @@ -1 +0,0 @@ -Avoid LsaNtStatus to WinError conversion on unsupported WinAPI partitions. diff --git a/Misc/NEWS.d/next/Windows/2025-05-07-13-04-22.gh-issue-133580.jBMujJ.rst b/Misc/NEWS.d/next/Windows/2025-05-07-13-04-22.gh-issue-133580.jBMujJ.rst deleted file mode 100644 index 414a6f87e65..00000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-07-13-04-22.gh-issue-133580.jBMujJ.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :func:`sys.getwindowsversion` failing without setting an exception when called on some WinAPI partitions. diff --git a/Misc/NEWS.d/next/Windows/2025-05-08-19-07-26.gh-issue-133626.yFTKYK.rst b/Misc/NEWS.d/next/Windows/2025-05-08-19-07-26.gh-issue-133626.yFTKYK.rst deleted file mode 100644 index 6c80d96bb83..00000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-08-19-07-26.gh-issue-133626.yFTKYK.rst +++ /dev/null @@ -1,2 +0,0 @@ -Ensures packages are not accidentally bundled into the traditional -installer. diff --git a/Misc/NEWS.d/next/Windows/2025-05-13-13-25-27.gh-issue-133779.-YcTBz.rst b/Misc/NEWS.d/next/Windows/2025-05-13-13-25-27.gh-issue-133779.-YcTBz.rst deleted file mode 100644 index 550600d5eeb..00000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-13-13-25-27.gh-issue-133779.-YcTBz.rst +++ /dev/null @@ -1,6 +0,0 @@ -Reverts the change to generate different :file:`pyconfig.h` files based on -compiler settings, as it was frequently causing extension builds to break. -In particular, the ``Py_GIL_DISABLED`` preprocessor variable must now always -be defined explicitly when compiling for the experimental free-threaded -runtime. The :func:`sysconfig.get_config_var` function can be used to -determine whether the current runtime was compiled with that flag or not. diff --git a/Misc/NEWS.d/next/Windows/2025-05-19-03-02-04.gh-issue-76023.vHOf6M.rst b/Misc/NEWS.d/next/Windows/2025-05-19-03-02-04.gh-issue-76023.vHOf6M.rst deleted file mode 100644 index 958f4f4a440..00000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-19-03-02-04.gh-issue-76023.vHOf6M.rst +++ /dev/null @@ -1 +0,0 @@ -Make :func:`os.path.realpath` ignore Windows error 1005 when in non-strict mode. diff --git a/Misc/NEWS.d/next/Windows/2025-05-20-21-43-20.gh-issue-130727.-69t4D.rst b/Misc/NEWS.d/next/Windows/2025-05-20-21-43-20.gh-issue-130727.-69t4D.rst deleted file mode 100644 index dc10b3e62c8..00000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-20-21-43-20.gh-issue-130727.-69t4D.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a race in internal calls into WMI that can result in an "invalid handle" -exception under high load. Patch by Chris Eibl. diff --git a/Misc/NEWS.d/next/Windows/2025-06-03-18-26-54.gh-issue-135099.Q9usKm.rst b/Misc/NEWS.d/next/Windows/2025-06-03-18-26-54.gh-issue-135099.Q9usKm.rst deleted file mode 100644 index 36e70b1c0d8..00000000000 --- a/Misc/NEWS.d/next/Windows/2025-06-03-18-26-54.gh-issue-135099.Q9usKm.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a crash that could occur on Windows when a background thread waits on a -:c:type:`PyMutex` while the main thread is shutting down the interpreter. diff --git a/Misc/NEWS.d/next/Windows/2025-07-27-02-16-53.gh-issue-137134.W0WpDF.rst b/Misc/NEWS.d/next/Windows/2025-07-27-02-16-53.gh-issue-137134.W0WpDF.rst deleted file mode 100644 index ddccf95b7d0..00000000000 --- a/Misc/NEWS.d/next/Windows/2025-07-27-02-16-53.gh-issue-137134.W0WpDF.rst +++ /dev/null @@ -1 +0,0 @@ -Update Windows installer to ship with SQLite 3.50.4. diff --git a/Misc/NEWS.d/next/Windows/2025-07-27-14-25-11.gh-issue-137136.xNthFT.rst b/Misc/NEWS.d/next/Windows/2025-07-27-14-25-11.gh-issue-137136.xNthFT.rst deleted file mode 100644 index 5c83af0ba71..00000000000 --- a/Misc/NEWS.d/next/Windows/2025-07-27-14-25-11.gh-issue-137136.xNthFT.rst +++ /dev/null @@ -1,2 +0,0 @@ -Suppress build warnings when build on Windows with -``--experimental-jit-interpreter``. diff --git a/Misc/NEWS.d/next/Windows/2025-09-03-01-07-44.gh-issue-138314.IeWQ2i.rst b/Misc/NEWS.d/next/Windows/2025-09-03-01-07-44.gh-issue-138314.IeWQ2i.rst deleted file mode 100644 index 1f9eadcccf2..00000000000 --- a/Misc/NEWS.d/next/Windows/2025-09-03-01-07-44.gh-issue-138314.IeWQ2i.rst +++ /dev/null @@ -1 +0,0 @@ -Add :func:`winreg.DeleteTree`. diff --git a/Misc/NEWS.d/next/Windows/2025-09-15-15-34-29.gh-issue-138896.lkiF_7.rst b/Misc/NEWS.d/next/Windows/2025-09-15-15-34-29.gh-issue-138896.lkiF_7.rst deleted file mode 100644 index 018bf20aeb9..00000000000 --- a/Misc/NEWS.d/next/Windows/2025-09-15-15-34-29.gh-issue-138896.lkiF_7.rst +++ /dev/null @@ -1 +0,0 @@ -Fix error installing C runtime on non-updated Windows machines diff --git a/Misc/NEWS.d/next/Windows/2025-10-04-12-18-45.gh-issue-139573.EO9kVB.rst b/Misc/NEWS.d/next/Windows/2025-10-04-12-18-45.gh-issue-139573.EO9kVB.rst deleted file mode 100644 index 02a3bfb41ce..00000000000 --- a/Misc/NEWS.d/next/Windows/2025-10-04-12-18-45.gh-issue-139573.EO9kVB.rst +++ /dev/null @@ -1 +0,0 @@ -Updated bundled version of OpenSSL to 3.0.18. diff --git a/Misc/NEWS.d/next/Windows/2025-10-08-22-54-38.gh-issue-139810.LAaemi.rst b/Misc/NEWS.d/next/Windows/2025-10-08-22-54-38.gh-issue-139810.LAaemi.rst deleted file mode 100644 index 55252f288b0..00000000000 --- a/Misc/NEWS.d/next/Windows/2025-10-08-22-54-38.gh-issue-139810.LAaemi.rst +++ /dev/null @@ -1,2 +0,0 @@ -Installing with ``py install 3[.x]-dev`` will now select final versions as -well as prereleases. diff --git a/Misc/NEWS.d/next/macOS/2025-07-27-02-17-40.gh-issue-137134.pjgITs.rst b/Misc/NEWS.d/next/macOS/2025-07-27-02-17-40.gh-issue-137134.pjgITs.rst deleted file mode 100644 index 957270f5aba..00000000000 --- a/Misc/NEWS.d/next/macOS/2025-07-27-02-17-40.gh-issue-137134.pjgITs.rst +++ /dev/null @@ -1 +0,0 @@ -Update macOS installer to ship with SQLite version 3.50.4. diff --git a/Misc/NEWS.d/next/macOS/2025-08-06-06-29-12.gh-issue-137450.JZypb7.rst b/Misc/NEWS.d/next/macOS/2025-08-06-06-29-12.gh-issue-137450.JZypb7.rst deleted file mode 100644 index 5efd74660c9..00000000000 --- a/Misc/NEWS.d/next/macOS/2025-08-06-06-29-12.gh-issue-137450.JZypb7.rst +++ /dev/null @@ -1,3 +0,0 @@ -macOS installer shell path management improvements: separate the installer -``Shell profile updater`` postinstall script from the -``Update Shell Profile.command`` to enable more robust error handling. diff --git a/Misc/NEWS.d/next/macOS/2025-10-13-23-46-12.gh-issue-132339.kAp603.rst b/Misc/NEWS.d/next/macOS/2025-10-13-23-46-12.gh-issue-132339.kAp603.rst deleted file mode 100644 index 4526c97fdc0..00000000000 --- a/Misc/NEWS.d/next/macOS/2025-10-13-23-46-12.gh-issue-132339.kAp603.rst +++ /dev/null @@ -1 +0,0 @@ -Update macOS installer version of OpenSSL to 3.5.4. diff --git a/Misc/NEWS.d/next/macOS/2025-10-14-00-08-16.gh-issue-124111.7-j-DQ.rst b/Misc/NEWS.d/next/macOS/2025-10-14-00-08-16.gh-issue-124111.7-j-DQ.rst deleted file mode 100644 index 847053463fd..00000000000 --- a/Misc/NEWS.d/next/macOS/2025-10-14-00-08-16.gh-issue-124111.7-j-DQ.rst +++ /dev/null @@ -1 +0,0 @@ -Update macOS installer to use Tcl/Tk 9.0.2. diff --git a/Misc/NEWS.d/next/macOS/2025-10-14-00-17-48.gh-issue-115119.470I1N.rst b/Misc/NEWS.d/next/macOS/2025-10-14-00-17-48.gh-issue-115119.470I1N.rst deleted file mode 100644 index d59da4b87b7..00000000000 --- a/Misc/NEWS.d/next/macOS/2025-10-14-00-17-48.gh-issue-115119.470I1N.rst +++ /dev/null @@ -1 +0,0 @@ -Update macOS installer to use libmpdecimal 4.0.1. diff --git a/README.rst b/README.rst index baea5e0978d..c6f4549f21b 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -This is Python version 3.15.0 alpha 0 +This is Python version 3.15.0 alpha 1 ===================================== .. image:: https://github.com/python/cpython/actions/workflows/build.yml/badge.svg?branch=main&event=push From 6ecf77dbdec7838e9ce2298cb8d16e8c2250da81 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 14 Oct 2025 14:02:02 +0300 Subject: [PATCH 153/373] gh-102431: Clarify constraints on operands of Decimal logical operations (GH-102836) Sync C/Python implementation of the decimal: logical_ops for contexts. --- Lib/_pydecimal.py | 20 ++- ...-03-21-10-59-40.gh-issue-102431.eUDnf4.rst | 2 + Modules/_decimal/_decimal.c | 116 +++++++++++++++--- Modules/_decimal/clinic/_decimal.c.h | 102 +++++++++++++-- 4 files changed, 211 insertions(+), 29 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-03-21-10-59-40.gh-issue-102431.eUDnf4.rst diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index 9b8e42a2342..97a629fe92c 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -3340,7 +3340,10 @@ def _fill_logical(self, context, opa, opb): return opa, opb def logical_and(self, other, context=None): - """Applies an 'and' operation between self and other's digits.""" + """Applies an 'and' operation between self and other's digits. + + Both self and other must be logical numbers. + """ if context is None: context = getcontext() @@ -3357,14 +3360,20 @@ def logical_and(self, other, context=None): return _dec_from_triple(0, result.lstrip('0') or '0', 0) def logical_invert(self, context=None): - """Invert all its digits.""" + """Invert all its digits. + + The self must be logical number. + """ if context is None: context = getcontext() return self.logical_xor(_dec_from_triple(0,'1'*context.prec,0), context) def logical_or(self, other, context=None): - """Applies an 'or' operation between self and other's digits.""" + """Applies an 'or' operation between self and other's digits. + + Both self and other must be logical numbers. + """ if context is None: context = getcontext() @@ -3381,7 +3390,10 @@ def logical_or(self, other, context=None): return _dec_from_triple(0, result.lstrip('0') or '0', 0) def logical_xor(self, other, context=None): - """Applies an 'xor' operation between self and other's digits.""" + """Applies an 'xor' operation between self and other's digits. + + Both self and other must be logical numbers. + """ if context is None: context = getcontext() diff --git a/Misc/NEWS.d/next/Library/2023-03-21-10-59-40.gh-issue-102431.eUDnf4.rst b/Misc/NEWS.d/next/Library/2023-03-21-10-59-40.gh-issue-102431.eUDnf4.rst new file mode 100644 index 00000000000..e82ddb6e101 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-03-21-10-59-40.gh-issue-102431.eUDnf4.rst @@ -0,0 +1,2 @@ +Clarify constraints for "logical" arguments in methods of +:class:`decimal.Context`. diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 04b6695f8af..4e2a4953126 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -5235,13 +5235,15 @@ _decimal_Decimal_copy_negate_impl(PyObject *self, PyTypeObject *cls) /*[clinic input] _decimal.Decimal.logical_invert = _decimal.Decimal.exp -Return the digit-wise inversion of the (logical) operand. +Invert all its digits. + +The self must be logical number. [clinic start generated code]*/ static PyObject * _decimal_Decimal_logical_invert_impl(PyObject *self, PyTypeObject *cls, PyObject *context) -/*[clinic end generated code: output=c626ed4b104a97b7 input=3531dac8b9548dad]*/ +/*[clinic end generated code: output=c626ed4b104a97b7 input=7158d5b525417955]*/ Dec_UnaryFuncVA(mpd_qinvert) /*[clinic input] @@ -5471,37 +5473,43 @@ _decimal_Decimal_same_quantum_impl(PyObject *self, PyTypeObject *cls, /*[clinic input] _decimal.Decimal.logical_and = _decimal.Decimal.compare -Return the digit-wise 'and' of the two (logical) operands. +Applies an 'and' operation between self and other's digits. + +Both self and other must be logical numbers. [clinic start generated code]*/ static PyObject * _decimal_Decimal_logical_and_impl(PyObject *self, PyTypeObject *cls, PyObject *other, PyObject *context) -/*[clinic end generated code: output=9a4cbb74c180b0bb input=2b319baee8970929]*/ +/*[clinic end generated code: output=9a4cbb74c180b0bb input=f22460f1285782d2]*/ Dec_BinaryFuncVA(mpd_qand) /*[clinic input] _decimal.Decimal.logical_or = _decimal.Decimal.compare -Return the digit-wise 'or' of the two (logical) operands. +Applies an 'or' operation between self and other's digits. + +Both self and other must be logical numbers. [clinic start generated code]*/ static PyObject * _decimal_Decimal_logical_or_impl(PyObject *self, PyTypeObject *cls, PyObject *other, PyObject *context) -/*[clinic end generated code: output=063c4de18dc41ecb input=75e0e1d4dd373b90]*/ +/*[clinic end generated code: output=063c4de18dc41ecb input=b5afa1e1fdebdfce]*/ Dec_BinaryFuncVA(mpd_qor) /*[clinic input] _decimal.Decimal.logical_xor = _decimal.Decimal.compare -Return the digit-wise 'xor' of the two (logical) operands. +Applies an 'xor' operation between self and other's digits. + +Both self and other must be logical numbers. [clinic start generated code]*/ static PyObject * _decimal_Decimal_logical_xor_impl(PyObject *self, PyTypeObject *cls, PyObject *other, PyObject *context) -/*[clinic end generated code: output=829b09cb49926ad7 input=a1ed8d6ac38c1c9e]*/ +/*[clinic end generated code: output=829b09cb49926ad7 input=84d722ada08a2da7]*/ Dec_BinaryFuncVA(mpd_qxor) /*[clinic input] @@ -7099,13 +7107,26 @@ DecCtx_UnaryFunc(mpd_qlogb) /*[clinic input] _decimal.Context.logical_invert = _decimal.Context.abs -Invert all digits of x. +Invert all the digits in the operand. + +The operand must be a logical number. + + >>> ExtendedContext.logical_invert(Decimal('0')) + Decimal('111111111') + >>> ExtendedContext.logical_invert(Decimal('1')) + Decimal('111111110') + >>> ExtendedContext.logical_invert(Decimal('111111111')) + Decimal('0') + >>> ExtendedContext.logical_invert(Decimal('101010101')) + Decimal('10101010') + >>> ExtendedContext.logical_invert(1101) + Decimal('111110010') [clinic start generated code]*/ static PyObject * _decimal_Context_logical_invert_impl(PyObject *context, PyTypeObject *cls, PyObject *x) -/*[clinic end generated code: output=97760277a958e2b0 input=1fa8dcc59c557fcc]*/ +/*[clinic end generated code: output=97760277a958e2b0 input=8e568f4c745ab596]*/ DecCtx_UnaryFunc(mpd_qinvert) /*[clinic input] @@ -7262,37 +7283,100 @@ _decimal_Context_copy_sign_impl(PyObject *context, PyTypeObject *cls, /*[clinic input] _decimal.Context.logical_and = _decimal.Context.add -Digit-wise and of x and y. +Applies the logical operation 'and' between each operand's digits. + +The operands must be both logical numbers. + + >>> ExtendedContext.logical_and(Decimal('0'), Decimal('0')) + Decimal('0') + >>> ExtendedContext.logical_and(Decimal('0'), Decimal('1')) + Decimal('0') + >>> ExtendedContext.logical_and(Decimal('1'), Decimal('0')) + Decimal('0') + >>> ExtendedContext.logical_and(Decimal('1'), Decimal('1')) + Decimal('1') + >>> ExtendedContext.logical_and(Decimal('1100'), Decimal('1010')) + Decimal('1000') + >>> ExtendedContext.logical_and(Decimal('1111'), Decimal('10')) + Decimal('10') + >>> ExtendedContext.logical_and(110, 1101) + Decimal('100') + >>> ExtendedContext.logical_and(Decimal(110), 1101) + Decimal('100') + >>> ExtendedContext.logical_and(110, Decimal(1101)) + Decimal('100') [clinic start generated code]*/ static PyObject * _decimal_Context_logical_and_impl(PyObject *context, PyTypeObject *cls, PyObject *x, PyObject *y) -/*[clinic end generated code: output=009dfa08ecaa2ac8 input=30ee33b5b365fd80]*/ +/*[clinic end generated code: output=009dfa08ecaa2ac8 input=bcb7d3d6ab7530de]*/ DecCtx_BinaryFunc(mpd_qand) /*[clinic input] _decimal.Context.logical_or = _decimal.Context.add -Digit-wise or of x and y. +Applies the logical operation 'or' between each operand's digits. + +The operands must be both logical numbers. + + >>> ExtendedContext.logical_or(Decimal('0'), Decimal('0')) + Decimal('0') + >>> ExtendedContext.logical_or(Decimal('0'), Decimal('1')) + Decimal('1') + >>> ExtendedContext.logical_or(Decimal('1'), Decimal('0')) + Decimal('1') + >>> ExtendedContext.logical_or(Decimal('1'), Decimal('1')) + Decimal('1') + >>> ExtendedContext.logical_or(Decimal('1100'), Decimal('1010')) + Decimal('1110') + >>> ExtendedContext.logical_or(Decimal('1110'), Decimal('10')) + Decimal('1110') + >>> ExtendedContext.logical_or(110, 1101) + Decimal('1111') + >>> ExtendedContext.logical_or(Decimal(110), 1101) + Decimal('1111') + >>> ExtendedContext.logical_or(110, Decimal(1101)) + Decimal('1111') [clinic start generated code]*/ static PyObject * _decimal_Context_logical_or_impl(PyObject *context, PyTypeObject *cls, PyObject *x, PyObject *y) -/*[clinic end generated code: output=eb38617e8d31bf12 input=3b1a6725d0262fb9]*/ +/*[clinic end generated code: output=eb38617e8d31bf12 input=47b45d296fb90846]*/ DecCtx_BinaryFunc(mpd_qor) /*[clinic input] _decimal.Context.logical_xor = _decimal.Context.add -Digit-wise xor of x and y. +Applies the logical operation 'xor' between each operand's digits. + +The operands must be both logical numbers. + + >>> ExtendedContext.logical_xor(Decimal('0'), Decimal('0')) + Decimal('0') + >>> ExtendedContext.logical_xor(Decimal('0'), Decimal('1')) + Decimal('1') + >>> ExtendedContext.logical_xor(Decimal('1'), Decimal('0')) + Decimal('1') + >>> ExtendedContext.logical_xor(Decimal('1'), Decimal('1')) + Decimal('0') + >>> ExtendedContext.logical_xor(Decimal('1100'), Decimal('1010')) + Decimal('110') + >>> ExtendedContext.logical_xor(Decimal('1111'), Decimal('10')) + Decimal('1101') + >>> ExtendedContext.logical_xor(110, 1101) + Decimal('1011') + >>> ExtendedContext.logical_xor(Decimal(110), 1101) + Decimal('1011') + >>> ExtendedContext.logical_xor(110, Decimal(1101)) + Decimal('1011') [clinic start generated code]*/ static PyObject * _decimal_Context_logical_xor_impl(PyObject *context, PyTypeObject *cls, PyObject *x, PyObject *y) -/*[clinic end generated code: output=23cd81fdcd865d5a input=5ebbbe8bb35da380]*/ +/*[clinic end generated code: output=23cd81fdcd865d5a input=fcaaf828c1d2d089]*/ DecCtx_BinaryFunc(mpd_qxor) /*[clinic input] diff --git a/Modules/_decimal/clinic/_decimal.c.h b/Modules/_decimal/clinic/_decimal.c.h index ccfbf63a7ce..b09200845d1 100644 --- a/Modules/_decimal/clinic/_decimal.c.h +++ b/Modules/_decimal/clinic/_decimal.c.h @@ -2744,7 +2744,9 @@ PyDoc_STRVAR(_decimal_Decimal_logical_invert__doc__, "logical_invert($self, /, context=None)\n" "--\n" "\n" -"Return the digit-wise inversion of the (logical) operand."); +"Invert all its digits.\n" +"\n" +"The self must be logical number."); #define _DECIMAL_DECIMAL_LOGICAL_INVERT_METHODDEF \ {"logical_invert", _PyCFunction_CAST(_decimal_Decimal_logical_invert), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_logical_invert__doc__}, @@ -3336,7 +3338,9 @@ PyDoc_STRVAR(_decimal_Decimal_logical_and__doc__, "logical_and($self, /, other, context=None)\n" "--\n" "\n" -"Return the digit-wise \'and\' of the two (logical) operands."); +"Applies an \'and\' operation between self and other\'s digits.\n" +"\n" +"Both self and other must be logical numbers."); #define _DECIMAL_DECIMAL_LOGICAL_AND_METHODDEF \ {"logical_and", _PyCFunction_CAST(_decimal_Decimal_logical_and), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_logical_and__doc__}, @@ -3402,7 +3406,9 @@ PyDoc_STRVAR(_decimal_Decimal_logical_or__doc__, "logical_or($self, /, other, context=None)\n" "--\n" "\n" -"Return the digit-wise \'or\' of the two (logical) operands."); +"Applies an \'or\' operation between self and other\'s digits.\n" +"\n" +"Both self and other must be logical numbers."); #define _DECIMAL_DECIMAL_LOGICAL_OR_METHODDEF \ {"logical_or", _PyCFunction_CAST(_decimal_Decimal_logical_or), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_logical_or__doc__}, @@ -3468,7 +3474,9 @@ PyDoc_STRVAR(_decimal_Decimal_logical_xor__doc__, "logical_xor($self, /, other, context=None)\n" "--\n" "\n" -"Return the digit-wise \'xor\' of the two (logical) operands."); +"Applies an \'xor\' operation between self and other\'s digits.\n" +"\n" +"Both self and other must be logical numbers."); #define _DECIMAL_DECIMAL_LOGICAL_XOR_METHODDEF \ {"logical_xor", _PyCFunction_CAST(_decimal_Decimal_logical_xor), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_logical_xor__doc__}, @@ -6235,7 +6243,20 @@ PyDoc_STRVAR(_decimal_Context_logical_invert__doc__, "logical_invert($self, x, /)\n" "--\n" "\n" -"Invert all digits of x."); +"Invert all the digits in the operand.\n" +"\n" +"The operand must be a logical number.\n" +"\n" +" >>> ExtendedContext.logical_invert(Decimal(\'0\'))\n" +" Decimal(\'111111111\')\n" +" >>> ExtendedContext.logical_invert(Decimal(\'1\'))\n" +" Decimal(\'111111110\')\n" +" >>> ExtendedContext.logical_invert(Decimal(\'111111111\'))\n" +" Decimal(\'0\')\n" +" >>> ExtendedContext.logical_invert(Decimal(\'101010101\'))\n" +" Decimal(\'10101010\')\n" +" >>> ExtendedContext.logical_invert(1101)\n" +" Decimal(\'111110010\')"); #define _DECIMAL_CONTEXT_LOGICAL_INVERT_METHODDEF \ {"logical_invert", _PyCFunction_CAST(_decimal_Context_logical_invert), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_logical_invert__doc__}, @@ -6556,7 +6577,28 @@ PyDoc_STRVAR(_decimal_Context_logical_and__doc__, "logical_and($self, x, y, /)\n" "--\n" "\n" -"Digit-wise and of x and y."); +"Applies the logical operation \'and\' between each operand\'s digits.\n" +"\n" +"The operands must be both logical numbers.\n" +"\n" +" >>> ExtendedContext.logical_and(Decimal(\'0\'), Decimal(\'0\'))\n" +" Decimal(\'0\')\n" +" >>> ExtendedContext.logical_and(Decimal(\'0\'), Decimal(\'1\'))\n" +" Decimal(\'0\')\n" +" >>> ExtendedContext.logical_and(Decimal(\'1\'), Decimal(\'0\'))\n" +" Decimal(\'0\')\n" +" >>> ExtendedContext.logical_and(Decimal(\'1\'), Decimal(\'1\'))\n" +" Decimal(\'1\')\n" +" >>> ExtendedContext.logical_and(Decimal(\'1100\'), Decimal(\'1010\'))\n" +" Decimal(\'1000\')\n" +" >>> ExtendedContext.logical_and(Decimal(\'1111\'), Decimal(\'10\'))\n" +" Decimal(\'10\')\n" +" >>> ExtendedContext.logical_and(110, 1101)\n" +" Decimal(\'100\')\n" +" >>> ExtendedContext.logical_and(Decimal(110), 1101)\n" +" Decimal(\'100\')\n" +" >>> ExtendedContext.logical_and(110, Decimal(1101))\n" +" Decimal(\'100\')"); #define _DECIMAL_CONTEXT_LOGICAL_AND_METHODDEF \ {"logical_and", _PyCFunction_CAST(_decimal_Context_logical_and), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_logical_and__doc__}, @@ -6603,7 +6645,28 @@ PyDoc_STRVAR(_decimal_Context_logical_or__doc__, "logical_or($self, x, y, /)\n" "--\n" "\n" -"Digit-wise or of x and y."); +"Applies the logical operation \'or\' between each operand\'s digits.\n" +"\n" +"The operands must be both logical numbers.\n" +"\n" +" >>> ExtendedContext.logical_or(Decimal(\'0\'), Decimal(\'0\'))\n" +" Decimal(\'0\')\n" +" >>> ExtendedContext.logical_or(Decimal(\'0\'), Decimal(\'1\'))\n" +" Decimal(\'1\')\n" +" >>> ExtendedContext.logical_or(Decimal(\'1\'), Decimal(\'0\'))\n" +" Decimal(\'1\')\n" +" >>> ExtendedContext.logical_or(Decimal(\'1\'), Decimal(\'1\'))\n" +" Decimal(\'1\')\n" +" >>> ExtendedContext.logical_or(Decimal(\'1100\'), Decimal(\'1010\'))\n" +" Decimal(\'1110\')\n" +" >>> ExtendedContext.logical_or(Decimal(\'1110\'), Decimal(\'10\'))\n" +" Decimal(\'1110\')\n" +" >>> ExtendedContext.logical_or(110, 1101)\n" +" Decimal(\'1111\')\n" +" >>> ExtendedContext.logical_or(Decimal(110), 1101)\n" +" Decimal(\'1111\')\n" +" >>> ExtendedContext.logical_or(110, Decimal(1101))\n" +" Decimal(\'1111\')"); #define _DECIMAL_CONTEXT_LOGICAL_OR_METHODDEF \ {"logical_or", _PyCFunction_CAST(_decimal_Context_logical_or), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_logical_or__doc__}, @@ -6650,7 +6713,28 @@ PyDoc_STRVAR(_decimal_Context_logical_xor__doc__, "logical_xor($self, x, y, /)\n" "--\n" "\n" -"Digit-wise xor of x and y."); +"Applies the logical operation \'xor\' between each operand\'s digits.\n" +"\n" +"The operands must be both logical numbers.\n" +"\n" +" >>> ExtendedContext.logical_xor(Decimal(\'0\'), Decimal(\'0\'))\n" +" Decimal(\'0\')\n" +" >>> ExtendedContext.logical_xor(Decimal(\'0\'), Decimal(\'1\'))\n" +" Decimal(\'1\')\n" +" >>> ExtendedContext.logical_xor(Decimal(\'1\'), Decimal(\'0\'))\n" +" Decimal(\'1\')\n" +" >>> ExtendedContext.logical_xor(Decimal(\'1\'), Decimal(\'1\'))\n" +" Decimal(\'0\')\n" +" >>> ExtendedContext.logical_xor(Decimal(\'1100\'), Decimal(\'1010\'))\n" +" Decimal(\'110\')\n" +" >>> ExtendedContext.logical_xor(Decimal(\'1111\'), Decimal(\'10\'))\n" +" Decimal(\'1101\')\n" +" >>> ExtendedContext.logical_xor(110, 1101)\n" +" Decimal(\'1011\')\n" +" >>> ExtendedContext.logical_xor(Decimal(110), 1101)\n" +" Decimal(\'1011\')\n" +" >>> ExtendedContext.logical_xor(110, Decimal(1101))\n" +" Decimal(\'1011\')"); #define _DECIMAL_CONTEXT_LOGICAL_XOR_METHODDEF \ {"logical_xor", _PyCFunction_CAST(_decimal_Context_logical_xor), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_logical_xor__doc__}, @@ -6896,4 +6980,4 @@ exit: #ifndef _DECIMAL_CONTEXT_APPLY_METHODDEF #define _DECIMAL_CONTEXT_APPLY_METHODDEF #endif /* !defined(_DECIMAL_CONTEXT_APPLY_METHODDEF) */ -/*[clinic end generated code: output=e938de3a355a353a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b288181c82fdc9f1 input=a9049054013a1b77]*/ From ded59f7e8e93274488584574ff2336a98bc4eff6 Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Tue, 14 Oct 2025 17:45:39 +0500 Subject: [PATCH 154/373] GH-140058: Clear key and value if `PyTuple_New` fails in `dictiter_iternextitem` (#140059) --- Objects/dictobject.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index ddf9bde63f3..b95a4a8dc5e 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -5719,8 +5719,11 @@ dictiter_iternextitem(PyObject *self) } else { result = PyTuple_New(2); - if (result == NULL) + if (result == NULL) { + Py_DECREF(key); + Py_DECREF(value); return NULL; + } PyTuple_SET_ITEM(result, 0, key); PyTuple_SET_ITEM(result, 1, value); } From 87eadce3e0309d80a95e85d70a00028b5dca9907 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Tue, 14 Oct 2025 14:55:00 +0100 Subject: [PATCH 155/373] gh-101828: Fix `jisx0213` codecs removing null characters (gh-139340) --- Lib/test/multibytecodec_support.py | 17 +++++++++++++++++ ...25-09-25-20-16-10.gh-issue-101828.yTxJlJ.rst | 3 +++ Modules/cjkcodecs/_codecs_iso2022.c | 11 +++++++---- Modules/cjkcodecs/_codecs_jp.c | 9 +++++++-- 4 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-09-25-20-16-10.gh-issue-101828.yTxJlJ.rst diff --git a/Lib/test/multibytecodec_support.py b/Lib/test/multibytecodec_support.py index dbf0cc428e3..6b4c57d0b4b 100644 --- a/Lib/test/multibytecodec_support.py +++ b/Lib/test/multibytecodec_support.py @@ -282,6 +282,23 @@ def test_incrementalencoder_del_segfault(self): with self.assertRaises(AttributeError): del e.errors + def test_null_terminator(self): + # see gh-101828 + text = "フルーツ" + try: + text.encode(self.encoding) + except UnicodeEncodeError: + text = "Python is cool" + encode_w_null = (text + "\0").encode(self.encoding) + encode_plus_null = text.encode(self.encoding) + "\0".encode(self.encoding) + self.assertTrue(encode_w_null.endswith(b'\x00')) + self.assertEqual(encode_w_null, encode_plus_null) + + encode_w_null_2 = (text + "\0" + text + "\0").encode(self.encoding) + encode_plus_null_2 = encode_plus_null + encode_plus_null + self.assertEqual(encode_w_null_2.count(b'\x00'), 2) + self.assertEqual(encode_w_null_2, encode_plus_null_2) + class TestBase_Mapping(unittest.TestCase): pass_enctest = [] diff --git a/Misc/NEWS.d/next/Library/2025-09-25-20-16-10.gh-issue-101828.yTxJlJ.rst b/Misc/NEWS.d/next/Library/2025-09-25-20-16-10.gh-issue-101828.yTxJlJ.rst new file mode 100644 index 00000000000..1d100180c07 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-25-20-16-10.gh-issue-101828.yTxJlJ.rst @@ -0,0 +1,3 @@ +Fix ``'shift_jisx0213'``, ``'shift_jis_2004'``, ``'euc_jisx0213'`` and +``'euc_jis_2004'`` codecs truncating null chars +as they were treated as part of multi-character sequences. diff --git a/Modules/cjkcodecs/_codecs_iso2022.c b/Modules/cjkcodecs/_codecs_iso2022.c index ef6faeb7127..b1984df2695 100644 --- a/Modules/cjkcodecs/_codecs_iso2022.c +++ b/Modules/cjkcodecs/_codecs_iso2022.c @@ -802,10 +802,13 @@ jisx0213_encoder(const MultibyteCodec *codec, const Py_UCS4 *data, return coded; case 2: /* second character of unicode pair */ - coded = find_pairencmap((ucs2_t)data[0], (ucs2_t)data[1], - jisx0213_pair_encmap, JISX0213_ENCPAIRS); - if (coded != DBCINV) - return coded; + if (data[1] != 0) { /* Don't consume null char as part of pair */ + coded = find_pairencmap((ucs2_t)data[0], (ucs2_t)data[1], + jisx0213_pair_encmap, JISX0213_ENCPAIRS); + if (coded != DBCINV) { + return coded; + } + } _Py_FALLTHROUGH; case -1: /* flush unterminated */ diff --git a/Modules/cjkcodecs/_codecs_jp.c b/Modules/cjkcodecs/_codecs_jp.c index f7127487aa5..cd77888d551 100644 --- a/Modules/cjkcodecs/_codecs_jp.c +++ b/Modules/cjkcodecs/_codecs_jp.c @@ -192,8 +192,11 @@ ENCODER(euc_jis_2004) JISX0213_ENCPAIRS); if (code == DBCINV) return 1; - } else + } + else if (c2 != 0) { + /* Don't consume null char as part of pair */ insize = 2; + } } } } @@ -611,8 +614,10 @@ ENCODER(shift_jis_2004) if (code == DBCINV) return 1; } - else + else if (ch2 != 0) { + /* Don't consume null char as part of pair */ insize = 2; + } } } } From 07e617ecc274f21607320e68a7e8b09ee91f968b Mon Sep 17 00:00:00 2001 From: Adorilson Bezerra Date: Tue, 14 Oct 2025 11:19:27 -0300 Subject: [PATCH 156/373] gh-106318: Add examples for str.istitle() (#140046) --- Doc/library/stdtypes.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 21fe35edc1b..2c5b721093d 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -2157,6 +2157,19 @@ expression support in the :mod:`re` module). character, for example uppercase characters may only follow uncased characters and lowercase characters only cased ones. Return ``False`` otherwise. + For example: + + .. doctest:: + + >>> 'Spam, Spam, Spam'.istitle() + True + >>> 'spam, spam, spam'.istitle() + False + >>> 'SPAM, SPAM, SPAM'.istitle() + False + + See also :meth:`title`. + .. method:: str.isupper() @@ -2534,6 +2547,8 @@ expression support in the :mod:`re` module). >>> titlecase("they're bill's friends.") "They're Bill's Friends." + See also :meth:`istitle`. + .. method:: str.translate(table, /) From 983c8db61e9ce1e7eaa9deeb32706e2e46fe8b32 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 14 Oct 2025 17:36:01 +0300 Subject: [PATCH 157/373] Post 3.15.0a1 --- Include/patchlevel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 640da6aa6fd..e3996ee8679 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -27,7 +27,7 @@ #define PY_RELEASE_SERIAL 1 /* Version as a string */ -#define PY_VERSION "3.15.0a1" +#define PY_VERSION "3.15.0a1+" /*--end constants--*/ From 59547a251f7069dc6e08cb6082dd21872671e381 Mon Sep 17 00:00:00 2001 From: Shamil Date: Tue, 14 Oct 2025 17:42:17 +0300 Subject: [PATCH 158/373] gh-140067: Fix memory leak in sub-interpreter creation (#140111) Fix memory leak in sub-interpreter creation caused by overwriting of the previously used `_malloced` field. Now the pointer is stored in the first word of the memory block to avoid it being overwritten accidentally. Co-authored-by: Kumar Aditya --- Include/internal/pycore_interp_structs.h | 6 ------ ...2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst | 1 + Python/pystate.c | 15 +++++++++------ 3 files changed, 10 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst diff --git a/Include/internal/pycore_interp_structs.h b/Include/internal/pycore_interp_structs.h index 2124e76514f..badc97808c6 100644 --- a/Include/internal/pycore_interp_structs.h +++ b/Include/internal/pycore_interp_structs.h @@ -769,12 +769,6 @@ struct _is { * and should be placed at the beginning. */ struct _ceval_state ceval; - /* This structure is carefully allocated so that it's correctly aligned - * to avoid undefined behaviors during LOAD and STORE. The '_malloced' - * field stores the allocated pointer address that will later be freed. - */ - void *_malloced; - PyInterpreterState *next; int64_t id; diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst new file mode 100644 index 00000000000..3c5a828101d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst @@ -0,0 +1 @@ +Fix memory leak in sub-interpreter creation. diff --git a/Python/pystate.c b/Python/pystate.c index dbed609f29a..bf6e4e56e6d 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -457,16 +457,19 @@ _PyInterpreterState_Enable(_PyRuntimeState *runtime) static PyInterpreterState * alloc_interpreter(void) { + // Aligned allocation for PyInterpreterState. + // the first word of the memory block is used to store + // the original pointer to be used later to free the memory. size_t alignment = _Alignof(PyInterpreterState); - size_t allocsize = sizeof(PyInterpreterState) + alignment - 1; + size_t allocsize = sizeof(PyInterpreterState) + sizeof(void *) + alignment - 1; void *mem = PyMem_RawCalloc(1, allocsize); if (mem == NULL) { return NULL; } - PyInterpreterState *interp = _Py_ALIGN_UP(mem, alignment); - assert(_Py_IS_ALIGNED(interp, alignment)); - interp->_malloced = mem; - return interp; + void *ptr = _Py_ALIGN_UP((char *)mem + sizeof(void *), alignment); + ((void **)ptr)[-1] = mem; + assert(_Py_IS_ALIGNED(ptr, alignment)); + return ptr; } static void @@ -481,7 +484,7 @@ free_interpreter(PyInterpreterState *interp) interp->obmalloc = NULL; } assert(_Py_IS_ALIGNED(interp, _Alignof(PyInterpreterState))); - PyMem_RawFree(interp->_malloced); + PyMem_RawFree(((void **)interp)[-1]); } } From 279db6bede30be3a1b86803585eb4404d27800da Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 14 Oct 2025 17:48:09 +0300 Subject: [PATCH 159/373] gh-139640: Fix swallowing syntax warnings in different modules (GH-139755) Revert GH-131993. Fix swallowing some syntax warnings in different modules if they accidentally have the same message and are emitted from the same line. --- Include/cpython/warnings.h | 6 -- Lib/test/test_compile.py | 66 ++++++++++++++----- Lib/test/test_pyrepl/test_interact.py | 26 -------- ...-10-06-10-03-37.gh-issue-139640.gY5oTb.rst | 2 + Python/_warnings.c | 22 ------- Python/compile.c | 10 +++ Python/errors.c | 4 +- 7 files changed, 62 insertions(+), 74 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-10-03-37.gh-issue-139640.gY5oTb.rst diff --git a/Include/cpython/warnings.h b/Include/cpython/warnings.h index 8731fd2e96b..4e3eb88e8ff 100644 --- a/Include/cpython/warnings.h +++ b/Include/cpython/warnings.h @@ -18,9 +18,3 @@ PyAPI_FUNC(int) PyErr_WarnExplicitFormat( // DEPRECATED: Use PyErr_WarnEx() instead. #define PyErr_Warn(category, msg) PyErr_WarnEx((category), (msg), 1) - -int _PyErr_WarnExplicitObjectWithContext( - PyObject *category, - PyObject *message, - PyObject *filename, - int lineno); diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 1660dabe681..bc8ef93cb8f 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -1679,22 +1679,21 @@ class WeirdDict(dict): self.assertRaises(NameError, ns['foo']) def test_compile_warnings(self): - # See gh-131927 - # Compile warnings originating from the same file and - # line are now only emitted once. + # Each invocation of compile() emits compiler warnings, even if they + # have the same message and line number. + source = textwrap.dedent(r""" + # tokenizer + 1or 0 # line 3 + # code generator + 1 is 1 # line 5 + """) with warnings.catch_warnings(record=True) as caught: warnings.simplefilter("default") - compile('1 is 1', '', 'eval') - compile('1 is 1', '', 'eval') + for i in range(2): + # Even if compile() is at the same line. + compile(source, '', 'exec') - self.assertEqual(len(caught), 1) - - with warnings.catch_warnings(record=True) as caught: - warnings.simplefilter("always") - compile('1 is 1', '', 'eval') - compile('1 is 1', '', 'eval') - - self.assertEqual(len(caught), 2) + self.assertEqual([wm.lineno for wm in caught], [3, 5] * 2) def test_compile_warning_in_finally(self): # Ensure that warnings inside finally blocks are @@ -1705,16 +1704,47 @@ def test_compile_warning_in_finally(self): try: pass finally: - 1 is 1 + 1 is 1 # line 5 + try: + pass + finally: # nested + 1 is 1 # line 9 """) with warnings.catch_warnings(record=True) as caught: - warnings.simplefilter("default") + warnings.simplefilter("always") compile(source, '', 'exec') - self.assertEqual(len(caught), 1) - self.assertEqual(caught[0].category, SyntaxWarning) - self.assertIn("\"is\" with 'int' literal", str(caught[0].message)) + self.assertEqual(sorted(wm.lineno for wm in caught), [5, 9]) + for wm in caught: + self.assertEqual(wm.category, SyntaxWarning) + self.assertIn("\"is\" with 'int' literal", str(wm.message)) + + # Other code path is used for "try" with "except*". + source = textwrap.dedent(""" + try: + pass + except *Exception: + pass + finally: + 1 is 1 # line 7 + try: + pass + except *Exception: + pass + finally: # nested + 1 is 1 # line 13 + """) + + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + compile(source, '', 'exec') + + self.assertEqual(sorted(wm.lineno for wm in caught), [7, 13]) + for wm in caught: + self.assertEqual(wm.category, SyntaxWarning) + self.assertIn("\"is\" with 'int' literal", str(wm.message)) + class TestBooleanExpression(unittest.TestCase): class Value: diff --git a/Lib/test/test_pyrepl/test_interact.py b/Lib/test/test_pyrepl/test_interact.py index 8c0eeab6dca..1a3146da8ea 100644 --- a/Lib/test/test_pyrepl/test_interact.py +++ b/Lib/test/test_pyrepl/test_interact.py @@ -1,7 +1,6 @@ import contextlib import io import unittest -import warnings from unittest.mock import patch from textwrap import dedent @@ -274,28 +273,3 @@ def test_incomplete_statement(self): code = "if foo:" console = InteractiveColoredConsole(namespace, filename="") self.assertTrue(_more_lines(console, code)) - - -class TestWarnings(unittest.TestCase): - def test_pep_765_warning(self): - """ - Test that a SyntaxWarning emitted from the - AST optimizer is only shown once in the REPL. - """ - # gh-131927 - console = InteractiveColoredConsole() - code = dedent("""\ - def f(): - try: - return 1 - finally: - return 2 - """) - - with warnings.catch_warnings(record=True) as caught: - warnings.simplefilter("default") - console.runsource(code) - - count = sum("'return' in a 'finally' block" in str(w.message) - for w in caught) - self.assertEqual(count, 1) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-10-03-37.gh-issue-139640.gY5oTb.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-10-03-37.gh-issue-139640.gY5oTb.rst new file mode 100644 index 00000000000..f1344aedb58 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-10-03-37.gh-issue-139640.gY5oTb.rst @@ -0,0 +1,2 @@ +Fix swallowing some syntax warnings in different modules if they +accidentally have the same message and are emitted from the same line. diff --git a/Python/_warnings.c b/Python/_warnings.c index 243a5e88e9d..9989b623dbc 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -1473,28 +1473,6 @@ PyErr_WarnExplicitObject(PyObject *category, PyObject *message, return 0; } -/* Like PyErr_WarnExplicitObject, but automatically sets up context */ -int -_PyErr_WarnExplicitObjectWithContext(PyObject *category, PyObject *message, - PyObject *filename, int lineno) -{ - PyObject *unused_filename, *module, *registry; - int unused_lineno; - int stack_level = 1; - - if (!setup_context(stack_level, NULL, &unused_filename, &unused_lineno, - &module, ®istry)) { - return -1; - } - - int rc = PyErr_WarnExplicitObject(category, message, filename, lineno, - module, registry); - Py_DECREF(unused_filename); - Py_DECREF(registry); - Py_DECREF(module); - return rc; -} - int PyErr_WarnExplicit(PyObject *category, const char *text, const char *filename_str, int lineno, diff --git a/Python/compile.c b/Python/compile.c index c04391e682f..8070d3f0376 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -103,6 +103,7 @@ typedef struct _PyCompiler { bool c_save_nested_seqs; /* if true, construct recursive instruction sequences * (including instructions for nested code objects) */ + int c_disable_warning; } compiler; static int @@ -765,6 +766,9 @@ _PyCompile_PushFBlock(compiler *c, location loc, f->fb_loc = loc; f->fb_exit = exit; f->fb_datum = datum; + if (t == COMPILE_FBLOCK_FINALLY_END) { + c->c_disable_warning++; + } return SUCCESS; } @@ -776,6 +780,9 @@ _PyCompile_PopFBlock(compiler *c, fblocktype t, jump_target_label block_label) u->u_nfblocks--; assert(u->u_fblock[u->u_nfblocks].fb_type == t); assert(SAME_JUMP_TARGET_LABEL(u->u_fblock[u->u_nfblocks].fb_block, block_label)); + if (t == COMPILE_FBLOCK_FINALLY_END) { + c->c_disable_warning--; + } } fblockinfo * @@ -1203,6 +1210,9 @@ _PyCompile_Error(compiler *c, location loc, const char *format, ...) int _PyCompile_Warn(compiler *c, location loc, const char *format, ...) { + if (c->c_disable_warning) { + return 0; + } va_list vargs; va_start(vargs, format); PyObject *msg = PyUnicode_FromFormatV(format, vargs); diff --git a/Python/errors.c b/Python/errors.c index 2688396004e..9fe95cec0ab 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -1962,8 +1962,8 @@ int _PyErr_EmitSyntaxWarning(PyObject *msg, PyObject *filename, int lineno, int col_offset, int end_lineno, int end_col_offset) { - if (_PyErr_WarnExplicitObjectWithContext(PyExc_SyntaxWarning, msg, - filename, lineno) < 0) + if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg, + filename, lineno, NULL, NULL) < 0) { if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) { /* Replace the SyntaxWarning exception with a SyntaxError From 3490a99046078e4f9df7ac7570f62a0181bb3b89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurycy=20Paw=C5=82owski-Wiero=C5=84ski?= <5383+maurycy@users.noreply.github.com> Date: Tue, 14 Oct 2025 17:18:20 +0200 Subject: [PATCH 160/373] Correct a simple NULL-check in `optimizer.c`'s `uop_item()` (GH-140069) --- Python/optimizer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index 83b0b1a5deb..6ad91247448 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -362,7 +362,7 @@ uop_item(PyObject *op, Py_ssize_t index) return NULL; } PyObject *target = PyLong_FromUnsignedLong(self->trace[index].target); - if (oparg == NULL) { + if (target == NULL) { Py_DECREF(oparg); Py_DECREF(oname); return NULL; From 404425575c68bef9d2f042710fc713134d04c23f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 14 Oct 2025 18:56:19 +0300 Subject: [PATCH 161/373] Add mention of other fixed bug in the gh-139640 NEWS entry (GH-140122) --- .../2025-10-06-10-03-37.gh-issue-139640.gY5oTb.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-10-03-37.gh-issue-139640.gY5oTb.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-10-03-37.gh-issue-139640.gY5oTb.rst index f1344aedb58..396e40f0e13 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-10-03-37.gh-issue-139640.gY5oTb.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-10-03-37.gh-issue-139640.gY5oTb.rst @@ -1,2 +1,3 @@ Fix swallowing some syntax warnings in different modules if they accidentally have the same message and are emitted from the same line. +Fix duplicated warnings in the ``finally`` block. From f262297d525e87906c5e4ab28e80284189641c9e Mon Sep 17 00:00:00 2001 From: Emma Smith Date: Tue, 14 Oct 2025 10:03:55 -0700 Subject: [PATCH 162/373] gh-139877: Use PyBytesWriter in pycore_blocks_output_buffer.h (#139976) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, the _BlocksOutputBuffer code creates a list of bytes objects to handle the output data from compression libraries. This ends up being slow due to the output buffer code needing to copy each bytes element of the list into the final bytes object buffer at the end of compression. The new PyBytesWriter API introduced in PEP 782 is an ergonomic and fast method of writing data into a buffer that will later turn into a bytes object. Benchmarks show that using the PyBytesWriter API is 10-30% faster for decompression across a variety of settings. The performance gains are greatest when the decompressor is very performant, such as for Zstandard (and likely zlib-ng). Otherwise the decompressor can bottleneck decompression and the gains are more modest, but still sizable (e.g. 10% faster for zlib)! Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- .../internal/pycore_blocks_output_buffer.h | 124 +++++------------- Modules/_bz2module.c | 4 +- Modules/_lzmamodule.c | 4 +- Modules/_zstd/buffer.h | 8 +- Modules/_zstd/compressor.c | 4 +- Modules/_zstd/decompressor.c | 2 +- Modules/zlibmodule.c | 12 +- 7 files changed, 49 insertions(+), 109 deletions(-) diff --git a/Include/internal/pycore_blocks_output_buffer.h b/Include/internal/pycore_blocks_output_buffer.h index 573e10359b7..016e7a18665 100644 --- a/Include/internal/pycore_blocks_output_buffer.h +++ b/Include/internal/pycore_blocks_output_buffer.h @@ -45,12 +45,14 @@ extern "C" { #endif typedef struct { - // List of bytes objects - PyObject *list; + // Bytes writer managing output buffer + PyBytesWriter *writer; // Number of whole allocated size Py_ssize_t allocated; - // Max length of the buffer, negative number means unlimited length. + // Max length of the buffer, negative number means unlimited length Py_ssize_t max_length; + // Number of blocks of bytes. Used to calculate next allocation size + size_t num_blocks; } _BlocksOutputBuffer; static const char unable_allocate_msg[] = "Unable to allocate output buffer."; @@ -107,11 +109,10 @@ _BlocksOutputBuffer_InitAndGrow(_BlocksOutputBuffer *buffer, const Py_ssize_t max_length, void **next_out) { - PyObject *b; Py_ssize_t block_size; - // ensure .list was set to NULL - assert(buffer->list == NULL); + // ensure .writer was set to NULL + assert(buffer->writer == NULL); // get block size if (0 <= max_length && max_length < BUFFER_BLOCK_SIZE[0]) { @@ -120,25 +121,17 @@ _BlocksOutputBuffer_InitAndGrow(_BlocksOutputBuffer *buffer, block_size = BUFFER_BLOCK_SIZE[0]; } - // the first block - b = PyBytes_FromStringAndSize(NULL, block_size); - if (b == NULL) { + buffer->writer = PyBytesWriter_Create(block_size); + if (buffer->writer == NULL) { return -1; } - // create the list - buffer->list = PyList_New(1); - if (buffer->list == NULL) { - Py_DECREF(b); - return -1; - } - PyList_SET_ITEM(buffer->list, 0, b); - // set variables buffer->allocated = block_size; buffer->max_length = max_length; + buffer->num_blocks = 1; - *next_out = PyBytes_AS_STRING(b); + *next_out = PyBytesWriter_GetData(buffer->writer); return block_size; } @@ -155,31 +148,21 @@ _BlocksOutputBuffer_InitWithSize(_BlocksOutputBuffer *buffer, const Py_ssize_t init_size, void **next_out) { - PyObject *b; - // ensure .list was set to NULL - assert(buffer->list == NULL); + // ensure .writer was set to NULL + assert(buffer->writer == NULL); - // the first block - b = PyBytes_FromStringAndSize(NULL, init_size); - if (b == NULL) { - PyErr_SetString(PyExc_MemoryError, unable_allocate_msg); + buffer->writer = PyBytesWriter_Create(init_size); + if (buffer->writer == NULL) { return -1; } - // create the list - buffer->list = PyList_New(1); - if (buffer->list == NULL) { - Py_DECREF(b); - return -1; - } - PyList_SET_ITEM(buffer->list, 0, b); - // set variables buffer->allocated = init_size; buffer->max_length = -1; + buffer->num_blocks = 1; - *next_out = PyBytes_AS_STRING(b); + *next_out = PyBytesWriter_GetData(buffer->writer); return init_size; } @@ -193,8 +176,6 @@ _BlocksOutputBuffer_Grow(_BlocksOutputBuffer *buffer, void **next_out, const Py_ssize_t avail_out) { - PyObject *b; - const Py_ssize_t list_len = Py_SIZE(buffer->list); Py_ssize_t block_size; // ensure no gaps in the data @@ -205,11 +186,10 @@ _BlocksOutputBuffer_Grow(_BlocksOutputBuffer *buffer, } // get block size - if (list_len < (Py_ssize_t) Py_ARRAY_LENGTH(BUFFER_BLOCK_SIZE)) { - block_size = BUFFER_BLOCK_SIZE[list_len]; - } else { - block_size = BUFFER_BLOCK_SIZE[Py_ARRAY_LENGTH(BUFFER_BLOCK_SIZE) - 1]; - } + size_t maxblock = Py_ARRAY_LENGTH(BUFFER_BLOCK_SIZE); + assert(maxblock >= 1); + size_t block_index = Py_MIN(buffer->num_blocks, maxblock - 1); + block_size = BUFFER_BLOCK_SIZE[block_index]; // check max_length if (buffer->max_length >= 0) { @@ -229,22 +209,19 @@ _BlocksOutputBuffer_Grow(_BlocksOutputBuffer *buffer, return -1; } - // create the block - b = PyBytes_FromStringAndSize(NULL, block_size); - if (b == NULL) { + if (PyBytesWriter_Grow(buffer->writer, block_size)) { PyErr_SetString(PyExc_MemoryError, unable_allocate_msg); return -1; } - if (PyList_Append(buffer->list, b) < 0) { - Py_DECREF(b); - return -1; - } - Py_DECREF(b); + + Py_ssize_t current_size = buffer->allocated; // set variables buffer->allocated += block_size; + buffer->num_blocks += 1; - *next_out = PyBytes_AS_STRING(b); + char *data = PyBytesWriter_GetData(buffer->writer); + *next_out = data + current_size; return block_size; } @@ -265,54 +242,17 @@ static inline PyObject * _BlocksOutputBuffer_Finish(_BlocksOutputBuffer *buffer, const Py_ssize_t avail_out) { - PyObject *result, *block; - const Py_ssize_t list_len = Py_SIZE(buffer->list); - - // fast path for single block - if ((list_len == 1 && avail_out == 0) || - (list_len == 2 && Py_SIZE(PyList_GET_ITEM(buffer->list, 1)) == avail_out)) - { - block = PyList_GET_ITEM(buffer->list, 0); - Py_INCREF(block); - - Py_CLEAR(buffer->list); - return block; - } - - // final bytes object - result = PyBytes_FromStringAndSize(NULL, buffer->allocated - avail_out); - if (result == NULL) { - PyErr_SetString(PyExc_MemoryError, unable_allocate_msg); - return NULL; - } - - // memory copy - if (list_len > 0) { - char *posi = PyBytes_AS_STRING(result); - - // blocks except the last one - Py_ssize_t i = 0; - for (; i < list_len-1; i++) { - block = PyList_GET_ITEM(buffer->list, i); - memcpy(posi, PyBytes_AS_STRING(block), Py_SIZE(block)); - posi += Py_SIZE(block); - } - // the last block - block = PyList_GET_ITEM(buffer->list, i); - memcpy(posi, PyBytes_AS_STRING(block), Py_SIZE(block) - avail_out); - } else { - assert(Py_SIZE(result) == 0); - } - - Py_CLEAR(buffer->list); - return result; + assert(buffer->writer != NULL); + return PyBytesWriter_FinishWithSize(buffer->writer, + buffer->allocated - avail_out); } /* Clean up the buffer when an error occurred. */ static inline void _BlocksOutputBuffer_OnError(_BlocksOutputBuffer *buffer) { - Py_CLEAR(buffer->list); + PyBytesWriter_Discard(buffer->writer); + buffer->writer = NULL; } #ifdef __cplusplus diff --git a/Modules/_bz2module.c b/Modules/_bz2module.c index 2e4cc43a2c3..9721b493a19 100644 --- a/Modules/_bz2module.c +++ b/Modules/_bz2module.c @@ -190,7 +190,7 @@ static PyObject * compress(BZ2Compressor *c, char *data, size_t len, int action) { PyObject *result; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; if (OutputBuffer_InitAndGrow(&buffer, -1, &c->bzs.next_out, &c->bzs.avail_out) < 0) { goto error; @@ -429,7 +429,7 @@ decompress_buf(BZ2Decompressor *d, Py_ssize_t max_length) compare against max_length and PyBytes_GET_SIZE we declare it as signed */ PyObject *result; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; bz_stream *bzs = &d->bzs; if (OutputBuffer_InitAndGrow(&buffer, max_length, &bzs->next_out, &bzs->avail_out) < 0) { diff --git a/Modules/_lzmamodule.c b/Modules/_lzmamodule.c index 3e8e37096ba..6fc072f6d0a 100644 --- a/Modules/_lzmamodule.c +++ b/Modules/_lzmamodule.c @@ -554,7 +554,7 @@ static PyObject * compress(Compressor *c, uint8_t *data, size_t len, lzma_action action) { PyObject *result; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; _lzma_state *state = PyType_GetModuleState(Py_TYPE(c)); assert(state != NULL); @@ -940,7 +940,7 @@ decompress_buf(Decompressor *d, Py_ssize_t max_length) { PyObject *result; lzma_stream *lzs = &d->lzs; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; _lzma_state *state = PyType_GetModuleState(Py_TYPE(d)); assert(state != NULL); diff --git a/Modules/_zstd/buffer.h b/Modules/_zstd/buffer.h index 0ac7bcb4ddc..807c72c80dd 100644 --- a/Modules/_zstd/buffer.h +++ b/Modules/_zstd/buffer.h @@ -16,8 +16,8 @@ static inline int _OutputBuffer_InitAndGrow(_BlocksOutputBuffer *buffer, ZSTD_outBuffer *ob, Py_ssize_t max_length) { - /* Ensure .list was set to NULL */ - assert(buffer->list == NULL); + /* Ensure .writer was set to NULL */ + assert(buffer->writer == NULL); Py_ssize_t res = _BlocksOutputBuffer_InitAndGrow(buffer, max_length, &ob->dst); @@ -39,8 +39,8 @@ _OutputBuffer_InitWithSize(_BlocksOutputBuffer *buffer, ZSTD_outBuffer *ob, { Py_ssize_t block_size; - /* Ensure .list was set to NULL */ - assert(buffer->list == NULL); + /* Ensure .writer was set to NULL */ + assert(buffer->writer == NULL); /* Get block size */ if (0 <= max_length && max_length < init_size) { diff --git a/Modules/_zstd/compressor.c b/Modules/_zstd/compressor.c index 029c07113d4..f90bc9c5ab5 100644 --- a/Modules/_zstd/compressor.c +++ b/Modules/_zstd/compressor.c @@ -446,7 +446,7 @@ compress_lock_held(ZstdCompressor *self, Py_buffer *data, assert(PyMutex_IsLocked(&self->lock)); ZSTD_inBuffer in; ZSTD_outBuffer out; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; size_t zstd_ret; PyObject *ret; @@ -527,7 +527,7 @@ compress_mt_continue_lock_held(ZstdCompressor *self, Py_buffer *data) assert(PyMutex_IsLocked(&self->lock)); ZSTD_inBuffer in; ZSTD_outBuffer out; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; size_t zstd_ret; PyObject *ret; diff --git a/Modules/_zstd/decompressor.c b/Modules/_zstd/decompressor.c index 6592cad6690..13071b7a2ba 100644 --- a/Modules/_zstd/decompressor.c +++ b/Modules/_zstd/decompressor.c @@ -216,7 +216,7 @@ decompress_lock_held(ZstdDecompressor *self, ZSTD_inBuffer *in, { size_t zstd_ret; ZSTD_outBuffer out; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; PyObject *ret; /* Initialize the output buffer */ diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c index f1312e687da..36c933bf618 100644 --- a/Modules/zlibmodule.c +++ b/Modules/zlibmodule.c @@ -344,7 +344,7 @@ zlib_compress_impl(PyObject *module, Py_buffer *data, int level, int wbits) PyObject *return_value; int flush; z_stream zst; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; zlibstate *state = get_zlib_state(module); @@ -445,7 +445,7 @@ zlib_decompress_impl(PyObject *module, Py_buffer *data, int wbits, Py_ssize_t ibuflen; int err, flush; z_stream zst; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; _Uint32Window window; // output buffer's UINT32_MAX sliding window zlibstate *state = get_zlib_state(module); @@ -774,7 +774,7 @@ zlib_Compress_compress_impl(compobject *self, PyTypeObject *cls, { PyObject *return_value; int err; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; zlibstate *state = PyType_GetModuleState(cls); ENTER_ZLIB(self); @@ -898,7 +898,7 @@ zlib_Decompress_decompress_impl(compobject *self, PyTypeObject *cls, int err = Z_OK; Py_ssize_t ibuflen; PyObject *return_value; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; PyObject *module = PyType_GetModule(cls); if (module == NULL) @@ -1005,7 +1005,7 @@ zlib_Compress_flush_impl(compobject *self, PyTypeObject *cls, int mode) { int err; PyObject *return_value; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; zlibstate *state = PyType_GetModuleState(cls); /* Flushing with Z_NO_FLUSH is a no-op, so there's no point in @@ -1267,7 +1267,7 @@ zlib_Decompress_flush_impl(compobject *self, PyTypeObject *cls, Py_buffer data; PyObject *return_value; Py_ssize_t ibuflen; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; _Uint32Window window; // output buffer's UINT32_MAX sliding window PyObject *module = PyType_GetModule(cls); From c50d794c7bb81f31d1b977e63d0faba0b926a168 Mon Sep 17 00:00:00 2001 From: SarahPythonista <4283226+SarahPythonista@users.noreply.github.com> Date: Tue, 14 Oct 2025 12:31:21 -0700 Subject: [PATCH 163/373] Improve the comment in URLError (#139874) Clarify that it It overrides `__init__` and `__str__`. --- Lib/urllib/error.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/urllib/error.py b/Lib/urllib/error.py index a9cd1ecadd6..beb32d0df48 100644 --- a/Lib/urllib/error.py +++ b/Lib/urllib/error.py @@ -18,7 +18,7 @@ class URLError(OSError): # URLError is a sub-type of OSError, but it doesn't share any of - # the implementation. need to override __init__ and __str__. + # the implementation. It overrides __init__ and __str__. # It sets self.args for compatibility with other OSError # subclasses, but args doesn't have the typical format with errno in # slot 0 and strerror in slot 1. This may be better than nothing. From 2ca3c85054b231505c5e6f5f3209eb5f63942dce Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Tue, 14 Oct 2025 23:16:00 +0100 Subject: [PATCH 164/373] Update documented minimum required zlib library version (#140116) --- Doc/library/zlib.rst | 6 ++---- Doc/using/configure.rst | 16 +++++++--------- configure | 20 ++++++++++---------- configure.ac | 2 +- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/Doc/library/zlib.rst b/Doc/library/zlib.rst index 6a51320df70..e37be69d6a6 100644 --- a/Doc/library/zlib.rst +++ b/Doc/library/zlib.rst @@ -9,10 +9,8 @@ For applications that require data compression, the functions in this module allow compression and decompression, using the zlib library. The zlib library -has its own home page at https://www.zlib.net. There are known -incompatibilities between the Python module and versions of the zlib library -earlier than 1.1.3; 1.1.3 has a `security vulnerability `_, so we recommend using -1.1.4 or later. +has its own home page at https://www.zlib.net. zlib 1.2.2.1 is the minium +supported version. zlib's functions have many options and often need to be used in a particular order. This documentation doesn't attempt to cover all of the permutations; diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 01537951aeb..1f773a3a547 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -26,28 +26,26 @@ To build optional modules: * `libbz2 `_ for the :mod:`bz2` module. -* `libb2 `_ (:ref:`BLAKE2 `), - used by :mod:`hashlib` module. +* `libb2 `_ (:ref:`BLAKE2 `) + for the :mod:`hashlib` module. * `libffi `_ 3.3.0 is the recommended minimum version for the :mod:`ctypes` module. -* ``liblzma``, for the :mod:`lzma` module. +* ``liblzma`` for the :mod:`lzma` module. * `libmpdec `_ 2.5.0 for the :mod:`decimal` module. -* ``libncurses`` or ``libncursesw``, - for the :mod:`curses` module. +* ``libncurses`` or ``libncursesw`` for the :mod:`curses` module. -* ``libpanel`` or ``libpanelw``, - for the :mod:`curses.panel` module. +* ``libpanel`` or ``libpanelw`` for the :mod:`curses.panel` module. * `libreadline `_ or `libedit `_ for the :mod:`readline` module. -* `libuuid `_, for the :mod:`uuid` module. +* `libuuid `_ for the :mod:`uuid` module. * `OpenSSL `_ 1.1.1 is the minimum version and OpenSSL 3.0.18 is the recommended minimum version for the @@ -57,7 +55,7 @@ To build optional modules: * `Tcl/Tk `_ 8.5.12 for the :mod:`tkinter` module. -* `zlib `_ 1.1.4 is the reccomended minimum version for the +* `zlib `_ 1.2.2.1 is the minimum version for the :mod:`zlib` module. * `zstd `_ 1.4.5 is the minimum version for diff --git a/configure b/configure index 211f8439906..9757b3419d3 100755 --- a/configure +++ b/configure @@ -21736,19 +21736,19 @@ fi pkg_failed=no -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for zlib >= 1.2.0" >&5 -printf %s "checking for zlib >= 1.2.0... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for zlib >= 1.2.2.1" >&5 +printf %s "checking for zlib >= 1.2.2.1... " >&6; } if test -n "$ZLIB_CFLAGS"; then pkg_cv_ZLIB_CFLAGS="$ZLIB_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.0\""; } >&5 - ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.0") 2>&5 + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.2.1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.2.1") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_ZLIB_CFLAGS=`$PKG_CONFIG --cflags "zlib >= 1.2.0" 2>/dev/null` + pkg_cv_ZLIB_CFLAGS=`$PKG_CONFIG --cflags "zlib >= 1.2.2.1" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -21760,12 +21760,12 @@ if test -n "$ZLIB_LIBS"; then pkg_cv_ZLIB_LIBS="$ZLIB_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.0\""; } >&5 - ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.0") 2>&5 + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.2.1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.2.1") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_ZLIB_LIBS=`$PKG_CONFIG --libs "zlib >= 1.2.0" 2>/dev/null` + pkg_cv_ZLIB_LIBS=`$PKG_CONFIG --libs "zlib >= 1.2.2.1" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -21786,9 +21786,9 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - ZLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib >= 1.2.0" 2>&1` + ZLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib >= 1.2.2.1" 2>&1` else - ZLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib >= 1.2.0" 2>&1` + ZLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib >= 1.2.2.1" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$ZLIB_PKG_ERRORS" >&5 diff --git a/configure.ac b/configure.ac index 35bf153a898..f244e0b71a6 100644 --- a/configure.ac +++ b/configure.ac @@ -5424,7 +5424,7 @@ AH_TEMPLATE([HAVE_ZLIB_COPY], [Define if the zlib library has inflateCopy]) dnl detect zlib from Emscripten emport PY_CHECK_EMSCRIPTEN_PORT([ZLIB], [-sUSE_ZLIB]) -PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.0], [ +PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.2.1], [ have_zlib=yes dnl zlib 1.2.0 (2003) added inflateCopy AC_DEFINE([HAVE_ZLIB_COPY], [1]) From 1e1f43519605b7c54a96aa44d0feed09d2bb1a67 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Tue, 14 Oct 2025 16:34:30 -0700 Subject: [PATCH 165/373] gh-140126: Fix compile error if --with-assertions is enabled (#140133) The `types_world_is_stopped()` function needs to be defined if NDEBUG is not defined. --- Objects/typeobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 9398bcb29c8..29233c1959c 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -81,7 +81,7 @@ class object "PyObject *" "&PyBaseObject_Type" #define END_TYPE_DICT_LOCK() Py_END_CRITICAL_SECTION2() -#ifdef Py_DEBUG +#ifndef NDEBUG // Return true if the world is currently stopped. static bool types_world_is_stopped(void) From 0bcb1c25f7ba254bea9b744c7c5423cfebade3b3 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Tue, 14 Oct 2025 21:46:43 -0400 Subject: [PATCH 166/373] Revert "gh-140067: Fix memory leak in sub-interpreter creation (#140111)" (#140140) This reverts commit 59547a251f7069dc6e08cb6082dd21872671e381. --- Include/internal/pycore_interp_structs.h | 6 ++++++ ...2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst | 1 - Python/pystate.c | 15 ++++++--------- 3 files changed, 12 insertions(+), 10 deletions(-) delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst diff --git a/Include/internal/pycore_interp_structs.h b/Include/internal/pycore_interp_structs.h index badc97808c6..2124e76514f 100644 --- a/Include/internal/pycore_interp_structs.h +++ b/Include/internal/pycore_interp_structs.h @@ -769,6 +769,12 @@ struct _is { * and should be placed at the beginning. */ struct _ceval_state ceval; + /* This structure is carefully allocated so that it's correctly aligned + * to avoid undefined behaviors during LOAD and STORE. The '_malloced' + * field stores the allocated pointer address that will later be freed. + */ + void *_malloced; + PyInterpreterState *next; int64_t id; diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst deleted file mode 100644 index 3c5a828101d..00000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst +++ /dev/null @@ -1 +0,0 @@ -Fix memory leak in sub-interpreter creation. diff --git a/Python/pystate.c b/Python/pystate.c index bf6e4e56e6d..dbed609f29a 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -457,19 +457,16 @@ _PyInterpreterState_Enable(_PyRuntimeState *runtime) static PyInterpreterState * alloc_interpreter(void) { - // Aligned allocation for PyInterpreterState. - // the first word of the memory block is used to store - // the original pointer to be used later to free the memory. size_t alignment = _Alignof(PyInterpreterState); - size_t allocsize = sizeof(PyInterpreterState) + sizeof(void *) + alignment - 1; + size_t allocsize = sizeof(PyInterpreterState) + alignment - 1; void *mem = PyMem_RawCalloc(1, allocsize); if (mem == NULL) { return NULL; } - void *ptr = _Py_ALIGN_UP((char *)mem + sizeof(void *), alignment); - ((void **)ptr)[-1] = mem; - assert(_Py_IS_ALIGNED(ptr, alignment)); - return ptr; + PyInterpreterState *interp = _Py_ALIGN_UP(mem, alignment); + assert(_Py_IS_ALIGNED(interp, alignment)); + interp->_malloced = mem; + return interp; } static void @@ -484,7 +481,7 @@ free_interpreter(PyInterpreterState *interp) interp->obmalloc = NULL; } assert(_Py_IS_ALIGNED(interp, _Alignof(PyInterpreterState))); - PyMem_RawFree(((void **)interp)[-1]); + PyMem_RawFree(interp->_malloced); } } From 6416e6ebe5b88087ada6f4a56972053edb9c2e01 Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Tue, 14 Oct 2025 19:39:17 -0700 Subject: [PATCH 167/373] gh-129559: Remove extra dot in bytearray.resize AC (#140134) --- Objects/bytearrayobject.c | 4 ++-- Objects/clinic/bytearrayobject.c.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index c519485c1cc..a73bfff340c 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -1469,14 +1469,14 @@ bytearray_removesuffix_impl(PyByteArrayObject *self, Py_buffer *suffix) /*[clinic input] bytearray.resize size: Py_ssize_t - New size to resize to.. + New size to resize to. / Resize the internal buffer of bytearray to len. [clinic start generated code]*/ static PyObject * bytearray_resize_impl(PyByteArrayObject *self, Py_ssize_t size) -/*[clinic end generated code: output=f73524922990b2d9 input=75fd4d17c4aa47d3]*/ +/*[clinic end generated code: output=f73524922990b2d9 input=6c9a260ca7f72071]*/ { Py_ssize_t start_size = PyByteArray_GET_SIZE(self); int result = PyByteArray_Resize((PyObject *)self, size); diff --git a/Objects/clinic/bytearrayobject.c.h b/Objects/clinic/bytearrayobject.c.h index ffb45ade11f..6f13865177d 100644 --- a/Objects/clinic/bytearrayobject.c.h +++ b/Objects/clinic/bytearrayobject.c.h @@ -599,7 +599,7 @@ PyDoc_STRVAR(bytearray_resize__doc__, "Resize the internal buffer of bytearray to len.\n" "\n" " size\n" -" New size to resize to.."); +" New size to resize to."); #define BYTEARRAY_RESIZE_METHODDEF \ {"resize", (PyCFunction)bytearray_resize, METH_O, bytearray_resize__doc__}, @@ -1796,4 +1796,4 @@ bytearray_sizeof(PyObject *self, PyObject *Py_UNUSED(ignored)) { return bytearray_sizeof_impl((PyByteArrayObject *)self); } -/*[clinic end generated code: output=be6d28193bc96a2c input=a9049054013a1b77]*/ +/*[clinic end generated code: output=fdfe41139c91e409 input=a9049054013a1b77]*/ From 46f11b36ade891ca7e83efa31055b0705bbc7bd5 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Wed, 15 Oct 2025 12:18:48 +0100 Subject: [PATCH 168/373] gh-76007: Deprecate `zlib.__version__` attribute (#140130) --- Doc/deprecations/pending-removal-in-3.20.rst | 3 ++- Doc/whatsnew/3.15.rst | 3 ++- Lib/test/test_zlib.py | 10 ++++++++ ...5-10-14-20-27-06.gh-issue-76007.2NcUbo.rst | 2 ++ Modules/zlibmodule.c | 25 ++++++++++++++++--- 5 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-14-20-27-06.gh-issue-76007.2NcUbo.rst diff --git a/Doc/deprecations/pending-removal-in-3.20.rst b/Doc/deprecations/pending-removal-in-3.20.rst index 8bc863b185d..c86979c8ff9 100644 --- a/Doc/deprecations/pending-removal-in-3.20.rst +++ b/Doc/deprecations/pending-removal-in-3.20.rst @@ -19,5 +19,6 @@ Pending removal in Python 3.20 - :mod:`tabnanny` - :mod:`tkinter.font` - :mod:`tkinter.ttk` + - :mod:`zlib` - (Contributed by Hugo van Kemenade in :gh:`76007`.) + (Contributed by Hugo van Kemenade and Stan Ulbrych in :gh:`76007`.) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index c5321ee9983..8a757724442 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -828,8 +828,9 @@ New deprecations - :mod:`tabnanny` - :mod:`tkinter.font` - :mod:`tkinter.ttk` + - :mod:`zlib` - (Contributed by Hugo van Kemenade in :gh:`76007`.) + (Contributed by Hugo van Kemenade and Stan Ulbrych in :gh:`76007`.) .. Add deprecations above alphabetically, not here at the end. diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index c57ab51eca1..ed9d8540815 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -1222,5 +1222,15 @@ def __index__(self): return 100 +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(zlib, "__version__") + self.assertEqual(cm.filename, __file__) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2025-10-14-20-27-06.gh-issue-76007.2NcUbo.rst b/Misc/NEWS.d/next/Library/2025-10-14-20-27-06.gh-issue-76007.2NcUbo.rst new file mode 100644 index 00000000000..567fb5ef904 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-14-20-27-06.gh-issue-76007.2NcUbo.rst @@ -0,0 +1,2 @@ +:mod:`zlib`: Deprecate ``__version__`` and schedule for removal in Python +3.20. diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c index 36c933bf618..6bac09aa6c2 100644 --- a/Modules/zlibmodule.c +++ b/Modules/zlibmodule.c @@ -2015,6 +2015,27 @@ zlib_crc32_combine_impl(PyObject *module, unsigned int crc1, return crc32_combine(crc1, crc2, len); } +static PyObject * +zlib_getattr(PyObject *self, PyObject *args) +{ + PyObject *name; + if (!PyArg_UnpackTuple(args, "__getattr__", 1, 1, &name)) { + return NULL; + } + + if (PyUnicode_Check(name) && PyUnicode_EqualToUTF8(name, "__version__")) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + 1) < 0) { + return NULL; + } + return PyUnicode_FromString("1.0"); + } + + PyErr_Format(PyExc_AttributeError, "module 'zlib' has no attribute %R", name); + return NULL; +} + static PyMethodDef zlib_methods[] = { ZLIB_ADLER32_METHODDEF @@ -2025,6 +2046,7 @@ static PyMethodDef zlib_methods[] = ZLIB_CRC32_COMBINE_METHODDEF ZLIB_DECOMPRESS_METHODDEF ZLIB_DECOMPRESSOBJ_METHODDEF + {"__getattr__", zlib_getattr, METH_VARARGS, "Module __getattr__"}, {NULL, NULL} }; @@ -2221,9 +2243,6 @@ zlib_exec(PyObject *mod) return -1; } #endif - if (PyModule_AddStringConstant(mod, "__version__", "1.0") < 0) { - return -1; - } return 0; } From 4126d9f1ab682afeecdff624b1fd698c192bcb21 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 15 Oct 2025 14:54:18 +0200 Subject: [PATCH 169/373] gh-129813: Enhance PyBytesWriter documentation (#140152) Co-authored-by: Antoine Pitrou --- Doc/c-api/bytes.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Doc/c-api/bytes.rst b/Doc/c-api/bytes.rst index 9bddfe4dce2..865a9e5d2bf 100644 --- a/Doc/c-api/bytes.rst +++ b/Doc/c-api/bytes.rst @@ -259,6 +259,7 @@ Create, Finish, Discard If *size* is greater than zero, allocate *size* bytes, and set the writer size to *size*. The caller is responsible to write *size* bytes using :c:func:`PyBytesWriter_GetData`. + This function does not overallocate. On error, set an exception and return ``NULL``. @@ -349,6 +350,8 @@ Low-level API Resize the writer to *size* bytes. It can be used to enlarge or to shrink the writer. + This function typically overallocates to achieve amortized performance when + resizing multiple times. Newly allocated bytes are left uninitialized. @@ -360,6 +363,8 @@ Low-level API .. c:function:: int PyBytesWriter_Grow(PyBytesWriter *writer, Py_ssize_t grow) Resize the writer by adding *grow* bytes to the current writer size. + This function typically overallocates to achieve amortized performance when + resizing multiple times. Newly allocated bytes are left uninitialized. From 27acaf1cb79b30ce2a79e64ab1bc204f57cbe5d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 15 Oct 2025 15:18:07 +0200 Subject: [PATCH 170/373] gh-139327: consolidate `sqlite3_finalize` and `sqlite3_reset` usages (GH-139329) --- Modules/_sqlite/blob.c | 4 +- Modules/_sqlite/cursor.c | 101 ++++++++++++++++++++++++++++++++---- Modules/_sqlite/statement.c | 17 ++++-- Modules/_sqlite/util.c | 5 +- Modules/_sqlite/util.h | 2 +- 5 files changed, 113 insertions(+), 16 deletions(-) diff --git a/Modules/_sqlite/blob.c b/Modules/_sqlite/blob.c index 7f1fa26c3ba..4a213f34888 100644 --- a/Modules/_sqlite/blob.c +++ b/Modules/_sqlite/blob.c @@ -118,7 +118,9 @@ static void blob_seterror(pysqlite_Blob *self, int rc) { assert(self->connection != NULL); - set_error_from_db(self->connection->state, self->connection->db); + assert(rc != SQLITE_OK); + set_error_from_code(self->connection->state, rc); + assert(PyErr_Occurred()); } static PyObject * diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index cb0f9adcc45..4611c9e5e3e 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -58,6 +58,41 @@ check_cursor_locked(pysqlite_Cursor *cur) return 1; } +static pysqlite_state * +get_module_state_by_cursor(pysqlite_Cursor *cursor) +{ + if (cursor->connection != NULL && cursor->connection->state != NULL) { + return cursor->connection->state; + } + return pysqlite_get_state_by_type(Py_TYPE(cursor)); +} + +static void +cursor_sqlite3_internal_error(pysqlite_Cursor *cursor, + const char *error_message, + int chain_exceptions) +{ + pysqlite_state *state = get_module_state_by_cursor(cursor); + if (chain_exceptions) { + assert(PyErr_Occurred()); + PyObject *exc = PyErr_GetRaisedException(); + PyErr_SetString(state->InternalError, error_message); + _PyErr_ChainExceptions1(exc); + } + else { + assert(!PyErr_Occurred()); + PyErr_SetString(state->InternalError, error_message); + } +} + +static void +cursor_cannot_reset_stmt_error(pysqlite_Cursor *cursor, int chain_exceptions) +{ + cursor_sqlite3_internal_error(cursor, + "cannot reset statement", + chain_exceptions); +} + /*[clinic input] module _sqlite3 class _sqlite3.Cursor "pysqlite_Cursor *" "clinic_state()->CursorType" @@ -173,8 +208,12 @@ cursor_clear(PyObject *op) Py_CLEAR(self->row_factory); if (self->statement) { /* Reset the statement if the user has not closed the cursor */ - stmt_reset(self->statement); + int rc = stmt_reset(self->statement); Py_CLEAR(self->statement); + if (rc != SQLITE_OK) { + cursor_cannot_reset_stmt_error(self, 0); + PyErr_FormatUnraisable("Exception ignored in cursor_clear()"); + } } return 0; @@ -837,7 +876,9 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation if (self->statement) { // Reset pending statements on this cursor. - (void)stmt_reset(self->statement); + if (stmt_reset(self->statement) != SQLITE_OK) { + goto reset_failure; + } } PyObject *stmt = get_statement_from_cache(self, operation); @@ -861,7 +902,9 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation } } - (void)stmt_reset(self->statement); + if (stmt_reset(self->statement) != SQLITE_OK) { + goto reset_failure; + } self->rowcount = self->statement->is_dml ? 0L : -1L; /* We start a transaction implicitly before a DML statement. @@ -943,7 +986,9 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation if (self->statement->is_dml) { self->rowcount += (long)sqlite3_changes(self->connection->db); } - stmt_reset(self->statement); + if (stmt_reset(self->statement) != SQLITE_OK) { + goto reset_failure; + } } Py_XDECREF(parameters); } @@ -968,8 +1013,15 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation if (PyErr_Occurred()) { if (self->statement) { - (void)stmt_reset(self->statement); + sqlite3 *db = sqlite3_db_handle(self->statement->st); + int sqlite3_state = sqlite3_errcode(db); + // stmt_reset() may return a previously set exception, + // either triggered because of Python or sqlite3. + rc = stmt_reset(self->statement); Py_CLEAR(self->statement); + if (sqlite3_state == SQLITE_OK && rc != SQLITE_OK) { + cursor_cannot_reset_stmt_error(self, 1); + } } self->rowcount = -1L; return NULL; @@ -978,6 +1030,20 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation Py_CLEAR(self->statement); } return Py_NewRef((PyObject *)self); + +reset_failure: + /* suite to execute when stmt_reset() failed and no exception is set */ + assert(!PyErr_Occurred()); + + Py_XDECREF(parameters); + Py_XDECREF(parameters_iter); + Py_XDECREF(parameters_list); + + self->locked = 0; + self->rowcount = -1L; + Py_CLEAR(self->statement); + cursor_cannot_reset_stmt_error(self, 0); + return NULL; } /*[clinic input] @@ -1120,14 +1186,20 @@ pysqlite_cursor_iternext(PyObject *op) if (self->statement->is_dml) { self->rowcount = (long)sqlite3_changes(self->connection->db); } - (void)stmt_reset(self->statement); + rc = stmt_reset(self->statement); Py_CLEAR(self->statement); + if (rc != SQLITE_OK) { + goto reset_failure; + } } else if (rc != SQLITE_ROW) { - set_error_from_db(self->connection->state, self->connection->db); - (void)stmt_reset(self->statement); + rc = set_error_from_db(self->connection->state, self->connection->db); + int reset_rc = stmt_reset(self->statement); Py_CLEAR(self->statement); Py_DECREF(row); + if (rc == SQLITE_OK && reset_rc != SQLITE_OK) { + goto reset_failure; + } return NULL; } if (!Py_IsNone(self->row_factory)) { @@ -1137,6 +1209,10 @@ pysqlite_cursor_iternext(PyObject *op) Py_SETREF(row, new_row); } return row; + +reset_failure: + cursor_cannot_reset_stmt_error(self, 0); + return NULL; } /*[clinic input] @@ -1291,8 +1367,15 @@ pysqlite_cursor_close_impl(pysqlite_Cursor *self) } if (self->statement) { - (void)stmt_reset(self->statement); + int rc = stmt_reset(self->statement); + // Force self->statement to be NULL even if stmt_reset() may have + // failed to avoid a possible double-free if someone calls close() + // twice as a leak here would be better than a double-free. Py_CLEAR(self->statement); + if (rc != SQLITE_OK) { + cursor_cannot_reset_stmt_error(self, 0); + return NULL; + } } self->closed = 1; diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c index c551ca59b33..f31c699482f 100644 --- a/Modules/_sqlite/statement.c +++ b/Modules/_sqlite/statement.c @@ -103,7 +103,12 @@ pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql) return self; error: - (void)sqlite3_finalize(stmt); + assert(PyErr_Occurred()); + if (sqlite3_finalize(stmt) != SQLITE_OK) { + PyObject *exc = PyErr_GetRaisedException(); + PyErr_SetString(connection->InternalError, "cannot finalize statement"); + _PyErr_ChainExceptions1(exc); + } return NULL; } @@ -114,10 +119,16 @@ stmt_dealloc(PyObject *op) PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(op); if (self->st) { + int rc; Py_BEGIN_ALLOW_THREADS - sqlite3_finalize(self->st); + rc = sqlite3_finalize(self->st); Py_END_ALLOW_THREADS - self->st = 0; + self->st = NULL; + if (rc != SQLITE_OK) { + pysqlite_state *state = PyType_GetModuleState(Py_TYPE(op)); + PyErr_SetString(state->InternalError, "cannot finalize statement"); + PyErr_FormatUnraisable("Exception ignored in stmt_dealloc()"); + } } tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_sqlite/util.c b/Modules/_sqlite/util.c index 103248ff55a..177e0f668a0 100644 --- a/Modules/_sqlite/util.c +++ b/Modules/_sqlite/util.c @@ -135,14 +135,14 @@ set_error_from_code(pysqlite_state *state, int code) /** * Checks the SQLite error code and sets the appropriate DB-API exception. */ -void +int set_error_from_db(pysqlite_state *state, sqlite3 *db) { int errorcode = sqlite3_errcode(db); PyObject *exc_class = get_exception_class(state, errorcode); if (exc_class == NULL) { // No new exception need be raised. - return; + return SQLITE_OK; } /* Create and set the exception. */ @@ -150,6 +150,7 @@ set_error_from_db(pysqlite_state *state, sqlite3 *db) // sqlite3_errmsg() always returns an UTF-8 encoded message const char *errmsg = sqlite3_errmsg(db); raise_exception(exc_class, extended_errcode, errmsg); + return errorcode; } #ifdef WORDS_BIGENDIAN diff --git a/Modules/_sqlite/util.h b/Modules/_sqlite/util.h index f8e45baffae..c00369496f9 100644 --- a/Modules/_sqlite/util.h +++ b/Modules/_sqlite/util.h @@ -31,7 +31,7 @@ /** * Checks the SQLite error code and sets the appropriate DB-API exception. */ -void set_error_from_db(pysqlite_state *state, sqlite3 *db); +int set_error_from_db(pysqlite_state *state, sqlite3 *db); void set_error_from_code(pysqlite_state *state, int code); sqlite_int64 _pysqlite_long_as_int64(PyObject * value); From fe9ac7fc8ca00515b3c9a4d91d7bbfe038c861e7 Mon Sep 17 00:00:00 2001 From: Jeffrey Bosboom Date: Wed, 15 Oct 2025 06:44:08 -0700 Subject: [PATCH 171/373] gh-83714: Implement os.statx() function (#139178) Co-authored-by: Cody Maloney Co-authored-by: Victor Stinner --- Doc/library/os.rst | 208 ++++++++ Doc/library/stat.rst | 19 + Doc/whatsnew/3.15.rst | 8 + .../pycore_global_objects_fini_generated.h | 1 + Include/internal/pycore_global_strings.h | 1 + .../internal/pycore_runtime_init_generated.h | 1 + .../internal/pycore_unicodeobject_generated.h | 4 + Lib/os.py | 6 + Lib/stat.py | 15 + Lib/test/test_os/test_os.py | 106 +++- Lib/test/test_os/test_posix.py | 88 +++- Misc/ACKS | 1 + ...5-09-18-21-25-41.gh-issue-83714.TQjDWZ.rst | 2 + Modules/clinic/posixmodule.c.h | 140 ++++- Modules/posixmodule.c | 478 ++++++++++++++++-- configure | 6 + configure.ac | 2 +- pyconfig.h.in | 3 + 18 files changed, 1018 insertions(+), 71 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-09-18-21-25-41.gh-issue-83714.TQjDWZ.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index a5843f3fc0d..1ac87b32bad 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3383,6 +3383,214 @@ features: Added the :attr:`st_birthtime` member on Windows. +.. function:: statx(path, mask, *, flags=0, dir_fd=None, follow_symlinks=True) + + Get the status of a file or file descriptor by performing a :c:func:`!statx` + system call on the given path. + + *path* is a :term:`path-like object` or an open file descriptor. *mask* is a + combination of the module-level :const:`STATX_* ` constants + specifying the information to retrieve. *flags* is a combination of the + module-level :const:`AT_STATX_* ` constants and/or + :const:`AT_NO_AUTOMOUNT`. Returns a :class:`statx_result` object whose + :attr:`~os.statx_result.stx_mask` attribute specifies the information + actually retrieved (which may differ from *mask*). + + This function supports :ref:`specifying a file descriptor `, + :ref:`paths relative to directory descriptors `, and + :ref:`not following symlinks `. + + .. seealso:: The :manpage:`statx(2)` man page. + + .. availability:: Linux >= 4.11 with glibc >= 2.28. + + .. versionadded:: next + + +.. class:: statx_result + + Information about a file returned by :func:`os.statx`. + + :class:`!statx_result` has all the attributes that :class:`~stat_result` has + on Linux, making it :term:`duck-typing` compatible, but + :class:`!statx_result` is not a subclass of :class:`~stat_result` and cannot + be used as a tuple. + + :class:`!statx_result` has the following additional attributes: + + .. attribute:: stx_mask + + Bitmask of :const:`STATX_* ` constants specifying the + information retrieved, which may differ from what was requested. + + .. attribute:: stx_attributes_mask + + Bitmask of :const:`STATX_ATTR_* ` constants + specifying the attributes bits supported for this file. + + .. attribute:: stx_attributes + + Bitmask of :const:`STATX_ATTR_* ` constants + specifying the attributes of this file. + + .. attribute:: stx_dev_major + + Major number of the device on which this file resides. + + .. attribute:: stx_dev_minor + + Minor number of the device on which this file resides. + + .. attribute:: stx_rdev_major + + Major number of the device this file represents. + + .. attribute:: stx_rdev_minor + + Minor number of the device this file represents. + + .. attribute:: stx_mnt_id + + Mount ID. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 5.8. + + .. attribute:: stx_dio_mem_align + + Direct I/O memory buffer alignment requirement. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.1. + + .. attribute:: stx_dio_offset_align + + Direct I/O file offset alignment requirement. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.1. + + .. attribute:: stx_subvol + + Subvolume ID. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.10. + + .. attribute:: stx_atomic_write_unit_min + + Minimum size for direct I/O with torn-write protection. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.11. + + .. attribute:: stx_atomic_write_unit_max + + Maximum size for direct I/O with torn-write protection. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.11. + + .. attribute:: stx_atomic_write_unit_max_opt + + Maximum optimized size for direct I/O with torn-write protection. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.11. + + .. attribute:: stx_atomic_write_segments_max + + Maximum iovecs for direct I/O with torn-write protection. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.11. + + .. attribute:: stx_dio_read_offset_align + + Direct I/O file offset alignment requirement for reads. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.14. + + .. seealso:: The :manpage:`statx(2)` man page. + + .. availability:: Linux >= 4.11 with glibc >= 2.28. + + .. versionadded:: next + + +.. data:: STATX_TYPE + STATX_MODE + STATX_NLINK + STATX_UID + STATX_GID + STATX_ATIME + STATX_MTIME + STATX_CTIME + STATX_INO + STATX_SIZE + STATX_BLOCKS + STATX_BASIC_STATS + STATX_BTIME + STATX_MNT_ID + STATX_DIOALIGN + STATX_MNT_ID_UNIQUE + STATX_SUBVOL + STATX_WRITE_ATOMIC + STATX_DIO_READ_ALIGN + + Bitflags for use in the *mask* parameter to :func:`os.statx`. Flags + including and after :const:`!STATX_MNT_ID` are only available when their + corresponding members in :class:`statx_result` are available. + + .. availability:: Linux >= 4.11 with glibc >= 2.28. + + .. versionadded:: next + +.. data:: AT_STATX_FORCE_SYNC + + A flag for the :func:`os.statx` function. Requests that the kernel return + up-to-date information even when doing so is expensive (for example, + requiring a round trip to the server for a file on a network filesystem). + + .. availability:: Linux >= 4.11 with glibc >= 2.28. + + .. versionadded:: next + +.. data:: AT_STATX_DONT_SYNC + + A flag for the :func:`os.statx` function. Requests that the kernel return + cached information if possible. + + .. availability:: Linux >= 4.11 with glibc >= 2.28. + + .. versionadded:: next + +.. data:: AT_STATX_SYNC_AS_STAT + + A flag for the :func:`os.statx` function. This flag is defined as ``0``, so + it has no effect, but it can be used to explicitly indicate neither + :data:`AT_STATX_FORCE_SYNC` nor :data:`AT_STATX_DONT_SYNC` is being passed. + In the absence of the other two flags, the kernel will generally return + information as fresh as :func:`os.stat` would return. + + .. availability:: Linux >= 4.11 with glibc >= 2.28. + + .. versionadded:: next + + +.. data:: AT_NO_AUTOMOUNT + + If the final component of a path is an automount point, operate on the + automount point instead of performing the automount. On Linux, + :func:`os.stat`, :func:`os.fstat` and :func:`os.lstat` always behave this + way. + + .. availability:: Linux. + + .. versionadded:: next + + .. function:: statvfs(path) Perform a :c:func:`!statvfs` system call on the given path. The return value is diff --git a/Doc/library/stat.rst b/Doc/library/stat.rst index 8434b2e8c75..1cbec3ab847 100644 --- a/Doc/library/stat.rst +++ b/Doc/library/stat.rst @@ -493,3 +493,22 @@ constants, but are not an exhaustive list. IO_REPARSE_TAG_APPEXECLINK .. versionadded:: 3.8 + +On Linux, the following file attribute constants are available for use when +testing bits in the :attr:`~os.statx_result.stx_attributes` and +:attr:`~os.statx_result.stx_attributes_mask` members returned by +:func:`os.statx`. See the :manpage:`statx(2)` man page for more detail on the +meaning of these constants. + +.. data:: STATX_ATTR_COMPRESSED + STATX_ATTR_IMMUTABLE + STATX_ATTR_APPEND + STATX_ATTR_NODUMP + STATX_ATTR_ENCRYPTED + STATX_ATTR_AUTOMOUNT + STATX_ATTR_MOUNT_ROOT + STATX_ATTR_VERITY + STATX_ATTR_DAX + STATX_ATTR_WRITE_ATOMIC + + .. versionadded:: next diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 8a757724442..56028a92aa2 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -433,6 +433,14 @@ mmap (Contributed by Serhiy Storchaka in :gh:`78502`.) +os +-- + +* Add :func:`os.statx` on Linux kernel versions 4.11 and later with + glibc versions 2.28 and later. + (Contributed by Jeffrey Bosboom in :gh:`83714`.) + + os.path ------- diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 1f6b27b14d0..f7416c5ffc5 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -1867,6 +1867,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(loop)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(manual_reset)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(mapping)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(mask)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(match)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(max_length)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(maxdigits)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 6959343947c..ca71c12836d 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -590,6 +590,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(loop) STRUCT_FOR_ID(manual_reset) STRUCT_FOR_ID(mapping) + STRUCT_FOR_ID(mask) STRUCT_FOR_ID(match) STRUCT_FOR_ID(max_length) STRUCT_FOR_ID(maxdigits) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index be4eae42b5d..72996db9f71 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -1865,6 +1865,7 @@ extern "C" { INIT_ID(loop), \ INIT_ID(manual_reset), \ INIT_ID(mapping), \ + INIT_ID(mask), \ INIT_ID(match), \ INIT_ID(max_length), \ INIT_ID(maxdigits), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 45b00a20a07..c4cf56ad7f1 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -2148,6 +2148,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(mask); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(match); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Lib/os.py b/Lib/os.py index 328d13c303b..6e6db96b307 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -136,6 +136,8 @@ def _add(str, fn): _add("HAVE_UNLINKAT", "unlink") _add("HAVE_UNLINKAT", "rmdir") _add("HAVE_UTIMENSAT", "utime") + if _exists("statx"): + _set.add(statx) supports_dir_fd = _set _set = set() @@ -157,6 +159,8 @@ def _add(str, fn): _add("HAVE_FPATHCONF", "pathconf") if _exists("statvfs") and _exists("fstatvfs"): # mac os x10.3 _add("HAVE_FSTATVFS", "statvfs") + if _exists("statx"): + _set.add(statx) supports_fd = _set _set = set() @@ -195,6 +199,8 @@ def _add(str, fn): _add("HAVE_FSTATAT", "stat") _add("HAVE_UTIMENSAT", "utime") _add("MS_WINDOWS", "stat") + if _exists("statx"): + _set.add(statx) supports_follow_symlinks = _set del _set diff --git a/Lib/stat.py b/Lib/stat.py index 1b4ed1ebc94..ab1b25b9d63 100644 --- a/Lib/stat.py +++ b/Lib/stat.py @@ -200,6 +200,21 @@ def filemode(mode): FILE_ATTRIBUTE_VIRTUAL = 65536 +# Linux STATX_ATTR constants for interpreting os.statx()'s +# "stx_attributes" and "stx_attributes_mask" members + +STATX_ATTR_COMPRESSED = 0x00000004 +STATX_ATTR_IMMUTABLE = 0x00000010 +STATX_ATTR_APPEND = 0x00000020 +STATX_ATTR_NODUMP = 0x00000040 +STATX_ATTR_ENCRYPTED = 0x00000800 +STATX_ATTR_AUTOMOUNT = 0x00001000 +STATX_ATTR_MOUNT_ROOT = 0x00002000 +STATX_ATTR_VERITY = 0x00100000 +STATX_ATTR_DAX = 0x00200000 +STATX_ATTR_WRITE_ATOMIC = 0x00400000 + + # If available, use C implementation try: from _stat import * diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py index 86880a6d281..dd6f89e81aa 100644 --- a/Lib/test/test_os/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -630,6 +630,14 @@ def setUp(self): self.addCleanup(os_helper.unlink, self.fname) create_file(self.fname, b"ABC") + def check_timestamp_agreement(self, result, names): + # Make sure that the st_?time and st_?time_ns fields roughly agree + # (they should always agree up to around tens-of-microseconds) + for name in names: + floaty = int(getattr(result, name) * 100_000) + nanosecondy = getattr(result, name + "_ns") // 10_000 + self.assertAlmostEqual(floaty, nanosecondy, delta=2, msg=name) + def check_stat_attributes(self, fname): result = os.stat(fname) @@ -650,21 +658,15 @@ def trunc(x): return x result[getattr(stat, name)]) self.assertIn(attr, members) - # Make sure that the st_?time and st_?time_ns fields roughly agree - # (they should always agree up to around tens-of-microseconds) - for name in 'st_atime st_mtime st_ctime'.split(): - floaty = int(getattr(result, name) * 100000) - nanosecondy = getattr(result, name + "_ns") // 10000 - self.assertAlmostEqual(floaty, nanosecondy, delta=2) - - # Ensure both birthtime and birthtime_ns roughly agree, if present + time_attributes = ['st_atime', 'st_mtime', 'st_ctime'] try: - floaty = int(result.st_birthtime * 100000) - nanosecondy = result.st_birthtime_ns // 10000 + result.st_birthtime + result.st_birthtime_ns except AttributeError: pass else: - self.assertAlmostEqual(floaty, nanosecondy, delta=2) + time_attributes.append('st_birthtime') + self.check_timestamp_agreement(result, time_attributes) try: result[200] @@ -725,6 +727,88 @@ def test_stat_result_pickle(self): unpickled = pickle.loads(p) self.assertEqual(result, unpickled) + def check_statx_attributes(self, fname): + maximal_mask = 0 + for name in dir(os): + if name.startswith('STATX_'): + maximal_mask |= getattr(os, name) + result = os.statx(self.fname, maximal_mask) + + time_attributes = ('st_atime', 'st_mtime', 'st_ctime', 'st_birthtime') + self.check_timestamp_agreement(result, time_attributes) + + # Check that valid attributes match os.stat. + requirements = ( + ('st_mode', os.STATX_TYPE | os.STATX_MODE), + ('st_nlink', os.STATX_NLINK), + ('st_uid', os.STATX_UID), + ('st_gid', os.STATX_GID), + ('st_atime', os.STATX_ATIME), + ('st_atime_ns', os.STATX_ATIME), + ('st_mtime', os.STATX_MTIME), + ('st_mtime_ns', os.STATX_MTIME), + ('st_ctime', os.STATX_CTIME), + ('st_ctime_ns', os.STATX_CTIME), + ('st_ino', os.STATX_INO), + ('st_size', os.STATX_SIZE), + ('st_blocks', os.STATX_BLOCKS), + ('st_birthtime', os.STATX_BTIME), + ('st_birthtime_ns', os.STATX_BTIME), + # unconditionally valid members + ('st_blksize', 0), + ('st_rdev', 0), + ('st_dev', 0), + ) + basic_result = os.stat(self.fname) + for name, bits in requirements: + if result.stx_mask & bits == bits and hasattr(basic_result, name): + x = getattr(result, name) + b = getattr(basic_result, name) + self.assertEqual(type(x), type(b)) + if isinstance(x, float): + self.assertAlmostEqual(x, b, msg=name) + else: + self.assertEqual(x, b, msg=name) + + self.assertEqual(result.stx_rdev_major, os.major(result.st_rdev)) + self.assertEqual(result.stx_rdev_minor, os.minor(result.st_rdev)) + self.assertEqual(result.stx_dev_major, os.major(result.st_dev)) + self.assertEqual(result.stx_dev_minor, os.minor(result.st_dev)) + + members = [name for name in dir(result) + if name.startswith('st_') or name.startswith('stx_')] + for name in members: + try: + setattr(result, name, 1) + self.fail("No exception raised") + except AttributeError: + pass + + self.assertEqual(result.stx_attributes & result.stx_attributes_mask, + result.stx_attributes) + + # statx_result is not a tuple or tuple-like object. + with self.assertRaisesRegex(TypeError, 'not subscriptable'): + result[0] + with self.assertRaisesRegex(TypeError, 'cannot unpack'): + _, _ = result + + @unittest.skipUnless(hasattr(os, 'statx'), 'test needs os.statx()') + def test_statx_attributes(self): + self.check_statx_attributes(self.fname) + + @unittest.skipUnless(hasattr(os, 'statx'), 'test needs os.statx()') + def test_statx_attributes_bytes(self): + try: + fname = self.fname.encode(sys.getfilesystemencoding()) + except UnicodeEncodeError: + self.skipTest("cannot encode %a for the filesystem" % self.fname) + self.check_statx_attributes(fname) + + @unittest.skipUnless(hasattr(os, 'statx'), 'test needs os.statx()') + def test_statx_attributes_pathlike(self): + self.check_statx_attributes(FakePath(self.fname)) + @unittest.skipUnless(hasattr(os, 'statvfs'), 'test needs os.statvfs()') def test_statvfs_attributes(self): result = os.statvfs(self.fname) diff --git a/Lib/test/test_os/test_posix.py b/Lib/test/test_os/test_posix.py index ab3d128d08a..905f0201253 100644 --- a/Lib/test/test_os/test_posix.py +++ b/Lib/test/test_os/test_posix.py @@ -668,22 +668,65 @@ def test_fstat(self): finally: fp.close() - def test_stat(self): - self.assertTrue(posix.stat(os_helper.TESTFN)) - self.assertTrue(posix.stat(os.fsencode(os_helper.TESTFN))) + def check_statlike_path(self, func): + self.assertTrue(func(os_helper.TESTFN)) + self.assertTrue(func(os.fsencode(os_helper.TESTFN))) + self.assertTrue(func(os_helper.FakePath(os_helper.TESTFN))) self.assertRaisesRegex(TypeError, 'should be string, bytes, os.PathLike or integer, not', - posix.stat, bytearray(os.fsencode(os_helper.TESTFN))) + func, bytearray(os.fsencode(os_helper.TESTFN))) self.assertRaisesRegex(TypeError, 'should be string, bytes, os.PathLike or integer, not', - posix.stat, None) + func, None) self.assertRaisesRegex(TypeError, 'should be string, bytes, os.PathLike or integer, not', - posix.stat, list(os_helper.TESTFN)) + func, list(os_helper.TESTFN)) self.assertRaisesRegex(TypeError, 'should be string, bytes, os.PathLike or integer, not', - posix.stat, list(os.fsencode(os_helper.TESTFN))) + func, list(os.fsencode(os_helper.TESTFN))) + + def test_stat(self): + self.check_statlike_path(posix.stat) + + @unittest.skipUnless(hasattr(posix, 'statx'), 'test needs posix.statx()') + def test_statx(self): + def func(path, **kwargs): + return posix.statx(path, posix.STATX_BASIC_STATS, **kwargs) + self.check_statlike_path(func) + + @unittest.skipUnless(hasattr(posix, 'statx'), 'test needs posix.statx()') + def test_statx_flags(self): + # glibc's fallback implementation of statx via the stat family fails + # with EINVAL on the (nonzero) sync flags. If you see this failure, + # update your kernel and/or seccomp syscall filter. + valid_flag_names = ('AT_NO_AUTOMOUNT', 'AT_STATX_SYNC_AS_STAT', + 'AT_STATX_FORCE_SYNC', 'AT_STATX_DONT_SYNC') + for flag_name in valid_flag_names: + flag = getattr(posix, flag_name) + with self.subTest(msg=flag_name, flags=flag): + posix.statx(os_helper.TESTFN, posix.STATX_BASIC_STATS, + flags=flag) + + # These flags are not exposed to Python because their functionality is + # implemented via kwargs instead. + kwarg_equivalent_flags = ( + (0x0100, 'AT_SYMLINK_NOFOLLOW', 'follow_symlinks'), + (0x0400, 'AT_SYMLINK_FOLLOW', 'follow_symlinks'), + (0x1000, 'AT_EMPTY_PATH', 'dir_fd'), + ) + for flag, flag_name, kwarg_name in kwarg_equivalent_flags: + with self.subTest(msg=flag_name, flags=flag): + with self.assertRaisesRegex(ValueError, kwarg_name): + posix.statx(os_helper.TESTFN, posix.STATX_BASIC_STATS, + flags=flag) + + with self.subTest(msg="AT_STATX_FORCE_SYNC | AT_STATX_DONT_SYNC"): + with self.assertRaises(OSError) as ctx: + flags = posix.AT_STATX_FORCE_SYNC | posix.AT_STATX_DONT_SYNC + posix.statx(os_helper.TESTFN, posix.STATX_BASIC_STATS, + flags=flags) + self.assertEqual(ctx.exception.errno, errno.EINVAL) @unittest.skipUnless(hasattr(posix, 'mkfifo'), "don't have mkfifo()") def test_mkfifo(self): @@ -1629,33 +1672,42 @@ def test_chown_dir_fd(self): with self.prepare_file() as (dir_fd, name, fullname): posix.chown(name, os.getuid(), os.getgid(), dir_fd=dir_fd) - @unittest.skipUnless(os.stat in os.supports_dir_fd, "test needs dir_fd support in os.stat()") - def test_stat_dir_fd(self): + def check_statlike_dir_fd(self, func): with self.prepare() as (dir_fd, name, fullname): with open(fullname, 'w') as outfile: outfile.write("testline\n") self.addCleanup(posix.unlink, fullname) - s1 = posix.stat(fullname) - s2 = posix.stat(name, dir_fd=dir_fd) - self.assertEqual(s1, s2) - s2 = posix.stat(fullname, dir_fd=None) - self.assertEqual(s1, s2) + s1 = func(fullname) + s2 = func(name, dir_fd=dir_fd) + self.assertEqual((s1.st_dev, s1.st_ino), (s2.st_dev, s2.st_ino)) + s2 = func(fullname, dir_fd=None) + self.assertEqual((s1.st_dev, s1.st_ino), (s2.st_dev, s2.st_ino)) self.assertRaisesRegex(TypeError, 'should be integer or None, not', - posix.stat, name, dir_fd=posix.getcwd()) + func, name, dir_fd=posix.getcwd()) self.assertRaisesRegex(TypeError, 'should be integer or None, not', - posix.stat, name, dir_fd=float(dir_fd)) + func, name, dir_fd=float(dir_fd)) self.assertRaises(OverflowError, - posix.stat, name, dir_fd=10**20) + func, name, dir_fd=10**20) for fd in False, True: with self.assertWarnsRegex(RuntimeWarning, 'bool is used as a file descriptor') as cm: with self.assertRaises(OSError): - posix.stat('nonexisting', dir_fd=fd) + func('nonexisting', dir_fd=fd) self.assertEqual(cm.filename, __file__) + @unittest.skipUnless(os.stat in os.supports_dir_fd, "test needs dir_fd support in os.stat()") + def test_stat_dir_fd(self): + self.check_statlike_dir_fd(posix.stat) + + @unittest.skipUnless(hasattr(posix, 'statx'), "test needs os.statx()") + def test_statx_dir_fd(self): + def func(path, **kwargs): + return posix.statx(path, os.STATX_INO, **kwargs) + self.check_statlike_dir_fd(func) + @unittest.skipUnless(os.utime in os.supports_dir_fd, "test needs dir_fd support in os.utime()") def test_utime_dir_fd(self): with self.prepare_file() as (dir_fd, name, fullname): diff --git a/Misc/ACKS b/Misc/ACKS index 0812b229e0a..2dc513829a2 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -210,6 +210,7 @@ Médéric Boquien Matias Bordese Jonas Borgström Jurjen Bos +Jeffrey Bosboom Peter Bosch Dan Boswell Eric Bouck diff --git a/Misc/NEWS.d/next/Library/2025-09-18-21-25-41.gh-issue-83714.TQjDWZ.rst b/Misc/NEWS.d/next/Library/2025-09-18-21-25-41.gh-issue-83714.TQjDWZ.rst new file mode 100644 index 00000000000..7229a361147 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-18-21-25-41.gh-issue-83714.TQjDWZ.rst @@ -0,0 +1,2 @@ +Implement :func:`os.statx` on Linux kernel versions 4.11 and later with +glibc versions 2.28 and later. Contributed by Jeffrey Bosboom. diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 71f87ac8ec7..7bd3d0ebfaa 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -186,6 +186,140 @@ exit: return return_value; } +#if defined(HAVE_STATX) + +PyDoc_STRVAR(os_statx__doc__, +"statx($module, /, path, mask, *, flags=0, dir_fd=None,\n" +" follow_symlinks=True)\n" +"--\n" +"\n" +"Perform a statx system call on the given path.\n" +"\n" +" path\n" +" Path to be examined; can be string, bytes, a path-like object or\n" +" open-file-descriptor int.\n" +" mask\n" +" A bitmask of STATX_* constants defining the requested information.\n" +" flags\n" +" A bitmask of AT_NO_AUTOMOUNT and/or AT_STATX_* flags.\n" +" dir_fd\n" +" If not None, it should be a file descriptor open to a directory,\n" +" and path should be a relative string; path will then be relative to\n" +" that directory.\n" +" follow_symlinks\n" +" If False, and the last element of the path is a symbolic link,\n" +" statx will examine the symbolic link itself instead of the file\n" +" the link points to.\n" +"\n" +"It\'s an error to use dir_fd or follow_symlinks when specifying path as\n" +" an open file descriptor."); + +#define OS_STATX_METHODDEF \ + {"statx", _PyCFunction_CAST(os_statx), METH_FASTCALL|METH_KEYWORDS, os_statx__doc__}, + +static PyObject * +os_statx_impl(PyObject *module, path_t *path, unsigned int mask, int flags, + int dir_fd, int follow_symlinks); + +static PyObject * +os_statx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 5 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(path), &_Py_ID(mask), &_Py_ID(flags), &_Py_ID(dir_fd), &_Py_ID(follow_symlinks), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"path", "mask", "flags", "dir_fd", "follow_symlinks", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "statx", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[5]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + path_t path = PATH_T_INITIALIZE_P("statx", "path", 0, 0, 0, 1); + unsigned int mask; + int flags = 0; + int dir_fd = DEFAULT_DIR_FD; + int follow_symlinks = 1; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!path_converter(args[0], &path)) { + goto exit; + } + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &mask, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[2]) { + flags = PyLong_AsInt(args[2]); + if (flags == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[3]) { + if (!dir_fd_converter(args[3], &dir_fd)) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + follow_symlinks = PyObject_IsTrue(args[4]); + if (follow_symlinks < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = os_statx_impl(module, &path, mask, flags, dir_fd, follow_symlinks); + +exit: + /* Cleanup for path */ + path_cleanup(&path); + + return return_value; +} + +#endif /* defined(HAVE_STATX) */ + PyDoc_STRVAR(os_access__doc__, "access($module, /, path, mode, *, dir_fd=None, effective_ids=False,\n" " follow_symlinks=True)\n" @@ -12793,6 +12927,10 @@ exit: #endif /* defined(__EMSCRIPTEN__) */ +#ifndef OS_STATX_METHODDEF + #define OS_STATX_METHODDEF +#endif /* !defined(OS_STATX_METHODDEF) */ + #ifndef OS_TTYNAME_METHODDEF #define OS_TTYNAME_METHODDEF #endif /* !defined(OS_TTYNAME_METHODDEF) */ @@ -13472,4 +13610,4 @@ exit: #ifndef OS__EMSCRIPTEN_LOG_METHODDEF #define OS__EMSCRIPTEN_LOG_METHODDEF #endif /* !defined(OS__EMSCRIPTEN_LOG_METHODDEF) */ -/*[clinic end generated code: output=67f0df7cd5a7de20 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=44f7a1a16dad2e08 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 38ddc3ec4ff..2dd43e50e79 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -40,6 +40,7 @@ // --- System includes ------------------------------------------------------ +#include // offsetof() #include // ctermid() #include // system() @@ -408,6 +409,31 @@ extern char *ctermid_r(char *); # define STRUCT_STAT struct stat #endif +#ifdef HAVE_STATX +/* until we can assume glibc 2.28 at runtime, we must weakly link */ +# pragma weak statx +static const unsigned int _Py_STATX_KNOWN = (STATX_BASIC_STATS | STATX_BTIME +#ifdef STATX_MNT_ID + | STATX_MNT_ID +#endif +#ifdef STATX_DIOALIGN + | STATX_DIOALIGN +#endif +#ifdef STATX_MNT_ID_UNIQUE + | STATX_MNT_ID_UNIQUE +#endif +#ifdef STATX_SUBVOL + | STATX_SUBVOL +#endif +#ifdef STATX_WRITE_ATOMIC + | STATX_WRITE_ATOMIC +#endif +#ifdef STATX_DIO_READ_ALIGN + | STATX_DIO_READ_ALIGN +#endif + ); +#endif /* HAVE_STATX */ + #if !defined(EX_OK) && defined(EXIT_SUCCESS) # define EX_OK EXIT_SUCCESS @@ -1169,6 +1195,9 @@ typedef struct { #endif newfunc statresult_new_orig; PyObject *StatResultType; +#ifdef HAVE_STATX + PyObject *StatxResultType; +#endif PyObject *StatVFSResultType; PyObject *TerminalSizeType; PyObject *TimesResultType; @@ -2549,6 +2578,9 @@ _posix_clear(PyObject *module) Py_CLEAR(state->SchedParamType); #endif Py_CLEAR(state->StatResultType); +#ifdef HAVE_STATX + Py_CLEAR(state->StatxResultType); +#endif Py_CLEAR(state->StatVFSResultType); Py_CLEAR(state->TerminalSizeType); Py_CLEAR(state->TimesResultType); @@ -2574,6 +2606,9 @@ _posix_traverse(PyObject *module, visitproc visit, void *arg) Py_VISIT(state->SchedParamType); #endif Py_VISIT(state->StatResultType); +#ifdef HAVE_STATX + Py_VISIT(state->StatxResultType); +#endif Py_VISIT(state->StatVFSResultType); Py_VISIT(state->TerminalSizeType); Py_VISIT(state->TimesResultType); @@ -2594,12 +2629,44 @@ _posix_free(void *module) _posix_clear((PyObject *)module); } + +#define SEC_TO_NS (1000000000LL) +static PyObject * +stat_nanosecond_timestamp(_posixstate *state, time_t sec, unsigned long nsec) +{ + /* 1677-09-21 00:12:44 to 2262-04-11 23:47:15 UTC inclusive */ + if ((LLONG_MIN/SEC_TO_NS) <= sec && sec <= (LLONG_MAX/SEC_TO_NS - 1)) { + return PyLong_FromLongLong(sec * SEC_TO_NS + nsec); + } + else { + PyObject *ns_total = NULL; + PyObject *s_in_ns = NULL; + PyObject *s = _PyLong_FromTime_t(sec); + PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec); + if (s == NULL || ns_fractional == NULL) { + goto exit; + } + + s_in_ns = PyNumber_Multiply(s, state->billion); + if (s_in_ns == NULL) { + goto exit; + } + + ns_total = PyNumber_Add(s_in_ns, ns_fractional); + + exit: + Py_XDECREF(s); + Py_XDECREF(ns_fractional); + Py_XDECREF(s_in_ns); + return ns_total; + } +} + static int fill_time(_posixstate *state, PyObject *v, int s_index, int f_index, int ns_index, time_t sec, unsigned long nsec) { assert(!PyErr_Occurred()); -#define SEC_TO_NS (1000000000LL) assert(nsec < SEC_TO_NS); if (s_index >= 0) { @@ -2618,50 +2685,18 @@ fill_time(_posixstate *state, PyObject *v, int s_index, int f_index, PyStructSequence_SET_ITEM(v, f_index, float_s); } - int res = -1; if (ns_index >= 0) { - /* 1677-09-21 00:12:44 to 2262-04-11 23:47:15 UTC inclusive */ - if ((LLONG_MIN/SEC_TO_NS) <= sec && sec <= (LLONG_MAX/SEC_TO_NS - 1)) { - PyObject *ns_total = PyLong_FromLongLong(sec * SEC_TO_NS + nsec); - if (ns_total == NULL) { - return -1; - } - PyStructSequence_SET_ITEM(v, ns_index, ns_total); - assert(!PyErr_Occurred()); - res = 0; - } - else { - PyObject *s_in_ns = NULL; - PyObject *ns_total = NULL; - PyObject *s = _PyLong_FromTime_t(sec); - PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec); - if (s == NULL || ns_fractional == NULL) { - goto exit; - } - - s_in_ns = PyNumber_Multiply(s, state->billion); - if (s_in_ns == NULL) { - goto exit; - } - - ns_total = PyNumber_Add(s_in_ns, ns_fractional); - if (ns_total == NULL) { - goto exit; - } - PyStructSequence_SET_ITEM(v, ns_index, ns_total); - assert(!PyErr_Occurred()); - res = 0; - - exit: - Py_XDECREF(s); - Py_XDECREF(ns_fractional); - Py_XDECREF(s_in_ns); + PyObject *ns_total = stat_nanosecond_timestamp(state, sec, nsec); + if (ns_total == NULL) { + return -1; } + PyStructSequence_SET_ITEM(v, ns_index, ns_total); } - return res; - #undef SEC_TO_NS + assert(!PyErr_Occurred()); + return 0; } +#undef SEC_TO_NS #ifdef MS_WINDOWS static PyObject* @@ -3276,6 +3311,307 @@ os_lstat_impl(PyObject *module, path_t *path, int dir_fd) } +#ifdef HAVE_STATX +typedef struct { + PyObject_HEAD + double atime_sec, btime_sec, ctime_sec, mtime_sec; + dev_t rdev, dev; + struct statx stx; +} Py_statx_result; + +#define M(attr, type, offset, doc) \ + {attr, type, offset, Py_READONLY, PyDoc_STR(doc)} +#define MM(attr, type, member, doc) \ + M(#attr, type, offsetof(Py_statx_result, stx.stx_##member), doc) +#define MX(attr, type, member, doc) \ + M(#attr, type, offsetof(Py_statx_result, member), doc) + +static PyMemberDef pystatx_result_members[] = { + MM(stx_mask, Py_T_UINT, mask, "member validity mask"), + MM(st_blksize, Py_T_UINT, blksize, "blocksize for filesystem I/O"), + MM(stx_attributes, Py_T_ULONGLONG, attributes, "Linux inode attribute bits"), + MM(st_nlink, Py_T_UINT, nlink, "number of hard links"), + MM(st_uid, Py_T_UINT, uid, "user ID of owner"), + MM(st_gid, Py_T_UINT, gid, "group ID of owner"), + MM(st_mode, Py_T_USHORT, mode, "protection bits"), + MM(st_ino, Py_T_ULONGLONG, ino, "inode"), + MM(st_size, Py_T_ULONGLONG, size, "total size, in bytes"), + MM(st_blocks, Py_T_ULONGLONG, blocks, "number of blocks allocated"), + MM(stx_attributes_mask, Py_T_ULONGLONG, attributes_mask, + "Mask of supported bits in stx_attributes"), + MX(st_atime, Py_T_DOUBLE, atime_sec, "time of last access"), + MX(st_birthtime, Py_T_DOUBLE, btime_sec, "time of creation"), + MX(st_ctime, Py_T_DOUBLE, ctime_sec, "time of last change"), + MX(st_mtime, Py_T_DOUBLE, mtime_sec, "time of last modification"), + MM(stx_rdev_major, Py_T_UINT, rdev_major, "represented device major number"), + MM(stx_rdev_minor, Py_T_UINT, rdev_minor, "represented device minor number"), + MX(st_rdev, Py_T_ULONGLONG, rdev, "device type (if inode device)"), + MM(stx_dev_major, Py_T_UINT, dev_major, "containing device major number"), + MM(stx_dev_minor, Py_T_UINT, dev_minor, "containing device minor number"), + MX(st_dev, Py_T_ULONGLONG, dev, "device"), +#ifdef STATX_MNT_ID + MM(stx_mnt_id, Py_T_ULONGLONG, mnt_id, "mount ID"), +#endif +#ifdef STATX_DIOALIGN + MM(stx_dio_mem_align, Py_T_UINT, dio_mem_align, + "direct I/O memory buffer alignment"), + MM(stx_dio_offset_align, Py_T_UINT, dio_offset_align, + "direct I/O file offset alignment"), +#endif +#ifdef STATX_SUBVOL + MM(stx_subvol, Py_T_ULONGLONG, subvol, "subvolume ID"), +#endif +#ifdef STATX_WRITE_ATOMIC + MM(stx_atomic_write_unit_min, Py_T_UINT, atomic_write_unit_min, + "minimum size for direct I/O with torn-write protection"), + MM(stx_atomic_write_unit_max, Py_T_UINT, atomic_write_unit_max, + "maximum size for direct I/O with torn-write protection"), + MM(stx_atomic_write_unit_max_opt, Py_T_UINT, atomic_write_unit_max_opt, + "maximum optimized size for direct I/O with torn-write protection"), + MM(stx_atomic_write_segments_max, Py_T_UINT, atomic_write_segments_max, + "maximum iovecs for direct I/O with torn-write protection"), +#endif +#ifdef STATX_DIO_READ_ALIGN + MM(stx_dio_read_offset_align, Py_T_UINT, dio_read_offset_align, + "direct I/O file offset alignment for reads"), +#endif + {NULL}, +}; + +#undef MX +#undef MM +#undef M + +static PyObject * +pystatx_result_get_nsec(PyObject *op, void *context) +{ + uint16_t offset = (uintptr_t)context; + struct statx_timestamp *ts = (void*)op + offset; + _posixstate *state = PyType_GetModuleState(Py_TYPE(op)); + assert(state != NULL); + return stat_nanosecond_timestamp(state, ts->tv_sec, ts->tv_nsec); +} + +/* The low 16 bits of the context pointer are the offset from the start of + Py_statx_result to the struct statx member. */ +#define GM(attr, type, member, doc) \ + {#attr, pystatx_result_get_##type, NULL, PyDoc_STR(doc), \ + (void *)(offsetof(Py_statx_result, stx.stx_##member))} + +static PyGetSetDef pystatx_result_getset[] = { + GM(st_atime_ns, nsec, atime, "time of last access in nanoseconds"), + GM(st_birthtime_ns, nsec, btime, "time of creation in nanoseconds"), + GM(st_ctime_ns, nsec, ctime, "time of last change in nanoseconds"), + GM(st_mtime_ns, nsec, mtime, "time of last modification in nanoseconds"), + {NULL}, +}; + +#undef GM + +static PyObject * +pystatx_result_repr(PyObject *op) +{ + PyUnicodeWriter *writer = PyUnicodeWriter_Create(0); + if (writer == NULL) { + return NULL; + } +#define WRITE_ASCII(s) \ + do { \ + if (PyUnicodeWriter_WriteASCII(writer, s, strlen(s)) < 0) { \ + goto error; \ + } \ + } while (0) + + WRITE_ASCII("os.statx_result("); + + for (size_t i = 0; i < Py_ARRAY_LENGTH(pystatx_result_members) - 1; ++i) { + if (i > 0) { + WRITE_ASCII(", "); + } + + PyMemberDef *d = &pystatx_result_members[i]; + WRITE_ASCII(d->name); + WRITE_ASCII("="); + + PyObject *o = PyMember_GetOne((const char *)op, d); + if (o == NULL) { + goto error; + } + if (PyUnicodeWriter_WriteRepr(writer, o) < 0) { + Py_DECREF(o); + goto error; + } + Py_DECREF(o); + } + + if (Py_ARRAY_LENGTH(pystatx_result_members) > 1 + && Py_ARRAY_LENGTH(pystatx_result_getset) > 1) { + WRITE_ASCII(", "); + } + + for (size_t i = 0; i < Py_ARRAY_LENGTH(pystatx_result_getset) - 1; ++i) { + if (i > 0) { + WRITE_ASCII(", "); + } + + PyGetSetDef *d = &pystatx_result_getset[i]; + WRITE_ASCII(d->name); + WRITE_ASCII("="); + + PyObject *o = d->get(op, d->closure); + if (o == NULL) { + goto error; + } + if (PyUnicodeWriter_WriteRepr(writer, o) < 0) { + Py_DECREF(o); + goto error; + } + Py_DECREF(o); + } + + WRITE_ASCII(")"); + return PyUnicodeWriter_Finish(writer); +#undef WRITE_ASCII + +error: + PyUnicodeWriter_Discard(writer); + return NULL; +} + +static int +pystatx_result_traverse(PyObject *self, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(self)); + return 0; +} + +static void +pystatx_result_dealloc(PyObject *op) +{ + Py_statx_result *self = (Py_statx_result *) op; + PyTypeObject *tp = Py_TYPE(self); + PyObject_GC_UnTrack(self); + tp->tp_free(self); + Py_DECREF(tp); +} + +static PyType_Slot pystatx_result_slots[] = { + {Py_tp_repr, pystatx_result_repr}, + {Py_tp_traverse, pystatx_result_traverse}, + {Py_tp_dealloc, pystatx_result_dealloc}, + {Py_tp_members, pystatx_result_members}, + {Py_tp_getset, pystatx_result_getset}, + {0, NULL}, +}; + +static PyType_Spec pystatx_result_spec = { + .name = "os.statx_result", + .basicsize = sizeof(Py_statx_result), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION, + .slots = pystatx_result_slots, +}; + +/*[clinic input] + +os.statx + + path : path_t(allow_fd=True) + Path to be examined; can be string, bytes, a path-like object or + open-file-descriptor int. + + mask: unsigned_int(bitwise=True) + A bitmask of STATX_* constants defining the requested information. + + * + + flags: int = 0 + A bitmask of AT_NO_AUTOMOUNT and/or AT_STATX_* flags. + + dir_fd : dir_fd = None + If not None, it should be a file descriptor open to a directory, + and path should be a relative string; path will then be relative to + that directory. + + follow_symlinks: bool = True + If False, and the last element of the path is a symbolic link, + statx will examine the symbolic link itself instead of the file + the link points to. + +Perform a statx system call on the given path. + +It's an error to use dir_fd or follow_symlinks when specifying path as + an open file descriptor. + +[clinic start generated code]*/ + +static PyObject * +os_statx_impl(PyObject *module, path_t *path, unsigned int mask, int flags, + int dir_fd, int follow_symlinks) +/*[clinic end generated code: output=e3765979ac6fe15b input=f0116380c5dc4f2f]*/ +{ + if (path_and_dir_fd_invalid("statx", path, dir_fd) || + dir_fd_and_fd_invalid("statx", dir_fd, path->fd) || + fd_and_follow_symlinks_invalid("statx", path->fd, follow_symlinks)) { + return NULL; + } + + /* reject flags covered by kwargs, but allow unknown flags that may be + future AT_STATX_* extensions */ + if (flags & (AT_SYMLINK_NOFOLLOW | AT_SYMLINK_FOLLOW)) { + PyErr_Format(PyExc_ValueError, + "use follow_symlinks kwarg instead of AT_SYMLINK_* flag"); + return NULL; + } + if (flags & AT_EMPTY_PATH) { + PyErr_Format(PyExc_ValueError, + "use dir_fd kwarg instead of AT_EMPTY_PATH flag"); + return NULL; + } + + /* Future bits may refer to members beyond the current size of struct + statx, so we need to mask them off to prevent memory corruption. */ + mask &= _Py_STATX_KNOWN; + + _posixstate *state = get_posix_state(module); + PyTypeObject *tp = (PyTypeObject *)state->StatxResultType; + Py_statx_result *v = (Py_statx_result *)tp->tp_alloc(tp, 0); + if (v == NULL) { + return NULL; + } + + int result; + Py_BEGIN_ALLOW_THREADS + if (path->fd != -1) { + result = statx(path->fd, "", flags | AT_EMPTY_PATH, mask, &v->stx); + } + else { + result = statx(dir_fd, path->narrow, flags, mask, &v->stx); + } + Py_END_ALLOW_THREADS + + if (result != 0) { + Py_DECREF(v); + return path_error(path); + } + + v->atime_sec = ((double)v->stx.stx_atime.tv_sec + + 1e-9 * v->stx.stx_atime.tv_nsec); + v->btime_sec = ((double)v->stx.stx_btime.tv_sec + + 1e-9 * v->stx.stx_btime.tv_nsec); + v->ctime_sec = ((double)v->stx.stx_ctime.tv_sec + + 1e-9 * v->stx.stx_ctime.tv_nsec); + v->mtime_sec = ((double)v->stx.stx_mtime.tv_sec + + 1e-9 * v->stx.stx_mtime.tv_nsec); + v->rdev = makedev(v->stx.stx_rdev_major, v->stx.stx_rdev_minor); + v->dev = makedev(v->stx.stx_dev_major, v->stx.stx_dev_minor); + + assert(!PyErr_Occurred()); + return (PyObject *)v; +} +#endif /* HAVE_STATX */ + + /*[clinic input] os.access -> bool @@ -17051,6 +17387,7 @@ os__emscripten_log_impl(PyObject *module, const char *arg) static PyMethodDef posix_methods[] = { OS_STAT_METHODDEF + OS_STATX_METHODDEF OS_ACCESS_METHODDEF OS_TTYNAME_METHODDEF OS_CHDIR_METHODDEF @@ -17897,6 +18234,49 @@ all_ins(PyObject *m) if (PyModule_Add(m, "NODEV", _PyLong_FromDev(NODEV))) return -1; #endif +#ifdef AT_NO_AUTOMOUNT + if (PyModule_AddIntMacro(m, AT_NO_AUTOMOUNT)) return -1; +#endif + +#ifdef HAVE_STATX + if (PyModule_AddIntMacro(m, STATX_TYPE)) return -1; + if (PyModule_AddIntMacro(m, STATX_MODE)) return -1; + if (PyModule_AddIntMacro(m, STATX_NLINK)) return -1; + if (PyModule_AddIntMacro(m, STATX_UID)) return -1; + if (PyModule_AddIntMacro(m, STATX_GID)) return -1; + if (PyModule_AddIntMacro(m, STATX_ATIME)) return -1; + if (PyModule_AddIntMacro(m, STATX_MTIME)) return -1; + if (PyModule_AddIntMacro(m, STATX_CTIME)) return -1; + if (PyModule_AddIntMacro(m, STATX_INO)) return -1; + if (PyModule_AddIntMacro(m, STATX_SIZE)) return -1; + if (PyModule_AddIntMacro(m, STATX_BLOCKS)) return -1; + if (PyModule_AddIntMacro(m, STATX_BASIC_STATS)) return -1; + if (PyModule_AddIntMacro(m, STATX_BTIME)) return -1; +#ifdef STATX_MNT_ID + if (PyModule_AddIntMacro(m, STATX_MNT_ID)) return -1; +#endif +#ifdef STATX_DIOALIGN + if (PyModule_AddIntMacro(m, STATX_DIOALIGN)) return -1; +#endif +#ifdef STATX_MNT_ID_UNIQUE + if (PyModule_AddIntMacro(m, STATX_MNT_ID_UNIQUE)) return -1; +#endif +#ifdef STATX_SUBVOL + if (PyModule_AddIntMacro(m, STATX_SUBVOL)) return -1; +#endif +#ifdef STATX_WRITE_ATOMIC + if (PyModule_AddIntMacro(m, STATX_WRITE_ATOMIC)) return -1; +#endif +#ifdef STATX_DIO_READ_ALIGN + if (PyModule_AddIntMacro(m, STATX_DIO_READ_ALIGN)) return -1; +#endif + /* STATX_ALL intentionally omitted because it is deprecated */ + if (PyModule_AddIntMacro(m, AT_STATX_SYNC_AS_STAT)) return -1; + if (PyModule_AddIntMacro(m, AT_STATX_FORCE_SYNC)) return -1; + if (PyModule_AddIntMacro(m, AT_STATX_DONT_SYNC)) return -1; + /* STATX_ATTR_* constants are in the stat module */ +#endif /* HAVE_STATX */ + #if defined(__APPLE__) if (PyModule_AddIntConstant(m, "_COPYFILE_DATA", COPYFILE_DATA)) return -1; if (PyModule_AddIntConstant(m, "_COPYFILE_STAT", COPYFILE_STAT)) return -1; @@ -18168,6 +18548,24 @@ posixmodule_exec(PyObject *m) } #endif +#ifdef HAVE_STATX + if (statx == NULL) { + PyObject* dct = PyModule_GetDict(m); + if (dct == NULL) { + return -1; + } + if (PyDict_PopString(dct, "statx", NULL) < 0) { + return -1; + } + } + else { + state->StatxResultType = PyType_FromModuleAndSpec(m, &pystatx_result_spec, NULL); + if (PyModule_AddObjectRef(m, "statx_result", state->StatxResultType) < 0) { + return -1; + } + } +#endif + /* Initialize environ dictionary */ if (PyModule_Add(m, "environ", convertenviron()) != 0) { return -1; diff --git a/configure b/configure index 9757b3419d3..28005cd1924 100755 --- a/configure +++ b/configure @@ -20191,6 +20191,12 @@ if test "x$ac_cv_func_splice" = xyes then : printf "%s\n" "#define HAVE_SPLICE 1" >>confdefs.h +fi +ac_fn_c_check_func "$LINENO" "statx" "ac_cv_func_statx" +if test "x$ac_cv_func_statx" = xyes +then : + printf "%s\n" "#define HAVE_STATX 1" >>confdefs.h + fi ac_fn_c_check_func "$LINENO" "strftime" "ac_cv_func_strftime" if test "x$ac_cv_func_strftime" = xyes diff --git a/configure.ac b/configure.ac index f244e0b71a6..d20f6f8c40a 100644 --- a/configure.ac +++ b/configure.ac @@ -5251,7 +5251,7 @@ AC_CHECK_FUNCS([ \ setitimer setlocale setpgid setpgrp setpriority setregid setresgid \ setresuid setreuid setsid setuid setvbuf shutdown sigaction sigaltstack \ sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ - sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ + sigwaitinfo snprintf splice statx strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ tmpnam tmpnam_r truncate ttyname_r umask uname unlinkat unlockpt utimensat utimes vfork \ wait wait3 wait4 waitid waitpid wcscoll wcsftime wcsxfrm wmemcmp writev \ diff --git a/pyconfig.h.in b/pyconfig.h.in index 72870411bc0..611408d88f0 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1285,6 +1285,9 @@ /* Define to 1 if you have the 'statvfs' function. */ #undef HAVE_STATVFS +/* Define to 1 if you have the 'statx' function. */ +#undef HAVE_STATX + /* Define if you have struct stat.st_mtim.tv_nsec */ #undef HAVE_STAT_TV_NSEC From 32c264982ec67460642b907dabc3304019318291 Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Wed, 15 Oct 2025 18:48:21 +0500 Subject: [PATCH 172/373] gh-140061: Use `_PyObject_IsUniquelyReferenced()` to check if objects are uniquely referenced (gh-140062) The previous `Py_REFCNT(x) == 1` checks can have data races in the free threaded build. `_PyObject_IsUniquelyReferenced(x)` is a more conservative check that is safe in the free threaded build and is identical to `Py_REFCNT(x) == 1` in the default GIL-enabled build. --- ...025-10-15-00-21-40.gh-issue-140061.J0XeDV.rst | 2 ++ Modules/_elementtree.c | 7 ++++--- Modules/_functoolsmodule.c | 4 ++-- Modules/itertoolsmodule.c | 14 +++++++------- Objects/bytesobject.c | 4 ++-- Objects/dictobject.c | 16 ++-------------- Objects/longobject.c | 7 ++++--- Objects/setobject.c | 4 ++-- Objects/tupleobject.c | 4 ++-- Python/marshal.c | 3 ++- 10 files changed, 29 insertions(+), 36 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-15-00-21-40.gh-issue-140061.J0XeDV.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-15-00-21-40.gh-issue-140061.J0XeDV.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-15-00-21-40.gh-issue-140061.J0XeDV.rst new file mode 100644 index 00000000000..7c3924195eb --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-15-00-21-40.gh-issue-140061.J0XeDV.rst @@ -0,0 +1,2 @@ +Fixing the checking of whether an object is uniquely referenced to ensure +free-threaded compatibility. Patch by Sergey Miryanov. diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 9263f14b57f..3173b52afb3 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -912,7 +912,7 @@ deepcopy(elementtreestate *st, PyObject *object, PyObject *memo) return Py_NewRef(object); } - if (Py_REFCNT(object) == 1) { + if (_PyObject_IsUniquelyReferenced(object)) { if (PyDict_CheckExact(object)) { PyObject *key, *value; Py_ssize_t pos = 0; @@ -2794,8 +2794,9 @@ treebuilder_handle_data(TreeBuilderObject* self, PyObject* data) self->data = Py_NewRef(data); } else { /* more than one item; use a list to collect items */ - if (PyBytes_CheckExact(self->data) && Py_REFCNT(self->data) == 1 && - PyBytes_CheckExact(data) && PyBytes_GET_SIZE(data) == 1) { + if (PyBytes_CheckExact(self->data) + && _PyObject_IsUniquelyReferenced(self->data) + && PyBytes_CheckExact(data) && PyBytes_GET_SIZE(data) == 1) { /* XXX this code path unused in Python 3? */ /* expat often generates single character data sections; handle the most common case by resizing the existing string... */ diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 257d5c6d536..44b1a302d49 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -291,7 +291,7 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw) if (kw == NULL) { pto->kw = PyDict_New(); } - else if (Py_REFCNT(kw) == 1) { + else if (_PyObject_IsUniquelyReferenced(kw)) { pto->kw = Py_NewRef(kw); } else { @@ -1076,7 +1076,7 @@ _functools_reduce_impl(PyObject *module, PyObject *func, PyObject *seq, for (;;) { PyObject *op2; - if (Py_REFCNT(args) > 1) { + if (!_PyObject_IsUniquelyReferenced(args)) { Py_DECREF(args); if ((args = PyTuple_New(2)) == NULL) goto Fail; diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 60ef6f9ff4c..8685eff8be6 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -376,7 +376,7 @@ pairwise_next(PyObject *op) } result = po->result; - if (Py_REFCNT(result) == 1) { + if (_PyObject_IsUniquelyReferenced(result)) { Py_INCREF(result); PyObject *last_old = PyTuple_GET_ITEM(result, 0); PyObject *last_new = PyTuple_GET_ITEM(result, 1); @@ -802,7 +802,7 @@ teedataobject_traverse(PyObject *op, visitproc visit, void * arg) static void teedataobject_safe_decref(PyObject *obj) { - while (obj && Py_REFCNT(obj) == 1) { + while (obj && _PyObject_IsUniquelyReferenced(obj)) { teedataobject *tmp = teedataobject_CAST(obj); PyObject *nextlink = tmp->nextlink; tmp->nextlink = NULL; @@ -2129,7 +2129,7 @@ product_next_lock_held(PyObject *op) Py_ssize_t *indices = lz->indices; /* Copy the previous result tuple or re-use it if available */ - if (Py_REFCNT(result) > 1) { + if (!_PyObject_IsUniquelyReferenced(result)) { PyObject *old_result = result; result = PyTuple_FromArray(_PyTuple_ITEMS(old_result), npools); if (result == NULL) @@ -2364,7 +2364,7 @@ combinations_next_lock_held(PyObject *op) } } else { /* Copy the previous result tuple or re-use it if available */ - if (Py_REFCNT(result) > 1) { + if (!_PyObject_IsUniquelyReferenced(result)) { PyObject *old_result = result; result = PyTuple_FromArray(_PyTuple_ITEMS(old_result), r); if (result == NULL) @@ -2618,7 +2618,7 @@ cwr_next(PyObject *op) } } else { /* Copy the previous result tuple or re-use it if available */ - if (Py_REFCNT(result) > 1) { + if (!_PyObject_IsUniquelyReferenced(result)) { PyObject *old_result = result; result = PyTuple_FromArray(_PyTuple_ITEMS(old_result), r); if (result == NULL) @@ -2879,7 +2879,7 @@ permutations_next(PyObject *op) goto empty; /* Copy the previous result tuple or re-use it if available */ - if (Py_REFCNT(result) > 1) { + if (!_PyObject_IsUniquelyReferenced(result)) { PyObject *old_result = result; result = PyTuple_FromArray(_PyTuple_ITEMS(old_result), r); if (result == NULL) @@ -3847,7 +3847,7 @@ zip_longest_next(PyObject *op) return NULL; if (lz->numactive == 0) return NULL; - if (Py_REFCNT(result) == 1) { + if (_PyObject_IsUniquelyReferenced(result)) { Py_INCREF(result); for (i=0 ; i < tuplesize ; i++) { it = PyTuple_GET_ITEM(lz->ittuple, i); diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index de8ab26db1e..9c807b3dd16 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -3171,7 +3171,7 @@ PyBytes_Concat(PyObject **pv, PyObject *w) return; } - if (Py_REFCNT(*pv) == 1 && PyBytes_CheckExact(*pv)) { + if (_PyObject_IsUniquelyReferenced(*pv) && PyBytes_CheckExact(*pv)) { /* Only one reference, so we can resize in place */ Py_ssize_t oldsize; Py_buffer wb; @@ -3256,7 +3256,7 @@ _PyBytes_Resize(PyObject **pv, Py_ssize_t newsize) Py_DECREF(v); return 0; } - if (Py_REFCNT(v) != 1) { + if (!_PyObject_IsUniquelyReferenced(v)) { if (oldsize < newsize) { *pv = _PyBytes_FromSize(newsize, 0); if (*pv) { diff --git a/Objects/dictobject.c b/Objects/dictobject.c index b95a4a8dc5e..73d4db4cac7 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -5667,22 +5667,10 @@ dictiter_iternext_threadsafe(PyDictObject *d, PyObject *self, #endif -static bool -has_unique_reference(PyObject *op) -{ -#ifdef Py_GIL_DISABLED - return (_Py_IsOwnedByCurrentThread(op) && - op->ob_ref_local == 1 && - _Py_atomic_load_ssize_relaxed(&op->ob_ref_shared) == 0); -#else - return Py_REFCNT(op) == 1; -#endif -} - static bool acquire_iter_result(PyObject *result) { - if (has_unique_reference(result)) { + if (_PyObject_IsUniquelyReferenced(result)) { Py_INCREF(result); return true; } @@ -5831,7 +5819,7 @@ dictreviter_iter_lock_held(PyDictObject *d, PyObject *self) } else if (Py_IS_TYPE(di, &PyDictRevIterItem_Type)) { result = di->di_result; - if (Py_REFCNT(result) == 1) { + if (_PyObject_IsUniquelyReferenced(result)) { PyObject *oldkey = PyTuple_GET_ITEM(result, 0); PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); PyTuple_SET_ITEM(result, 0, Py_NewRef(key)); diff --git a/Objects/longobject.c b/Objects/longobject.c index 5eb4063f861..298399210af 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -352,7 +352,7 @@ _PyLong_Negate(PyLongObject **x_p) PyLongObject *x; x = (PyLongObject *)*x_p; - if (Py_REFCNT(x) == 1) { + if (_PyObject_IsUniquelyReferenced((PyObject *)x)) { _PyLong_FlipSign(x); return; } @@ -5849,7 +5849,7 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg) assert(size_a >= 0); _PyLong_SetSignAndDigitCount(c, 1, size_a); } - else if (Py_REFCNT(a) == 1) { + else if (_PyObject_IsUniquelyReferenced((PyObject *)a)) { c = (PyLongObject*)Py_NewRef(a); } else { @@ -5863,7 +5863,8 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg) assert(size_a >= 0); _PyLong_SetSignAndDigitCount(d, 1, size_a); } - else if (Py_REFCNT(b) == 1 && size_a <= alloc_b) { + else if (_PyObject_IsUniquelyReferenced((PyObject *)b) + && size_a <= alloc_b) { d = (PyLongObject*)Py_NewRef(b); assert(size_a >= 0); _PyLong_SetSignAndDigitCount(d, 1, size_a); diff --git a/Objects/setobject.c b/Objects/setobject.c index d8340499be5..213bd821d8a 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -2444,7 +2444,7 @@ set_init(PyObject *so, PyObject *args, PyObject *kwds) if (!PyArg_UnpackTuple(args, Py_TYPE(self)->tp_name, 0, 1, &iterable)) return -1; - if (Py_REFCNT(self) == 1 && self->fill == 0) { + if (_PyObject_IsUniquelyReferenced((PyObject *)self) && self->fill == 0) { self->hash = -1; if (iterable == NULL) { return 0; @@ -2774,7 +2774,7 @@ int PySet_Add(PyObject *anyset, PyObject *key) { if (!PySet_Check(anyset) && - (!PyFrozenSet_Check(anyset) || Py_REFCNT(anyset) != 1)) { + (!PyFrozenSet_Check(anyset) || !_PyObject_IsUniquelyReferenced(anyset))) { PyErr_BadInternalCall(); return -1; } diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 94b7ae7e642..cd90b06d499 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -118,7 +118,7 @@ int PyTuple_SetItem(PyObject *op, Py_ssize_t i, PyObject *newitem) { PyObject **p; - if (!PyTuple_Check(op) || Py_REFCNT(op) != 1) { + if (!PyTuple_Check(op) || !_PyObject_IsUniquelyReferenced(op)) { Py_XDECREF(newitem); PyErr_BadInternalCall(); return -1; @@ -923,7 +923,7 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize) v = (PyTupleObject *) *pv; if (v == NULL || !Py_IS_TYPE(v, &PyTuple_Type) || - (Py_SIZE(v) != 0 && Py_REFCNT(v) != 1)) { + (Py_SIZE(v) != 0 && !_PyObject_IsUniquelyReferenced(*pv))) { *pv = 0; Py_XDECREF(v); PyErr_BadInternalCall(); diff --git a/Python/marshal.c b/Python/marshal.c index 4f444d4671c..8b56de65755 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -11,6 +11,7 @@ #include "pycore_code.h" // _PyCode_New() #include "pycore_hashtable.h" // _Py_hashtable_t #include "pycore_long.h" // _PyLong_IsZero() +#include "pycore_object.h" // _PyObject_IsUniquelyReferenced #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_setobject.h" // _PySet_NextEntryRef() #include "pycore_unicodeobject.h" // _PyUnicode_InternImmortal() @@ -388,7 +389,7 @@ w_ref(PyObject *v, char *flag, WFILE *p) * But we use TYPE_REF always for interned string, to PYC file stable * as possible. */ - if (Py_REFCNT(v) == 1 && + if (_PyObject_IsUniquelyReferenced(v) && !(PyUnicode_CheckExact(v) && PyUnicode_CHECK_INTERNED(v))) { return 0; } From a05aece5438409fd76f193c564a34e2ca63e278f Mon Sep 17 00:00:00 2001 From: yihong Date: Wed, 15 Oct 2025 21:49:55 +0800 Subject: [PATCH 173/373] gh-140080: Clear `atexit` callbacks when memory allocation fails during finalization (GH-140103) This fixes a regression introduced by GH-136004, in which finalization would hang while executing atexit handlers if the system was out of memory. --------- Signed-off-by: yihong0618 Co-authored-by: Peter Bierma Co-authored-by: Victor Stinner --- Lib/test/test_atexit.py | 35 ++++++++++++++++++- ...-10-14-20-18-31.gh-issue-140080.8ROjxW.rst | 1 + Modules/atexitmodule.c | 1 + 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-20-18-31.gh-issue-140080.8ROjxW.rst diff --git a/Lib/test/test_atexit.py b/Lib/test/test_atexit.py index 66142a108d5..8256ff183f2 100644 --- a/Lib/test/test_atexit.py +++ b/Lib/test/test_atexit.py @@ -1,9 +1,11 @@ import atexit import os +import subprocess import textwrap import unittest from test import support -from test.support import script_helper +from test.support import SuppressCrashReport, script_helper +from test.support import os_helper from test.support import threading_helper class GeneralTest(unittest.TestCase): @@ -189,6 +191,37 @@ def callback(): self.assertEqual(os.read(r, len(expected)), expected) os.close(r) + # Python built with Py_TRACE_REFS fail with a fatal error in + # _PyRefchain_Trace() on memory allocation error. + @unittest.skipIf(support.Py_TRACE_REFS, 'cannot test Py_TRACE_REFS build') + def test_atexit_with_low_memory(self): + # gh-140080: Test that setting low memory after registering an atexit + # callback doesn't cause an infinite loop during finalization. + code = textwrap.dedent(""" + import atexit + import _testcapi + + def callback(): + print("hello") + + atexit.register(callback) + # Simulate low memory condition + _testcapi.set_nomemory(0) + """) + + with os_helper.temp_dir() as temp_dir: + script = script_helper.make_script(temp_dir, 'test_atexit_script', code) + with SuppressCrashReport(): + with script_helper.spawn_python(script, + stderr=subprocess.PIPE) as proc: + proc.wait() + stdout = proc.stdout.read() + stderr = proc.stderr.read() + + self.assertIn(proc.returncode, (0, 1)) + self.assertNotIn(b"hello", stdout) + self.assertIn(b"MemoryError", stderr) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-20-18-31.gh-issue-140080.8ROjxW.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-20-18-31.gh-issue-140080.8ROjxW.rst new file mode 100644 index 00000000000..0ddcea57f9d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-20-18-31.gh-issue-140080.8ROjxW.rst @@ -0,0 +1 @@ +Fix hang during finalization when attempting to call :mod:`atexit` handlers under no memory. diff --git a/Modules/atexitmodule.c b/Modules/atexitmodule.c index 4b068967a6c..4536b03fbc4 100644 --- a/Modules/atexitmodule.c +++ b/Modules/atexitmodule.c @@ -112,6 +112,7 @@ atexit_callfuncs(struct atexit_state *state) { PyErr_FormatUnraisable("Exception ignored while " "copying atexit callbacks"); + atexit_cleanup(state); return; } From 0c66da8de45e7decdd06face441f7517eb8efe22 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Wed, 15 Oct 2025 14:59:12 +0100 Subject: [PATCH 174/373] gh-140137: Handle empty collections in profiling.sampling (#140154) --- Lib/profiling/sampling/sample.py | 11 +- Lib/pstats.py | 1 + .../test_profiling/test_sampling_profiler.py | 118 +++++++++++++----- 3 files changed, 97 insertions(+), 33 deletions(-) diff --git a/Lib/profiling/sampling/sample.py b/Lib/profiling/sampling/sample.py index e0d4583f0a1..7a0f739a542 100644 --- a/Lib/profiling/sampling/sample.py +++ b/Lib/profiling/sampling/sample.py @@ -642,9 +642,14 @@ def sample( if output_format == "pstats" and not filename: stats = pstats.SampledStats(collector).strip_dirs() - print_sampled_stats( - stats, sort, limit, show_summary, sample_interval_usec - ) + if not stats.stats: + print("No samples were collected.") + if mode == PROFILING_MODE_CPU: + print("This can happen in CPU mode when all threads are idle.") + else: + print_sampled_stats( + stats, sort, limit, show_summary, sample_interval_usec + ) else: collector.export(filename) diff --git a/Lib/pstats.py b/Lib/pstats.py index 079abd2c1b8..07ecda07796 100644 --- a/Lib/pstats.py +++ b/Lib/pstats.py @@ -154,6 +154,7 @@ def load_stats(self, arg): arg.create_stats() self.stats = arg.stats arg.stats = {} + return if not self.stats: raise TypeError("Cannot create or construct a %r object from %r" % (self.__class__, arg)) diff --git a/Lib/test/test_profiling/test_sampling_profiler.py b/Lib/test/test_profiling/test_sampling_profiler.py index a1342cafff1..59bc18b9bcf 100644 --- a/Lib/test/test_profiling/test_sampling_profiler.py +++ b/Lib/test/test_profiling/test_sampling_profiler.py @@ -11,6 +11,7 @@ import sys import tempfile import unittest +from collections import namedtuple from unittest import mock from profiling.sampling.pstats_collector import PstatsCollector @@ -84,6 +85,8 @@ def __repr__(self): "Test only runs on Linux, Windows and MacOS", ) +SubprocessInfo = namedtuple('SubprocessInfo', ['process', 'socket']) + @contextlib.contextmanager def test_subprocess(script): @@ -123,7 +126,7 @@ def test_subprocess(script): if response != b"ready": raise RuntimeError(f"Unexpected response from subprocess: {response}") - yield proc + yield SubprocessInfo(proc, client_socket) finally: if client_socket is not None: client_socket.close() @@ -1752,13 +1755,13 @@ def main_loop(): def test_sampling_basic_functionality(self): with ( - test_subprocess(self.test_script) as proc, + test_subprocess(self.test_script) as subproc, io.StringIO() as captured_output, mock.patch("sys.stdout", captured_output), ): try: profiling.sampling.sample.sample( - proc.pid, + subproc.process.pid, duration_sec=2, sample_interval_usec=1000, # 1ms show_summary=False, @@ -1782,7 +1785,7 @@ def test_sampling_with_pstats_export(self): ) self.addCleanup(close_and_unlink, pstats_out) - with test_subprocess(self.test_script) as proc: + with test_subprocess(self.test_script) as subproc: # Suppress profiler output when testing file export with ( io.StringIO() as captured_output, @@ -1790,7 +1793,7 @@ def test_sampling_with_pstats_export(self): ): try: profiling.sampling.sample.sample( - proc.pid, + subproc.process.pid, duration_sec=1, filename=pstats_out.name, sample_interval_usec=10000, @@ -1826,7 +1829,7 @@ def test_sampling_with_collapsed_export(self): self.addCleanup(close_and_unlink, collapsed_file) with ( - test_subprocess(self.test_script) as proc, + test_subprocess(self.test_script) as subproc, ): # Suppress profiler output when testing file export with ( @@ -1835,7 +1838,7 @@ def test_sampling_with_collapsed_export(self): ): try: profiling.sampling.sample.sample( - proc.pid, + subproc.process.pid, duration_sec=1, filename=collapsed_file.name, output_format="collapsed", @@ -1876,14 +1879,14 @@ def test_sampling_with_collapsed_export(self): def test_sampling_all_threads(self): with ( - test_subprocess(self.test_script) as proc, + test_subprocess(self.test_script) as subproc, # Suppress profiler output io.StringIO() as captured_output, mock.patch("sys.stdout", captured_output), ): try: profiling.sampling.sample.sample( - proc.pid, + subproc.process.pid, duration_sec=1, all_threads=True, sample_interval_usec=10000, @@ -1969,14 +1972,14 @@ def test_invalid_pid(self): profiling.sampling.sample.sample(-1, duration_sec=1) def test_process_dies_during_sampling(self): - with test_subprocess("import time; time.sleep(0.5); exit()") as proc: + with test_subprocess("import time; time.sleep(0.5); exit()") as subproc: with ( io.StringIO() as captured_output, mock.patch("sys.stdout", captured_output), ): try: profiling.sampling.sample.sample( - proc.pid, + subproc.process.pid, duration_sec=2, # Longer than process lifetime sample_interval_usec=50000, ) @@ -2018,17 +2021,17 @@ def test_invalid_output_format_with_mocked_profiler(self): ) def test_is_process_running(self): - with test_subprocess("import time; time.sleep(1000)") as proc: + with test_subprocess("import time; time.sleep(1000)") as subproc: try: - profiler = SampleProfiler(pid=proc.pid, sample_interval_usec=1000, all_threads=False) + profiler = SampleProfiler(pid=subproc.process.pid, sample_interval_usec=1000, all_threads=False) except PermissionError: self.skipTest( "Insufficient permissions to read the stack trace" ) self.assertTrue(profiler._is_process_running()) self.assertIsNotNone(profiler.unwinder.get_stack_trace()) - proc.kill() - proc.wait() + subproc.process.kill() + subproc.process.wait() self.assertRaises(ProcessLookupError, profiler.unwinder.get_stack_trace) # Exit the context manager to ensure the process is terminated @@ -2037,9 +2040,9 @@ def test_is_process_running(self): @unittest.skipUnless(sys.platform == "linux", "Only valid on Linux") def test_esrch_signal_handling(self): - with test_subprocess("import time; time.sleep(1000)") as proc: + with test_subprocess("import time; time.sleep(1000)") as subproc: try: - unwinder = _remote_debugging.RemoteUnwinder(proc.pid) + unwinder = _remote_debugging.RemoteUnwinder(subproc.process.pid) except PermissionError: self.skipTest( "Insufficient permissions to read the stack trace" @@ -2047,10 +2050,10 @@ def test_esrch_signal_handling(self): initial_trace = unwinder.get_stack_trace() self.assertIsNotNone(initial_trace) - proc.kill() + subproc.process.kill() # Wait for the process to die and try to get another trace - proc.wait() + subproc.process.wait() with self.assertRaises(ProcessLookupError): unwinder.get_stack_trace() @@ -2644,35 +2647,47 @@ def test_cpu_mode_integration_filtering(self): import time import threading +cpu_ready = threading.Event() + def idle_worker(): time.sleep(999999) def cpu_active_worker(): + cpu_ready.set() x = 1 while True: x += 1 def main(): -# Start both threads + # Start both threads idle_thread = threading.Thread(target=idle_worker) cpu_thread = threading.Thread(target=cpu_active_worker) idle_thread.start() cpu_thread.start() + + # Wait for CPU thread to be running, then signal test + cpu_ready.wait() + _test_sock.sendall(b"threads_ready") + idle_thread.join() cpu_thread.join() main() ''' - with test_subprocess(cpu_vs_idle_script) as proc: + with test_subprocess(cpu_vs_idle_script) as subproc: + # Wait for signal that threads are running + response = subproc.socket.recv(1024) + self.assertEqual(response, b"threads_ready") + with ( io.StringIO() as captured_output, mock.patch("sys.stdout", captured_output), ): try: profiling.sampling.sample.sample( - proc.pid, - duration_sec=0.5, + subproc.process.pid, + duration_sec=2.0, sample_interval_usec=5000, mode=1, # CPU mode show_summary=False, @@ -2690,8 +2705,8 @@ def main(): ): try: profiling.sampling.sample.sample( - proc.pid, - duration_sec=0.5, + subproc.process.pid, + duration_sec=2.0, sample_interval_usec=5000, mode=0, # Wall-clock mode show_summary=False, @@ -2716,6 +2731,37 @@ def main(): self.assertIn("cpu_active_worker", wall_mode_output) self.assertIn("idle_worker", wall_mode_output) + def test_cpu_mode_with_no_samples(self): + """Test that CPU mode handles no samples gracefully when no samples are collected.""" + # Mock a collector that returns empty stats + mock_collector = mock.MagicMock() + mock_collector.stats = {} + mock_collector.create_stats = mock.MagicMock() + + with ( + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + mock.patch("profiling.sampling.sample.PstatsCollector", return_value=mock_collector), + mock.patch("profiling.sampling.sample.SampleProfiler") as mock_profiler_class, + ): + mock_profiler = mock.MagicMock() + mock_profiler_class.return_value = mock_profiler + + profiling.sampling.sample.sample( + 12345, # dummy PID + duration_sec=0.5, + sample_interval_usec=5000, + mode=1, # CPU mode + show_summary=False, + all_threads=True, + ) + + output = captured_output.getvalue() + + # Should see the "No samples were collected" message + self.assertIn("No samples were collected", output) + self.assertIn("CPU mode", output) + class TestGilModeFiltering(unittest.TestCase): """Test GIL mode filtering functionality (--mode=gil).""" @@ -2852,34 +2898,46 @@ def test_gil_mode_integration_behavior(self): import time import threading +gil_ready = threading.Event() + def gil_releasing_work(): time.sleep(999999) def gil_holding_work(): + gil_ready.set() x = 1 while True: x += 1 def main(): -# Start both threads + # Start both threads idle_thread = threading.Thread(target=gil_releasing_work) cpu_thread = threading.Thread(target=gil_holding_work) idle_thread.start() cpu_thread.start() + + # Wait for GIL-holding thread to be running, then signal test + gil_ready.wait() + _test_sock.sendall(b"threads_ready") + idle_thread.join() cpu_thread.join() main() ''' - with test_subprocess(gil_test_script) as proc: + with test_subprocess(gil_test_script) as subproc: + # Wait for signal that threads are running + response = subproc.socket.recv(1024) + self.assertEqual(response, b"threads_ready") + with ( io.StringIO() as captured_output, mock.patch("sys.stdout", captured_output), ): try: profiling.sampling.sample.sample( - proc.pid, - duration_sec=0.5, + subproc.process.pid, + duration_sec=2.0, sample_interval_usec=5000, mode=2, # GIL mode show_summary=False, @@ -2897,7 +2955,7 @@ def main(): ): try: profiling.sampling.sample.sample( - proc.pid, + subproc.process.pid, duration_sec=0.5, sample_interval_usec=5000, mode=0, # Wall-clock mode From d3015873693eaa30f904deada5e46374a1d31f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurycy=20Paw=C5=82owski-Wiero=C5=84ski?= <5383+maurycy@users.noreply.github.com> Date: Wed, 15 Oct 2025 16:04:17 +0200 Subject: [PATCH 175/373] gh-140135: Use `PyBytesWriter` in `io.RawIOBase.readall()`; 4x faster (#140139) --- ...-10-15-02-26-50.gh-issue-140135.54JYfM.rst | 2 ++ Modules/_io/iobase.c | 30 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-15-02-26-50.gh-issue-140135.54JYfM.rst diff --git a/Misc/NEWS.d/next/Library/2025-10-15-02-26-50.gh-issue-140135.54JYfM.rst b/Misc/NEWS.d/next/Library/2025-10-15-02-26-50.gh-issue-140135.54JYfM.rst new file mode 100644 index 00000000000..8d5a76af909 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-15-02-26-50.gh-issue-140135.54JYfM.rst @@ -0,0 +1,2 @@ +Speed up :meth:`io.RawIOBase.readall` by using PyBytesWriter API (about 4x +faster) diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index aa373f6fdcb..acadbcc4d59 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -962,12 +962,10 @@ static PyObject * _io__RawIOBase_readall_impl(PyObject *self) /*[clinic end generated code: output=1987b9ce929425a0 input=688874141213622a]*/ { - int r; - PyObject *chunks = PyList_New(0); - PyObject *result; - - if (chunks == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(0); + if (writer == NULL) { return NULL; + } while (1) { PyObject *data = _PyObject_CallMethod(self, &_Py_ID(read), @@ -978,21 +976,21 @@ _io__RawIOBase_readall_impl(PyObject *self) if (_PyIO_trap_eintr()) { continue; } - Py_DECREF(chunks); + PyBytesWriter_Discard(writer); return NULL; } if (data == Py_None) { - if (PyList_GET_SIZE(chunks) == 0) { - Py_DECREF(chunks); + if (PyBytesWriter_GetSize(writer) == 0) { + PyBytesWriter_Discard(writer); return data; } Py_DECREF(data); break; } if (!PyBytes_Check(data)) { - Py_DECREF(chunks); Py_DECREF(data); PyErr_SetString(PyExc_TypeError, "read() should return bytes"); + PyBytesWriter_Discard(writer); return NULL; } if (PyBytes_GET_SIZE(data) == 0) { @@ -1000,16 +998,16 @@ _io__RawIOBase_readall_impl(PyObject *self) Py_DECREF(data); break; } - r = PyList_Append(chunks, data); - Py_DECREF(data); - if (r < 0) { - Py_DECREF(chunks); + if (PyBytesWriter_WriteBytes(writer, + PyBytes_AS_STRING(data), + PyBytes_GET_SIZE(data)) < 0) { + Py_DECREF(data); + PyBytesWriter_Discard(writer); return NULL; } + Py_DECREF(data); } - result = PyBytes_Join((PyObject *)&_Py_SINGLETON(bytes_empty), chunks); - Py_DECREF(chunks); - return result; + return PyBytesWriter_Finish(writer); } static PyObject * From 728d239e57b650c392517b7ae569b0eb05af826e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 15 Oct 2025 16:36:49 +0200 Subject: [PATCH 176/373] gh-140153: Fix Py_REFCNT() definition on limited C API 3.11-3.13 (#140158) --- Include/refcount.h | 2 ++ .../next/C_API/2025-10-15-15-59-59.gh-issue-140153.BO7sH4.rst | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/C_API/2025-10-15-15-59-59.gh-issue-140153.BO7sH4.rst diff --git a/Include/refcount.h b/Include/refcount.h index ba34461fefc..51346c7e519 100644 --- a/Include/refcount.h +++ b/Include/refcount.h @@ -114,6 +114,8 @@ PyAPI_FUNC(Py_ssize_t) Py_REFCNT(PyObject *ob); } #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000 # define Py_REFCNT(ob) _Py_REFCNT(_PyObject_CAST(ob)) + #else + # define Py_REFCNT(ob) _Py_REFCNT(ob) #endif #endif diff --git a/Misc/NEWS.d/next/C_API/2025-10-15-15-59-59.gh-issue-140153.BO7sH4.rst b/Misc/NEWS.d/next/C_API/2025-10-15-15-59-59.gh-issue-140153.BO7sH4.rst new file mode 100644 index 00000000000..502c48b6842 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2025-10-15-15-59-59.gh-issue-140153.BO7sH4.rst @@ -0,0 +1,2 @@ +Fix :c:func:`Py_REFCNT` definition on limited C API 3.11-3.13. Patch by +Victor Stinner. From 1624c646b045df15ba41d17ff03231978b80c3ff Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Wed, 15 Oct 2025 16:15:45 +0100 Subject: [PATCH 177/373] gh-140065: Lexical analysis: Correct note about leading zeros in floating point numbers (GH-140066) --- Doc/reference/lexical_analysis.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst index 0b0dba1a996..ea386706e8b 100644 --- a/Doc/reference/lexical_analysis.rst +++ b/Doc/reference/lexical_analysis.rst @@ -1308,8 +1308,8 @@ The parts are separated by a decimal point, ``.``:: 2.71828 4.0 -Unlike in integer literals, leading zeros are allowed in the numeric parts. -For example, ``077.010`` is legal, and denotes the same number as ``77.10``. +Unlike in integer literals, leading zeros are allowed. +For example, ``077.010`` is legal, and denotes the same number as ``77.01``. As in integer literals, single underscores may occur between digits to help readability:: From 5213f1b6840dfb6a6e7b3ea0b0d8edd62ddd7574 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 15 Oct 2025 17:38:34 +0200 Subject: [PATCH 178/373] gh-83714: Disable os.statx_result.stx_atomic_write_unit_max_opt (#140162) Fix building on Centos 9 x86-64 buildbot. --- Modules/posixmodule.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 2dd43e50e79..e2b7146237f 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -3366,11 +3366,13 @@ static PyMemberDef pystatx_result_members[] = { "minimum size for direct I/O with torn-write protection"), MM(stx_atomic_write_unit_max, Py_T_UINT, atomic_write_unit_max, "maximum size for direct I/O with torn-write protection"), - MM(stx_atomic_write_unit_max_opt, Py_T_UINT, atomic_write_unit_max_opt, - "maximum optimized size for direct I/O with torn-write protection"), MM(stx_atomic_write_segments_max, Py_T_UINT, atomic_write_segments_max, "maximum iovecs for direct I/O with torn-write protection"), #endif +#if 0 + MM(stx_atomic_write_unit_max_opt, Py_T_UINT, atomic_write_unit_max_opt, + "maximum optimized size for direct I/O with torn-write protection"), +#endif #ifdef STATX_DIO_READ_ALIGN MM(stx_dio_read_offset_align, Py_T_UINT, dio_read_offset_align, "direct I/O file offset alignment for reads"), From f673f0e7b49b305ba7608386a354da328066a664 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Wed, 15 Oct 2025 19:08:17 +0300 Subject: [PATCH 179/373] gh-139817: Attribute `__qualname__` is added to `TypeAliasType` (#139919) --- Doc/library/typing.rst | 16 +++- .../pycore_global_objects_fini_generated.h | 1 + Include/internal/pycore_global_strings.h | 1 + .../internal/pycore_runtime_init_generated.h | 1 + .../internal/pycore_unicodeobject_generated.h | 4 + Lib/test/test_type_aliases.py | 82 ++++++++++++++++--- ...-10-12-01-12-12.gh-issue-139817.PAn-8Z.rst | 2 + Objects/clinic/typevarobject.c.h | 25 ++++-- Objects/typevarobject.c | 45 +++++++--- 9 files changed, 144 insertions(+), 33 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-12-01-12-12.gh-issue-139817.PAn-8Z.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 279ae3ef820..4265c587195 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2243,7 +2243,7 @@ without the dedicated syntax, as documented below. .. versionadded:: 3.10 -.. class:: TypeAliasType(name, value, *, type_params=()) +.. class:: TypeAliasType(name, value, *, type_params=(), qualname=None) The type of type aliases created through the :keyword:`type` statement. @@ -2267,6 +2267,20 @@ without the dedicated syntax, as documented below. >>> Alias.__name__ 'Alias' + .. attribute:: __qualname__ + + The :term:`qualified name` of the type alias: + + .. doctest:: + + >>> class Class: + ... type Alias = int + ... + >>> Class.Alias.__qualname__ + 'Class.Alias' + + .. versionadded:: 3.15 + .. attribute:: __module__ The name of the module in which the type alias was defined:: diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index f7416c5ffc5..92ded14891a 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -1974,6 +1974,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ps1)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ps2)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(qid)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(qualname)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(query)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(queuetype)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(quotetabs)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index ca71c12836d..cd21b0847b7 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -697,6 +697,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(ps1) STRUCT_FOR_ID(ps2) STRUCT_FOR_ID(qid) + STRUCT_FOR_ID(qualname) STRUCT_FOR_ID(query) STRUCT_FOR_ID(queuetype) STRUCT_FOR_ID(quotetabs) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 72996db9f71..50d82d0a365 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -1972,6 +1972,7 @@ extern "C" { INIT_ID(ps1), \ INIT_ID(ps2), \ INIT_ID(qid), \ + INIT_ID(qualname), \ INIT_ID(query), \ INIT_ID(queuetype), \ INIT_ID(quotetabs), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index c4cf56ad7f1..b4d920154b6 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -2576,6 +2576,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(qualname); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(query); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Lib/test/test_type_aliases.py b/Lib/test/test_type_aliases.py index ee1791bc1d0..9ceee565764 100644 --- a/Lib/test/test_type_aliases.py +++ b/Lib/test/test_type_aliases.py @@ -8,6 +8,11 @@ Callable, TypeAliasType, TypeVar, TypeVarTuple, ParamSpec, Unpack, get_args, ) +type GlobalTypeAlias = int + +def get_type_alias(): + type TypeAliasInFunc = str + return TypeAliasInFunc class TypeParamsInvalidTest(unittest.TestCase): def test_name_collisions(self): @@ -70,6 +75,8 @@ def inner[B](self): class TypeParamsAliasValueTest(unittest.TestCase): + type TypeAliasInClass = dict + def test_alias_value_01(self): type TA1 = int @@ -142,33 +149,67 @@ def test_subscripting(self): self.assertIs(specialized2.__origin__, VeryGeneric) self.assertEqual(specialized2.__args__, (int, str, float, [bool, range])) + def test___name__(self): + type TypeAliasLocal = GlobalTypeAlias + + self.assertEqual(GlobalTypeAlias.__name__, 'GlobalTypeAlias') + self.assertEqual(get_type_alias().__name__, 'TypeAliasInFunc') + self.assertEqual(self.TypeAliasInClass.__name__, 'TypeAliasInClass') + self.assertEqual(TypeAliasLocal.__name__, 'TypeAliasLocal') + + with self.assertRaisesRegex( + AttributeError, + "readonly attribute", + ): + setattr(TypeAliasLocal, '__name__', 'TA') + + def test___qualname__(self): + type TypeAliasLocal = GlobalTypeAlias + + self.assertEqual(GlobalTypeAlias.__qualname__, + 'GlobalTypeAlias') + self.assertEqual(get_type_alias().__qualname__, + 'get_type_alias..TypeAliasInFunc') + self.assertEqual(self.TypeAliasInClass.__qualname__, + 'TypeParamsAliasValueTest.TypeAliasInClass') + self.assertEqual(TypeAliasLocal.__qualname__, + 'TypeParamsAliasValueTest.test___qualname__..TypeAliasLocal') + + with self.assertRaisesRegex( + AttributeError, + "readonly attribute", + ): + setattr(TypeAliasLocal, '__qualname__', 'TA') + def test_repr(self): type Simple = int - type VeryGeneric[T, *Ts, **P] = Callable[P, tuple[T, *Ts]] + self.assertEqual(repr(Simple), Simple.__qualname__) - self.assertEqual(repr(Simple), "Simple") - self.assertEqual(repr(VeryGeneric), "VeryGeneric") + type VeryGeneric[T, *Ts, **P] = Callable[P, tuple[T, *Ts]] + self.assertEqual(repr(VeryGeneric), VeryGeneric.__qualname__) + fullname = f"{VeryGeneric.__module__}.{VeryGeneric.__qualname__}" self.assertEqual(repr(VeryGeneric[int, bytes, str, [float, object]]), - "VeryGeneric[int, bytes, str, [float, object]]") + f"{fullname}[int, bytes, str, [float, object]]") self.assertEqual(repr(VeryGeneric[int, []]), - "VeryGeneric[int, []]") + f"{fullname}[int, []]") self.assertEqual(repr(VeryGeneric[int, [VeryGeneric[int], list[str]]]), - "VeryGeneric[int, [VeryGeneric[int], list[str]]]") + f"{fullname}[int, [{fullname}[int], list[str]]]") def test_recursive_repr(self): type Recursive = Recursive - self.assertEqual(repr(Recursive), "Recursive") + self.assertEqual(repr(Recursive), Recursive.__qualname__) type X = list[Y] type Y = list[X] - self.assertEqual(repr(X), "X") - self.assertEqual(repr(Y), "Y") + self.assertEqual(repr(X), X.__qualname__) + self.assertEqual(repr(Y), Y.__qualname__) type GenericRecursive[X] = list[X | GenericRecursive[X]] - self.assertEqual(repr(GenericRecursive), "GenericRecursive") - self.assertEqual(repr(GenericRecursive[int]), "GenericRecursive[int]") + self.assertEqual(repr(GenericRecursive), GenericRecursive.__qualname__) + fullname = f"{GenericRecursive.__module__}.{GenericRecursive.__qualname__}" + self.assertEqual(repr(GenericRecursive[int]), f"{fullname}[int]") self.assertEqual(repr(GenericRecursive[GenericRecursive[int]]), - "GenericRecursive[GenericRecursive[int]]") + f"{fullname}[{fullname}[int]]") def test_raising(self): type MissingName = list[_My_X] @@ -193,15 +234,25 @@ class TypeAliasConstructorTest(unittest.TestCase): def test_basic(self): TA = TypeAliasType("TA", int) self.assertEqual(TA.__name__, "TA") + self.assertEqual(TA.__qualname__, "TA") self.assertIs(TA.__value__, int) self.assertEqual(TA.__type_params__, ()) self.assertEqual(TA.__module__, __name__) + def test_with_qualname(self): + TA = TypeAliasType("TA", str, qualname="Class.TA") + self.assertEqual(TA.__name__, "TA") + self.assertEqual(TA.__qualname__, "Class.TA") + self.assertIs(TA.__value__, str) + self.assertEqual(TA.__type_params__, ()) + self.assertEqual(TA.__module__, __name__) + def test_attributes_with_exec(self): ns = {} exec("type TA = int", ns, ns) TA = ns["TA"] self.assertEqual(TA.__name__, "TA") + self.assertEqual(TA.__qualname__, "TA") self.assertIs(TA.__value__, int) self.assertEqual(TA.__type_params__, ()) self.assertIs(TA.__module__, None) @@ -210,6 +261,7 @@ def test_generic(self): T = TypeVar("T") TA = TypeAliasType("TA", list[T], type_params=(T,)) self.assertEqual(TA.__name__, "TA") + self.assertEqual(TA.__qualname__, "TA") self.assertEqual(TA.__value__, list[T]) self.assertEqual(TA.__type_params__, (T,)) self.assertEqual(TA.__module__, __name__) @@ -218,6 +270,7 @@ def test_generic(self): def test_not_generic(self): TA = TypeAliasType("TA", list[int], type_params=()) self.assertEqual(TA.__name__, "TA") + self.assertEqual(TA.__qualname__, "TA") self.assertEqual(TA.__value__, list[int]) self.assertEqual(TA.__type_params__, ()) self.assertEqual(TA.__module__, __name__) @@ -268,8 +321,9 @@ def test_expects_type_like(self): TypeAliasType("A", int, type_params=(T, 2)) def test_keywords(self): - TA = TypeAliasType(name="TA", value=int) + TA = TypeAliasType(name="TA", value=int, type_params=(), qualname=None) self.assertEqual(TA.__name__, "TA") + self.assertEqual(TA.__qualname__, "TA") self.assertIs(TA.__value__, int) self.assertEqual(TA.__type_params__, ()) self.assertEqual(TA.__module__, __name__) @@ -283,6 +337,8 @@ def test_errors(self): TypeAliasType("TA", list, ()) with self.assertRaises(TypeError): TypeAliasType("TA", list, type_params=42) + with self.assertRaises(TypeError): + TypeAliasType("TA", list, qualname=range(5)) class TypeAliasTypeTest(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-12-01-12-12.gh-issue-139817.PAn-8Z.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-12-01-12-12.gh-issue-139817.PAn-8Z.rst new file mode 100644 index 00000000000..b205d21edfe --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-12-01-12-12.gh-issue-139817.PAn-8Z.rst @@ -0,0 +1,2 @@ +Attribute ``__qualname__`` is added to :class:`typing.TypeAliasType`. +Patch by Mikhail Efimov. diff --git a/Objects/clinic/typevarobject.c.h b/Objects/clinic/typevarobject.c.h index 9ae2f51f758..bd4c7a0e64f 100644 --- a/Objects/clinic/typevarobject.c.h +++ b/Objects/clinic/typevarobject.c.h @@ -688,14 +688,14 @@ typealias_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) } PyDoc_STRVAR(typealias_new__doc__, -"typealias(name, value, *, type_params=)\n" +"typealias(name, value, *, type_params=, qualname=None)\n" "--\n" "\n" "Create a TypeAliasType."); static PyObject * typealias_new_impl(PyTypeObject *type, PyObject *name, PyObject *value, - PyObject *type_params); + PyObject *type_params, PyObject *qualname); static PyObject * typealias_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) @@ -703,7 +703,7 @@ typealias_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - #define NUM_KEYWORDS 3 + #define NUM_KEYWORDS 4 static struct { PyGC_Head _this_is_not_used; PyObject_VAR_HEAD @@ -712,7 +712,7 @@ typealias_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) .ob_hash = -1, - .ob_item = { &_Py_ID(name), &_Py_ID(value), &_Py_ID(type_params), }, + .ob_item = { &_Py_ID(name), &_Py_ID(value), &_Py_ID(type_params), &_Py_ID(qualname), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -721,20 +721,21 @@ typealias_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"name", "value", "type_params", NULL}; + static const char * const _keywords[] = {"name", "value", "type_params", "qualname", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "typealias", .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[4]; PyObject * const *fastargs; Py_ssize_t nargs = PyTuple_GET_SIZE(args); Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 2; PyObject *name; PyObject *value; PyObject *type_params = NULL; + PyObject *qualname = NULL; fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); @@ -750,11 +751,17 @@ typealias_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) if (!noptargs) { goto skip_optional_kwonly; } - type_params = fastargs[2]; + if (fastargs[2]) { + type_params = fastargs[2]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + qualname = fastargs[3]; skip_optional_kwonly: - return_value = typealias_new_impl(type, name, value, type_params); + return_value = typealias_new_impl(type, name, value, type_params, qualname); exit: return return_value; } -/*[clinic end generated code: output=9dad71445e079303 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=67ab9a5d1869f2c9 input=a9049054013a1b77]*/ diff --git a/Objects/typevarobject.c b/Objects/typevarobject.c index 4fe46e9fccb..8a3a1e98345 100644 --- a/Objects/typevarobject.c +++ b/Objects/typevarobject.c @@ -53,6 +53,7 @@ typedef struct { typedef struct { PyObject_HEAD PyObject *name; + PyObject *qualname; PyObject *type_params; PyObject *compute_value; PyObject *value; @@ -1858,6 +1859,7 @@ typealias_dealloc(PyObject *self) _PyObject_GC_UNTRACK(self); typealiasobject *ta = typealiasobject_CAST(self); Py_XDECREF(ta->name); + Py_XDECREF(ta->qualname); Py_XDECREF(ta->type_params); Py_XDECREF(ta->compute_value); Py_XDECREF(ta->value); @@ -1884,11 +1886,12 @@ static PyObject * typealias_repr(PyObject *self) { typealiasobject *ta = (typealiasobject *)self; - return Py_NewRef(ta->name); + return Py_NewRef(ta->qualname); } static PyMemberDef typealias_members[] = { {"__name__", _Py_T_OBJECT, offsetof(typealiasobject, name), Py_READONLY}, + {"__qualname__", _Py_T_OBJECT, offsetof(typealiasobject, qualname), Py_READONLY}, {0} }; @@ -2003,7 +2006,7 @@ typealias_check_type_params(PyObject *type_params, int *err) { } static PyObject * -typelias_convert_type_params(PyObject *type_params) +typealias_convert_type_params(PyObject *type_params) { if ( type_params == NULL @@ -2018,14 +2021,15 @@ typelias_convert_type_params(PyObject *type_params) } static typealiasobject * -typealias_alloc(PyObject *name, PyObject *type_params, PyObject *compute_value, - PyObject *value, PyObject *module) +typealias_alloc(PyObject *name, PyObject *qualname, PyObject *type_params, + PyObject *compute_value, PyObject *value, PyObject *module) { typealiasobject *ta = PyObject_GC_New(typealiasobject, &_PyTypeAlias_Type); if (ta == NULL) { return NULL; } ta->name = Py_NewRef(name); + ta->qualname = Py_NewRef(qualname); ta->type_params = Py_XNewRef(type_params); ta->compute_value = Py_XNewRef(compute_value); ta->value = Py_XNewRef(value); @@ -2039,6 +2043,7 @@ typealias_traverse(PyObject *op, visitproc visit, void *arg) { typealiasobject *self = typealiasobject_CAST(op); Py_VISIT(self->name); + Py_VISIT(self->qualname); Py_VISIT(self->type_params); Py_VISIT(self->compute_value); Py_VISIT(self->value); @@ -2051,6 +2056,7 @@ typealias_clear(PyObject *op) { typealiasobject *self = typealiasobject_CAST(op); Py_CLEAR(self->name); + Py_CLEAR(self->qualname); Py_CLEAR(self->type_params); Py_CLEAR(self->compute_value); Py_CLEAR(self->value); @@ -2096,14 +2102,15 @@ typealias.__new__ as typealias_new value: object * type_params: object = NULL + qualname: object(c_default="NULL") = None Create a TypeAliasType. [clinic start generated code]*/ static PyObject * typealias_new_impl(PyTypeObject *type, PyObject *name, PyObject *value, - PyObject *type_params) -/*[clinic end generated code: output=8920ce6bdff86f00 input=df163c34e17e1a35]*/ + PyObject *type_params, PyObject *qualname) +/*[clinic end generated code: output=b7f6d9f1c577cd9c input=cbec290f8c4886ef]*/ { if (type_params != NULL && !PyTuple_Check(type_params)) { PyErr_SetString(PyExc_TypeError, "type_params must be a tuple"); @@ -2120,8 +2127,19 @@ typealias_new_impl(PyTypeObject *type, PyObject *name, PyObject *value, if (module == NULL) { return NULL; } - PyObject *ta = (PyObject *)typealias_alloc(name, checked_params, NULL, value, - module); + + if (qualname == NULL || qualname == Py_None) { + // If qualname was not set directly, we use name instead. + qualname = name; + } else { + if (!PyUnicode_Check(qualname)) { + PyErr_SetString(PyExc_TypeError, "qualname must be a string"); + return NULL; + } + } + + PyObject *ta = (PyObject *)typealias_alloc( + name, qualname, checked_params, NULL, value, module); Py_DECREF(module); return ta; } @@ -2187,10 +2205,17 @@ _Py_make_typealias(PyThreadState* unused, PyObject *args) assert(PyTuple_GET_SIZE(args) == 3); PyObject *name = PyTuple_GET_ITEM(args, 0); assert(PyUnicode_Check(name)); - PyObject *type_params = typelias_convert_type_params(PyTuple_GET_ITEM(args, 1)); + PyObject *type_params = typealias_convert_type_params(PyTuple_GET_ITEM(args, 1)); PyObject *compute_value = PyTuple_GET_ITEM(args, 2); assert(PyFunction_Check(compute_value)); - return (PyObject *)typealias_alloc(name, type_params, compute_value, NULL, NULL); + + PyFunctionObject *compute_func = (PyFunctionObject *)compute_value; + PyCodeObject *code_obj = (PyCodeObject *)compute_func->func_code; + PyObject *qualname = code_obj->co_qualname; + assert(qualname != NULL); + + return (PyObject *)typealias_alloc( + name, qualname, type_params, compute_value, NULL, NULL); } PyDoc_STRVAR(generic_doc, From bcced02604f845b2b71d0a1dd95f95366bd7774d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20S=C5=82awecki?= Date: Wed, 15 Oct 2025 18:49:14 +0200 Subject: [PATCH 180/373] gh-140141: Properly break exception chain in `importlib.metadata.Distribution.from_name` (#140142) --- Lib/importlib/metadata/__init__.py | 2 +- .../Library/2025-10-15-17-23-51.gh-issue-140141.j2mUDB.rst | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-15-17-23-51.gh-issue-140141.j2mUDB.rst diff --git a/Lib/importlib/metadata/__init__.py b/Lib/importlib/metadata/__init__.py index 1e2cea40094..b010bb8525e 100644 --- a/Lib/importlib/metadata/__init__.py +++ b/Lib/importlib/metadata/__init__.py @@ -457,7 +457,7 @@ def from_name(cls, name: str) -> Distribution: try: return next(iter(cls._prefer_valid(cls.discover(name=name)))) except StopIteration: - raise PackageNotFoundError(name) + raise PackageNotFoundError(name) from None @classmethod def discover( diff --git a/Misc/NEWS.d/next/Library/2025-10-15-17-23-51.gh-issue-140141.j2mUDB.rst b/Misc/NEWS.d/next/Library/2025-10-15-17-23-51.gh-issue-140141.j2mUDB.rst new file mode 100644 index 00000000000..2edadbc3e38 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-15-17-23-51.gh-issue-140141.j2mUDB.rst @@ -0,0 +1,5 @@ +The :py:class:`importlib.metadata.PackageNotFoundError` traceback raised when +``importlib.metadata.Distribution.from_name`` cannot discover a +distribution no longer includes a transient :exc:`StopIteration` exception trace. + +Contributed by Bartosz Sławecki in :gh:`140142`. From 65d1a14d59a16b441963bad3ca4b4783d37afd1d Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 15 Oct 2025 21:18:39 +0100 Subject: [PATCH 181/373] gh-139817: typing docs: Fix indentation of `.. versionadded::` note for `TypeAliasType.__qualname__` (#140177) --- Doc/library/typing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 4265c587195..41f22aa72cd 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2279,7 +2279,7 @@ without the dedicated syntax, as documented below. >>> Class.Alias.__qualname__ 'Class.Alias' - .. versionadded:: 3.15 + .. versionadded:: 3.15 .. attribute:: __module__ From 7f371ed84ba471bb1b11e79b502f244a9c17ac84 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Wed, 15 Oct 2025 22:40:39 +0100 Subject: [PATCH 182/373] gh-140041: Fix import of `ctypes` on Android and Cygwin when ABI flags are present (#140178) Use sysconfig to determine the full name of libpython, rather than hardcoding a library name that doesn't have ABI flags. --- Android/testbed/app/build.gradle.kts | 9 +++++---- Lib/ctypes/__init__.py | 11 ++++++----- .../2025-10-15-21-42-13.gh-issue-140041._Fka2j.rst | 1 + 3 files changed, 12 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-15-21-42-13.gh-issue-140041._Fka2j.rst diff --git a/Android/testbed/app/build.gradle.kts b/Android/testbed/app/build.gradle.kts index 92cffd61f86..4de628a279c 100644 --- a/Android/testbed/app/build.gradle.kts +++ b/Android/testbed/app/build.gradle.kts @@ -47,7 +47,7 @@ for ((i, prefix) in prefixes.withIndex()) { val libDir = file("$prefix/lib") val version = run { for (filename in libDir.list()!!) { - """python(\d+\.\d+)""".toRegex().matchEntire(filename)?.let { + """python(\d+\.\d+[a-z]*)""".toRegex().matchEntire(filename)?.let { return@run it.groupValues[1] } } @@ -64,9 +64,10 @@ for ((i, prefix) in prefixes.withIndex()) { val libPythonDir = file("$libDir/python$pythonVersion") val triplet = run { for (filename in libPythonDir.list()!!) { - """_sysconfigdata__android_(.+).py""".toRegex().matchEntire(filename)?.let { - return@run it.groupValues[1] - } + """_sysconfigdata_[a-z]*_android_(.+).py""".toRegex() + .matchEntire(filename)?.let { + return@run it.groupValues[1] + } } throw GradleException("Failed to find Python triplet in $libPythonDir") } diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 768cbada894..ab5b656e6e5 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -1,6 +1,8 @@ """create and manipulate C data types in Python""" -import os as _os, sys as _sys +import os as _os +import sys as _sys +import sysconfig as _sysconfig import types as _types __version__ = "1.1.0" @@ -550,10 +552,9 @@ def LoadLibrary(self, name): if _os.name == "nt": pythonapi = PyDLL("python dll", None, _sys.dllhandle) -elif _sys.platform == "android": - pythonapi = PyDLL("libpython%d.%d.so" % _sys.version_info[:2]) -elif _sys.platform == "cygwin": - pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2]) +elif _sys.platform in ["android", "cygwin"]: + # These are Unix-like platforms which use a dynamically-linked libpython. + pythonapi = PyDLL(_sysconfig.get_config_var("LDLIBRARY")) else: pythonapi = PyDLL(None) diff --git a/Misc/NEWS.d/next/Library/2025-10-15-21-42-13.gh-issue-140041._Fka2j.rst b/Misc/NEWS.d/next/Library/2025-10-15-21-42-13.gh-issue-140041._Fka2j.rst new file mode 100644 index 00000000000..243ff39311c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-15-21-42-13.gh-issue-140041._Fka2j.rst @@ -0,0 +1 @@ +Fix import of :mod:`ctypes` on Android and Cygwin when ABI flags are present. From 3a81313019ba82152653bd86f6ffd87dfe339c60 Mon Sep 17 00:00:00 2001 From: Jonathan Protzenko Date: Thu, 16 Oct 2025 13:25:51 +0800 Subject: [PATCH 183/373] gh-140120: Refresh HACL* to fix an hmac memory leak (GH-140188) This pulls an updated version of HACL* that fixes the memory leak reported in #140120, via an upstream fix. --- ...-10-15-20-47-04.gh-issue-140120.3gffZq.rst | 2 + Misc/sbom.spdx.json | 124 +++++++++--------- Modules/_hacl/Hacl_HMAC.h | 8 +- Modules/_hacl/Hacl_Hash_Blake2b.h | 8 +- Modules/_hacl/Hacl_Hash_Blake2b_Simd256.h | 8 +- Modules/_hacl/Hacl_Hash_Blake2s.h | 8 +- Modules/_hacl/Hacl_Hash_Blake2s_Simd128.h | 8 +- Modules/_hacl/Hacl_Hash_MD5.h | 8 +- Modules/_hacl/Hacl_Hash_SHA1.h | 8 +- Modules/_hacl/Hacl_Hash_SHA2.h | 8 +- Modules/_hacl/Hacl_Hash_SHA3.h | 8 +- Modules/_hacl/Hacl_Streaming_HMAC.c | 8 +- Modules/_hacl/Hacl_Streaming_HMAC.h | 8 +- Modules/_hacl/Hacl_Streaming_Types.h | 8 +- .../include/krml/FStar_UInt128_Verified.h | 8 +- .../include/krml/FStar_UInt_8_16_32_64.h | 16 ++- Modules/_hacl/include/krml/internal/target.h | 9 +- Modules/_hacl/include/krml/internal/types.h | 2 +- .../_hacl/include/krml/lowstar_endianness.h | 6 +- Modules/_hacl/internal/Hacl_HMAC.h | 8 +- Modules/_hacl/internal/Hacl_Hash_Blake2b.h | 8 +- .../internal/Hacl_Hash_Blake2b_Simd256.h | 8 +- Modules/_hacl/internal/Hacl_Hash_Blake2s.h | 8 +- .../internal/Hacl_Hash_Blake2s_Simd128.h | 8 +- Modules/_hacl/internal/Hacl_Hash_MD5.h | 8 +- Modules/_hacl/internal/Hacl_Hash_SHA1.h | 8 +- Modules/_hacl/internal/Hacl_Hash_SHA2.h | 8 +- Modules/_hacl/internal/Hacl_Hash_SHA3.h | 8 +- .../internal/Hacl_Impl_Blake2_Constants.h | 8 +- Modules/_hacl/internal/Hacl_Streaming_HMAC.h | 8 +- Modules/_hacl/internal/Hacl_Streaming_Types.h | 8 +- Modules/_hacl/refresh.sh | 2 +- 32 files changed, 189 insertions(+), 172 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-15-20-47-04.gh-issue-140120.3gffZq.rst diff --git a/Misc/NEWS.d/next/Library/2025-10-15-20-47-04.gh-issue-140120.3gffZq.rst b/Misc/NEWS.d/next/Library/2025-10-15-20-47-04.gh-issue-140120.3gffZq.rst new file mode 100644 index 00000000000..9eefe140520 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-15-20-47-04.gh-issue-140120.3gffZq.rst @@ -0,0 +1,2 @@ +Fixed a memory leak in :mod:`hmac` when it was using the hacl-star backend. +Discovered by ``@ashm-dev`` using AddressSanitizer. diff --git a/Misc/sbom.spdx.json b/Misc/sbom.spdx.json index dc5497663a9..e9554bf7837 100644 --- a/Misc/sbom.spdx.json +++ b/Misc/sbom.spdx.json @@ -300,11 +300,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "de7179fe6970e2b5d281dfed977ed91be635b8d2" + "checksumValue": "a57d53c35aa916ce0cd1567c8f10dcea58270321" }, { "algorithm": "SHA256", - "checksumValue": "c0ba888d87775c7d7f7d8a08dac7b3988fed81e11bb52396d90f762a8e90a7eb" + "checksumValue": "d68209032703137326f9aadd9abac8fe4a5c92d391e2f43b0414fa63087adc6b" } ], "fileName": "Modules/_hacl/Hacl_HMAC.h" @@ -328,11 +328,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "7c66ac004a1dcf3fee0ab9aa62d61972f029de3a" + "checksumValue": "4d767388a34e2a2000e0cbd31f06cf36af1ae10d" }, { "algorithm": "SHA256", - "checksumValue": "9a7239a01a4ee8defbe3ebd9f0d12c873a1dd8e0659070380b2eab3ab0177333" + "checksumValue": "fac493e96af252abcf5705f0ab4eec59c1f3bc8244e105d75a57c43552dd1569" } ], "fileName": "Modules/_hacl/Hacl_Hash_Blake2b.h" @@ -356,11 +356,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "7f273d26942233e5dcdfb4c1a16ff2486b15f899" + "checksumValue": "280fd03ee23e13ecf90711d2be8230035d957343" }, { "algorithm": "SHA256", - "checksumValue": "dbc0dacc68ed52dbf1b7d6fba2c87870317998bc046e65f6deaaa150625432f8" + "checksumValue": "817dcd05d06d804587fce7d8f2f3f42a6fcf6818d2419a3551ef0df70cb7125a" } ], "fileName": "Modules/_hacl/Hacl_Hash_Blake2b_Simd256.h" @@ -398,11 +398,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "2120c8c467aeebcc7c8b9678c15e79648433b91a" + "checksumValue": "79f47ab5458d88bce0f210a566aa117ce2125049" }, { "algorithm": "SHA256", - "checksumValue": "45735f7fe2dbbad7656d07854e9ec8176ad26c79f90dcc0fec0b9a59a6311ba7" + "checksumValue": "5cc96313f8c066f055c2819e473c79aeff086ba91a1449d54aa569127cd8601e" } ], "fileName": "Modules/_hacl/Hacl_Hash_Blake2s.h" @@ -426,11 +426,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "9028e24c9876d9d16b2435ec29240c6b57bfe2a0" + "checksumValue": "c98890b6193baa3dbd472cfb67f22aea3dd0d3e1" }, { "algorithm": "SHA256", - "checksumValue": "062e3b856acac4f929c1e04b8264a754cad21ca6580215f7094a3f0a04edb912" + "checksumValue": "fe3f17bf237166f872ba88c8204333c4f64bdc4bb29c265448581c7bfea4b151" } ], "fileName": "Modules/_hacl/Hacl_Hash_Blake2s_Simd128.h" @@ -468,11 +468,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "e67a9bc18358c57afaeff3a174893ddfdb52dfc6" + "checksumValue": "2ed70f612a998ef6c04d62f9487a1b2534e6d76a" }, { "algorithm": "SHA256", - "checksumValue": "16e982081f6c2fd03ea751fcc64f5a835c94652841836e231fe562b9e287f4bc" + "checksumValue": "a34534ef36bc8428ac1169ca5f4f1c17a0817507ebae388e3dd825d1a331f28d" } ], "fileName": "Modules/_hacl/Hacl_Hash_MD5.h" @@ -496,11 +496,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "f1ca21f1ee8b15ad9ccfbda72165b9d86912166c" + "checksumValue": "a3f9bdc9e73f80eb4a586dc180246cfb8267ffc0" }, { "algorithm": "SHA256", - "checksumValue": "4b2ad9ea93fdd9c2fdc521fc4e14e02550666c2717a23b85819db2e07ea555f3" + "checksumValue": "2d5cae94382f5473cf6d2654760375ff1ee9cebdb8d4506b63ba33f488de1559" } ], "fileName": "Modules/_hacl/Hacl_Hash_SHA1.h" @@ -524,11 +524,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "f38cebeeca40a83aeb2cf5dfce578ffefe176d84" + "checksumValue": "68b35d0c573f7301ba4d6919b1680d55675a0d98" }, { "algorithm": "SHA256", - "checksumValue": "ee03bf9368d1a3a3c70cfd4e9391b2485466404db4a60bfc5319630cc314b590" + "checksumValue": "442d8997d2bcda20ddf628665e2e69945400e1ab3019bc14fc7c8e20db20c320" } ], "fileName": "Modules/_hacl/Hacl_Hash_SHA2.h" @@ -552,11 +552,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "01717207aef77174e328186d48c27517f6644c15" + "checksumValue": "db1008095b64b2f8ee2a4ce72fa7cfc4a622a108" }, { "algorithm": "SHA256", - "checksumValue": "620dded172e94cb3f25f9904b44977d91f2cc9573e41b38f19e929d083ae0308" + "checksumValue": "961a686186b76a1cd6a64bcfd06afdc8657b1f901bac991166f498ed16f9349a" } ], "fileName": "Modules/_hacl/Hacl_Hash_SHA3.h" @@ -566,11 +566,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "417e68ac8498cb2f93a06a19003ea1cc3f0f6753" + "checksumValue": "eb224e26bc0503a48c88c837e28bbc7a9878ba5c" }, { "algorithm": "SHA256", - "checksumValue": "843db4bba78a476d4d53dabe4eed5c9235f490ccc9fdaf19e22af488af858920" + "checksumValue": "29fcf948a3715e09cbbd434cebb6105f276236a6e4947d237b7bf06634bf2432" } ], "fileName": "Modules/_hacl/Hacl_Streaming_HMAC.c" @@ -580,11 +580,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "49523144583a15d96ba1646af02dc292e633bf8f" + "checksumValue": "2d6c600024275c780e8313e0a5ab506e025bb4d6" }, { "algorithm": "SHA256", - "checksumValue": "78345519bf6789264f6792b809ee97a9ecf7cb5829c674c61e2d99bfdfdc36fc" + "checksumValue": "6450aef92f507fb0a8a3086b1d2792e7fca07121580c24fdaedd1b32e9ad0a76" } ], "fileName": "Modules/_hacl/Hacl_Streaming_HMAC.h" @@ -594,11 +594,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "372448599774a98e5c5d083e91f301ed1c4b822e" + "checksumValue": "ed283b95ebb772b05bdf802b70dbb301ece84e91" }, { "algorithm": "SHA256", - "checksumValue": "95d8e70ca4bc6aa98f6d2435ceb6410ead299b1f700fae1f5c603ec3f57ea551" + "checksumValue": "efc7bd11460744768425aedf4e004d3ad3397a5489752b80044ae785f30b78d4" } ], "fileName": "Modules/_hacl/Hacl_Streaming_Types.h" @@ -622,11 +622,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "911c97a0f24067635b164fbca49da76055f192cd" + "checksumValue": "ae06c415ae7e0e1e85558863f29d1b9013e46e9b" }, { "algorithm": "SHA256", - "checksumValue": "972b5111ebada8e11dd60df3119da1af505fd3e0b6c782ead6cab7f1daf134f1" + "checksumValue": "69dc2e78411e9b271100eb0d2a2d8dc39dd2348afc26f8b4cfba14c025102c7f" } ], "fileName": "Modules/_hacl/include/krml/FStar_UInt128_Verified.h" @@ -636,11 +636,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "41ee1e34ede7ef5b24b87d4ca816fd6d9fac8010" + "checksumValue": "edefe48ced707327fd6cdf3f18b3ca42dda9a9f7" }, { "algorithm": "SHA256", - "checksumValue": "d48ed03e504cb87793a310a9552fb3ba2ebd6fe90127b7d642c8740fba1b9748" + "checksumValue": "f01e3f0892935f3f659019875f580445b9ea23482afbe11ffbe7cdbd22dbd4d2" } ], "fileName": "Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h" @@ -678,11 +678,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "e01d7d493fbaceeedc4b1c6451d8240bcb9c903a" + "checksumValue": "7a943fbb8f55729a960f9b426e9ff2794453aca9" }, { "algorithm": "SHA256", - "checksumValue": "c2f0a43884771f24d7cb744b79818b160020d2739b2881b2054cfc97fb2e7b4a" + "checksumValue": "08bb43ef5626a8450f985da0af345b6658ac8bcc4585f77271decd0e41fc02e0" } ], "fileName": "Modules/_hacl/include/krml/internal/target.h" @@ -692,11 +692,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "3f66313d16891f43b21c1a736081c2c6d46bf370" + "checksumValue": "b4e6c5fc5e17864cc2bf452db2688be733d6df72" }, { "algorithm": "SHA256", - "checksumValue": "78e9bff9124968108e1699e1c6388e3d4ec9bd72dd8adff49734a69ab380ee5c" + "checksumValue": "404691cf9b2269ecc754ceca27bb8f8f029f1c8deaa4d967990dcf5ce08cd016" } ], "fileName": "Modules/_hacl/include/krml/internal/types.h" @@ -706,11 +706,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "e18efc9239a5df0f222b5f7b0a65f72509d7e304" + "checksumValue": "8c3e09e00459951e060ad469c1fa31eeb9b6b9cb" }, { "algorithm": "SHA256", - "checksumValue": "47dd5a7d21b5302255f9fff28884f65d3056fc3f54471ed62ec85fa1904f8aa5" + "checksumValue": "6d1a350ec272c83761f1789611d8f60947a4d69fed3d23d8eee38709a0ad2c0a" } ], "fileName": "Modules/_hacl/include/krml/lowstar_endianness.h" @@ -720,11 +720,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "aaa656e25a92ba83655e1398a97efa6981f60fc4" + "checksumValue": "c9517c43046de129f02da8b74e454bade4872c00" }, { "algorithm": "SHA256", - "checksumValue": "a59abc6e9b3019cb18976a15e634f5146bd965fc9babf4ccbf2b531164a34f85" + "checksumValue": "96c2a973bc01b1295f7af67c7de3839facfeee36c8848d7d88c62695de98ab21" } ], "fileName": "Modules/_hacl/internal/Hacl_HMAC.h" @@ -734,11 +734,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "0741cb8497309d648428be1e7b5944b1fc167187" + "checksumValue": "1c5eeb5d1866acb6bc8b4e5a01a107e3a87f5694" }, { "algorithm": "SHA256", - "checksumValue": "f9b923a566d62de047c753637143d439ca1c25221c08352ddc1738ff4a6ac721" + "checksumValue": "f262738390f56e8e9692acadecd1434c688075047d788283e1fb45d920ea6956" } ], "fileName": "Modules/_hacl/internal/Hacl_Hash_Blake2b.h" @@ -748,11 +748,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "878ae284c93824b80b1763e8b3e6be3c410777a8" + "checksumValue": "b600c1b5eafc34b29a778186737d8a51e2342d85" }, { "algorithm": "SHA256", - "checksumValue": "49df6223f6403daf503a1af1a3d2f943d30b5889fe7ed20299c3df24c1e3853d" + "checksumValue": "f22e088ad9c2eb739aefc3685ef3ab1ccaab3c5ef2e5d06cc846ad9f8e3d2669" } ], "fileName": "Modules/_hacl/internal/Hacl_Hash_Blake2b_Simd256.h" @@ -762,11 +762,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "25552d8cbf8aa345907635b38f284eec9075301e" + "checksumValue": "eb618ecc6ca2830066448f5f6d4df84a5c09f0f4" }, { "algorithm": "SHA256", - "checksumValue": "a3424cf4c5518654908086bbbf5d465715ec3b23625ef0cadc29492d1f90366c" + "checksumValue": "a4728e43deb0a9d8213b8ddcbda68a63bc73a12fb99aad54b7d28b314776a0d4" } ], "fileName": "Modules/_hacl/internal/Hacl_Hash_Blake2s.h" @@ -776,11 +776,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "54a712fc3ed5a817288351cbac5b7d9afa7e379f" + "checksumValue": "5441b6a97cc053c332b29477238d70fa011c74ac" }, { "algorithm": "SHA256", - "checksumValue": "c6abae648b8a1e9d5631c0a959620cad1f7e92ce522e07c3416199fe51debef6" + "checksumValue": "dad568d256a2ccbbbcdd419fe0543ea7137d8065a713a5f009aa52521c0f7f6a" } ], "fileName": "Modules/_hacl/internal/Hacl_Hash_Blake2s_Simd128.h" @@ -790,11 +790,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "c15c5f83bbb9f62611c49f0f8f723eaab1a27488" + "checksumValue": "7081b58f28568f600d4624dd5bd6f735191e068b" }, { "algorithm": "SHA256", - "checksumValue": "95cd5d91c4a9217901d0b3395dcd8881e62e2055d723b532ec5176386a636d22" + "checksumValue": "305af1422213ed97e8e5d3d8a9dfee4f21cb2fcd2acf65ee7303ce00b2b4bd2a" } ], "fileName": "Modules/_hacl/internal/Hacl_Hash_MD5.h" @@ -804,11 +804,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "7b8717e3a24e7e16a34b251d0d02da6f68439695" + "checksumValue": "b6fb6219cb40d039e789666e34b43461e3b5c82a" }, { "algorithm": "SHA256", - "checksumValue": "9473d8bc9506fe0053d7d98c225d4873011329863f1c4a8e93e43fc71bd1f314" + "checksumValue": "63c58363ff95e8146d5628dab2da25bc2fd0e8b590fd4823b512c33f843355bb" } ], "fileName": "Modules/_hacl/internal/Hacl_Hash_SHA1.h" @@ -818,11 +818,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "e319c949f5a2dd765be2c8c7ff77bfe52ee6c7da" + "checksumValue": "bcc71e702df1070cb0081cb983aec7683e036719" }, { "algorithm": "SHA256", - "checksumValue": "75261448e51c3eb1ba441e973b193e23570b167f67743942ee2ee57417491c9f" + "checksumValue": "709c6272f77e2368f6cc0bf36527e3b16dd5d5f3dc26a2afdef8238200af8770" } ], "fileName": "Modules/_hacl/internal/Hacl_Hash_SHA2.h" @@ -832,11 +832,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "dbd92415c31606804102b79d5ba3d1752fe03887" + "checksumValue": "e3b5e6add5357760554b032b818992ce9bc211e0" }, { "algorithm": "SHA256", - "checksumValue": "5d74a76a0ac3659a1ae1276c3ca55521f09e83d2f0039f5c519a76f8f3c76a8e" + "checksumValue": "cf49d536a5663379fb4517018b4b3f8a232f8d4c7ddc7edfd60163d13f98a530" } ], "fileName": "Modules/_hacl/internal/Hacl_Hash_SHA3.h" @@ -846,11 +846,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "ad788265f8e1b078c4d1cb6e90b8c031590e6baf" + "checksumValue": "09a0ea364fb073f5f622a763f1351fbf4bac4627" }, { "algorithm": "SHA256", - "checksumValue": "d8354a9b75e2470085fa7e538493130e81fa23a804a6a69d34da8fdcc941c038" + "checksumValue": "916d54d7217517f0360edea920dc21585fbe6d1c2458eac86826182e12c82ec2" } ], "fileName": "Modules/_hacl/internal/Hacl_Impl_Blake2_Constants.h" @@ -860,11 +860,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "2048f3cd61dbda2df862a2982ebaf24b6815ed51" + "checksumValue": "b082645b43f8841db5e9e57c0b9a3825fa7e52f5" }, { "algorithm": "SHA256", - "checksumValue": "b0f5a79c98525b0cb1659238e095641328b7da16a94cb57a0793e635d1da3653" + "checksumValue": "059cf0d31427abf84c626797a97c140630a1a6ead578005162f46fad46663389" } ], "fileName": "Modules/_hacl/internal/Hacl_Streaming_HMAC.h" @@ -874,11 +874,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "4e6b098e89fd447bd03f47b55208208456b20966" + "checksumValue": "1f85ed69e395829b3ac5af9e2d049af7708cb9cb" }, { "algorithm": "SHA256", - "checksumValue": "d54d947968ca125978d61fea844711b990f0a18ab0fbca87e41029004d9d04b6" + "checksumValue": "76997c7069a347ac78b65d26354a0324d54add4ca7a02e7be36d6d5e1c9702e0" } ], "fileName": "Modules/_hacl/internal/Hacl_Streaming_Types.h" @@ -1752,14 +1752,14 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "39f6fd4f2fe98aecc995a2fd980fae0e0835cef6e563ebbf25f69d3d3102bafd" + "checksumValue": "61e48893f37cb2280d106cefacf6fb5afe84edf625fec39572d0ee94e1018f26" } ], - "downloadLocation": "https://github.com/hacl-star/hacl-star/archive/4ef25b547b377dcef855db4289c6a00580e7221c.zip", + "downloadLocation": "https://github.com/hacl-star/hacl-star/archive/8ba599b2f6c9701b3dc961db895b0856a2210f76.zip", "externalRefs": [ { "referenceCategory": "SECURITY", - "referenceLocator": "cpe:2.3:a:hacl-star:hacl-star:4ef25b547b377dcef855db4289c6a00580e7221c:*:*:*:*:*:*:*", + "referenceLocator": "cpe:2.3:a:hacl-star:hacl-star:8ba599b2f6c9701b3dc961db895b0856a2210f76:*:*:*:*:*:*:*", "referenceType": "cpe23Type" } ], @@ -1767,7 +1767,7 @@ "name": "hacl-star", "originator": "Organization: HACL* Developers", "primaryPackagePurpose": "SOURCE", - "versionInfo": "4ef25b547b377dcef855db4289c6a00580e7221c" + "versionInfo": "8ba599b2f6c9701b3dc961db895b0856a2210f76" }, { "SPDXID": "SPDXRef-PACKAGE-macholib", diff --git a/Modules/_hacl/Hacl_HMAC.h b/Modules/_hacl/Hacl_HMAC.h index 10ff15183f2..7dca53c9254 100644 --- a/Modules/_hacl/Hacl_HMAC.h +++ b/Modules/_hacl/Hacl_HMAC.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_HMAC_H -#define __Hacl_HMAC_H +#ifndef Hacl_HMAC_H +#define Hacl_HMAC_H #if defined(__cplusplus) extern "C" { @@ -220,5 +220,5 @@ Hacl_HMAC_compute_blake2b_32( } #endif -#define __Hacl_HMAC_H_DEFINED -#endif +#define Hacl_HMAC_H_DEFINED +#endif /* Hacl_HMAC_H */ diff --git a/Modules/_hacl/Hacl_Hash_Blake2b.h b/Modules/_hacl/Hacl_Hash_Blake2b.h index 3a73f358c98..b07893ba339 100644 --- a/Modules/_hacl/Hacl_Hash_Blake2b.h +++ b/Modules/_hacl/Hacl_Hash_Blake2b.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Hash_Blake2b_H -#define __Hacl_Hash_Blake2b_H +#ifndef Hacl_Hash_Blake2b_H +#define Hacl_Hash_Blake2b_H #if defined(__cplusplus) extern "C" { @@ -220,5 +220,5 @@ Hacl_Hash_Blake2b_hash_with_key_and_params( } #endif -#define __Hacl_Hash_Blake2b_H_DEFINED -#endif +#define Hacl_Hash_Blake2b_H_DEFINED +#endif /* Hacl_Hash_Blake2b_H */ diff --git a/Modules/_hacl/Hacl_Hash_Blake2b_Simd256.h b/Modules/_hacl/Hacl_Hash_Blake2b_Simd256.h index 0271ab8e024..8be8f32db62 100644 --- a/Modules/_hacl/Hacl_Hash_Blake2b_Simd256.h +++ b/Modules/_hacl/Hacl_Hash_Blake2b_Simd256.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Hash_Blake2b_Simd256_H -#define __Hacl_Hash_Blake2b_Simd256_H +#ifndef Hacl_Hash_Blake2b_Simd256_H +#define Hacl_Hash_Blake2b_Simd256_H #if defined(__cplusplus) extern "C" { @@ -206,5 +206,5 @@ Hacl_Hash_Blake2b_Simd256_hash_with_key_and_params( } #endif -#define __Hacl_Hash_Blake2b_Simd256_H_DEFINED -#endif +#define Hacl_Hash_Blake2b_Simd256_H_DEFINED +#endif /* Hacl_Hash_Blake2b_Simd256_H */ diff --git a/Modules/_hacl/Hacl_Hash_Blake2s.h b/Modules/_hacl/Hacl_Hash_Blake2s.h index fbf8cff5cd1..2582a3d34e3 100644 --- a/Modules/_hacl/Hacl_Hash_Blake2s.h +++ b/Modules/_hacl/Hacl_Hash_Blake2s.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Hash_Blake2s_H -#define __Hacl_Hash_Blake2s_H +#ifndef Hacl_Hash_Blake2s_H +#define Hacl_Hash_Blake2s_H #if defined(__cplusplus) extern "C" { @@ -198,5 +198,5 @@ Hacl_Hash_Blake2s_hash_with_key_and_params( } #endif -#define __Hacl_Hash_Blake2s_H_DEFINED -#endif +#define Hacl_Hash_Blake2s_H_DEFINED +#endif /* Hacl_Hash_Blake2s_H */ diff --git a/Modules/_hacl/Hacl_Hash_Blake2s_Simd128.h b/Modules/_hacl/Hacl_Hash_Blake2s_Simd128.h index 56456e6fbb3..4b3b4e4fbb5 100644 --- a/Modules/_hacl/Hacl_Hash_Blake2s_Simd128.h +++ b/Modules/_hacl/Hacl_Hash_Blake2s_Simd128.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Hash_Blake2s_Simd128_H -#define __Hacl_Hash_Blake2s_Simd128_H +#ifndef Hacl_Hash_Blake2s_Simd128_H +#define Hacl_Hash_Blake2s_Simd128_H #if defined(__cplusplus) extern "C" { @@ -206,5 +206,5 @@ Hacl_Hash_Blake2s_Simd128_hash_with_key_and_params( } #endif -#define __Hacl_Hash_Blake2s_Simd128_H_DEFINED -#endif +#define Hacl_Hash_Blake2s_Simd128_H_DEFINED +#endif /* Hacl_Hash_Blake2s_Simd128_H */ diff --git a/Modules/_hacl/Hacl_Hash_MD5.h b/Modules/_hacl/Hacl_Hash_MD5.h index 521c2addc50..b220bbf6890 100644 --- a/Modules/_hacl/Hacl_Hash_MD5.h +++ b/Modules/_hacl/Hacl_Hash_MD5.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Hash_MD5_H -#define __Hacl_Hash_MD5_H +#ifndef Hacl_Hash_MD5_H +#define Hacl_Hash_MD5_H #if defined(__cplusplus) extern "C" { @@ -62,5 +62,5 @@ void Hacl_Hash_MD5_hash(uint8_t *output, uint8_t *input, uint32_t input_len); } #endif -#define __Hacl_Hash_MD5_H_DEFINED -#endif +#define Hacl_Hash_MD5_H_DEFINED +#endif /* Hacl_Hash_MD5_H */ diff --git a/Modules/_hacl/Hacl_Hash_SHA1.h b/Modules/_hacl/Hacl_Hash_SHA1.h index 63ac83f9dce..7fc8c8cfd20 100644 --- a/Modules/_hacl/Hacl_Hash_SHA1.h +++ b/Modules/_hacl/Hacl_Hash_SHA1.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Hash_SHA1_H -#define __Hacl_Hash_SHA1_H +#ifndef Hacl_Hash_SHA1_H +#define Hacl_Hash_SHA1_H #if defined(__cplusplus) extern "C" { @@ -62,5 +62,5 @@ void Hacl_Hash_SHA1_hash(uint8_t *output, uint8_t *input, uint32_t input_len); } #endif -#define __Hacl_Hash_SHA1_H_DEFINED -#endif +#define Hacl_Hash_SHA1_H_DEFINED +#endif /* Hacl_Hash_SHA1_H */ diff --git a/Modules/_hacl/Hacl_Hash_SHA2.h b/Modules/_hacl/Hacl_Hash_SHA2.h index a93138fb7ee..dd168b8aa1a 100644 --- a/Modules/_hacl/Hacl_Hash_SHA2.h +++ b/Modules/_hacl/Hacl_Hash_SHA2.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Hash_SHA2_H -#define __Hacl_Hash_SHA2_H +#ifndef Hacl_Hash_SHA2_H +#define Hacl_Hash_SHA2_H #if defined(__cplusplus) extern "C" { @@ -199,5 +199,5 @@ void Hacl_Hash_SHA2_hash_384(uint8_t *output, uint8_t *input, uint32_t input_len } #endif -#define __Hacl_Hash_SHA2_H_DEFINED -#endif +#define Hacl_Hash_SHA2_H_DEFINED +#endif /* Hacl_Hash_SHA2_H */ diff --git a/Modules/_hacl/Hacl_Hash_SHA3.h b/Modules/_hacl/Hacl_Hash_SHA3.h index 65ec99ee3a3..df6ebe439e9 100644 --- a/Modules/_hacl/Hacl_Hash_SHA3.h +++ b/Modules/_hacl/Hacl_Hash_SHA3.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Hash_SHA3_H -#define __Hacl_Hash_SHA3_H +#ifndef Hacl_Hash_SHA3_H +#define Hacl_Hash_SHA3_H #if defined(__cplusplus) extern "C" { @@ -155,5 +155,5 @@ Hacl_Hash_SHA3_shake128_squeeze_nblocks( } #endif -#define __Hacl_Hash_SHA3_H_DEFINED -#endif +#define Hacl_Hash_SHA3_H_DEFINED +#endif /* Hacl_Hash_SHA3_H */ diff --git a/Modules/_hacl/Hacl_Streaming_HMAC.c b/Modules/_hacl/Hacl_Streaming_HMAC.c index 8dd7e2c0bf3..f89c00ee1ae 100644 --- a/Modules/_hacl/Hacl_Streaming_HMAC.c +++ b/Modules/_hacl/Hacl_Streaming_HMAC.c @@ -2375,9 +2375,13 @@ Hacl_Streaming_HMAC_digest( Hacl_Agile_Hash_state_s *s112 = tmp_block_state1.snd; update_multi(s112, prev_len, buf_multi, 0U); uint64_t prev_len_last = total_len - (uint64_t)r; - Hacl_Agile_Hash_state_s *s11 = tmp_block_state1.snd; - update_last(s11, prev_len_last, buf_last, r); + Hacl_Agile_Hash_state_s *s113 = tmp_block_state1.snd; + update_last(s113, prev_len_last, buf_last, r); finish0(tmp_block_state1, output); + Hacl_Agile_Hash_state_s *s210 = tmp_block_state1.thd; + Hacl_Agile_Hash_state_s *s11 = tmp_block_state1.snd; + free_(s11); + free_(s210); return Hacl_Streaming_Types_Success; } KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", diff --git a/Modules/_hacl/Hacl_Streaming_HMAC.h b/Modules/_hacl/Hacl_Streaming_HMAC.h index a0806c02d0b..6c302b1f92d 100644 --- a/Modules/_hacl/Hacl_Streaming_HMAC.h +++ b/Modules/_hacl/Hacl_Streaming_HMAC.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Streaming_HMAC_H -#define __Hacl_Streaming_HMAC_H +#ifndef Hacl_Streaming_HMAC_H +#define Hacl_Streaming_HMAC_H #if defined(__cplusplus) extern "C" { @@ -130,5 +130,5 @@ Hacl_Streaming_HMAC_agile_state } #endif -#define __Hacl_Streaming_HMAC_H_DEFINED -#endif +#define Hacl_Streaming_HMAC_H_DEFINED +#endif /* Hacl_Streaming_HMAC_H */ diff --git a/Modules/_hacl/Hacl_Streaming_Types.h b/Modules/_hacl/Hacl_Streaming_Types.h index 5c497750793..cce25eb7946 100644 --- a/Modules/_hacl/Hacl_Streaming_Types.h +++ b/Modules/_hacl/Hacl_Streaming_Types.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Streaming_Types_H -#define __Hacl_Streaming_Types_H +#ifndef Hacl_Streaming_Types_H +#define Hacl_Streaming_Types_H #if defined(__cplusplus) extern "C" { @@ -68,5 +68,5 @@ typedef struct Hacl_Streaming_MD_state_64_s Hacl_Streaming_MD_state_64; } #endif -#define __Hacl_Streaming_Types_H_DEFINED -#endif +#define Hacl_Streaming_Types_H_DEFINED +#endif /* Hacl_Streaming_Types_H */ diff --git a/Modules/_hacl/include/krml/FStar_UInt128_Verified.h b/Modules/_hacl/include/krml/FStar_UInt128_Verified.h index f85982f3373..7aa2a6d83f9 100644 --- a/Modules/_hacl/include/krml/FStar_UInt128_Verified.h +++ b/Modules/_hacl/include/krml/FStar_UInt128_Verified.h @@ -4,8 +4,8 @@ */ -#ifndef __FStar_UInt128_Verified_H -#define __FStar_UInt128_Verified_H +#ifndef FStar_UInt128_Verified_H +#define FStar_UInt128_Verified_H #include "FStar_UInt_8_16_32_64.h" #include @@ -331,5 +331,5 @@ static inline FStar_UInt128_uint128 FStar_UInt128_mul_wide(uint64_t x, uint64_t } -#define __FStar_UInt128_Verified_H_DEFINED -#endif +#define FStar_UInt128_Verified_H_DEFINED +#endif /* FStar_UInt128_Verified_H */ diff --git a/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h b/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h index 00be8083657..2720348bbb7 100644 --- a/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h +++ b/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h @@ -4,8 +4,8 @@ */ -#ifndef __FStar_UInt_8_16_32_64_H -#define __FStar_UInt_8_16_32_64_H +#ifndef FStar_UInt_8_16_32_64_H +#define FStar_UInt_8_16_32_64_H #include #include @@ -30,6 +30,8 @@ extern uint64_t FStar_UInt64_zero; extern uint64_t FStar_UInt64_one; +extern bool FStar_UInt64_ne(uint64_t a, uint64_t b); + extern uint64_t FStar_UInt64_minus(uint64_t a); extern uint32_t FStar_UInt64_n_minus_one; @@ -80,6 +82,8 @@ extern uint32_t FStar_UInt32_zero; extern uint32_t FStar_UInt32_one; +extern bool FStar_UInt32_ne(uint32_t a, uint32_t b); + extern uint32_t FStar_UInt32_minus(uint32_t a); extern uint32_t FStar_UInt32_n_minus_one; @@ -130,6 +134,8 @@ extern uint16_t FStar_UInt16_zero; extern uint16_t FStar_UInt16_one; +extern bool FStar_UInt16_ne(uint16_t a, uint16_t b); + extern uint16_t FStar_UInt16_minus(uint16_t a); extern uint32_t FStar_UInt16_n_minus_one; @@ -180,6 +186,8 @@ extern uint8_t FStar_UInt8_zero; extern uint8_t FStar_UInt8_one; +extern bool FStar_UInt8_ne(uint8_t a, uint8_t b); + extern uint8_t FStar_UInt8_minus(uint8_t a); extern uint32_t FStar_UInt8_n_minus_one; @@ -217,5 +225,5 @@ extern uint8_t FStar_UInt8_of_string(Prims_string uu___); typedef uint8_t FStar_UInt8_byte; -#define __FStar_UInt_8_16_32_64_H_DEFINED -#endif +#define FStar_UInt_8_16_32_64_H_DEFINED +#endif /* FStar_UInt_8_16_32_64_H */ diff --git a/Modules/_hacl/include/krml/internal/target.h b/Modules/_hacl/include/krml/internal/target.h index c592214634a..73555ab5ca1 100644 --- a/Modules/_hacl/include/krml/internal/target.h +++ b/Modules/_hacl/include/krml/internal/target.h @@ -1,8 +1,8 @@ /* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 and MIT Licenses. */ -#ifndef __KRML_TARGET_H -#define __KRML_TARGET_H +#ifndef KRML_HEADER_TARGET_H +#define KRML_HEADER_TARGET_H #include #include @@ -12,6 +12,9 @@ #include #include +typedef float float32_t; +typedef double float64_t; + /* Since KaRaMeL emits the inline keyword unconditionally, we follow the * guidelines at https://gcc.gnu.org/onlinedocs/gcc/Inline.html and make this * __inline__ to ensure the code compiles with -std=c90 and earlier. */ @@ -425,4 +428,4 @@ inline static int32_t krml_time(void) { #else # define KRML_MAYBE_FOR16(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif -#endif +#endif /* KRML_HEADER_TARGET_H */ diff --git a/Modules/_hacl/include/krml/internal/types.h b/Modules/_hacl/include/krml/internal/types.h index 2280dfad48d..d96ed19fcca 100644 --- a/Modules/_hacl/include/krml/internal/types.h +++ b/Modules/_hacl/include/krml/internal/types.h @@ -92,7 +92,7 @@ typedef FStar_UInt128_uint128 FStar_UInt128_t, uint128_t; /* Avoid a circular loop: if this header is included via FStar_UInt8_16_32_64, * then don't bring the uint128 definitions into scope. */ -#ifndef __FStar_UInt_8_16_32_64_H +#ifndef FStar_UInt_8_16_32_64_H #if !defined(KRML_VERIFIED_UINT128) && defined(IS_MSVC64) #include "fstar_uint128_msvc.h" diff --git a/Modules/_hacl/include/krml/lowstar_endianness.h b/Modules/_hacl/include/krml/lowstar_endianness.h index af6b882cf25..5f706fa51aa 100644 --- a/Modules/_hacl/include/krml/lowstar_endianness.h +++ b/Modules/_hacl/include/krml/lowstar_endianness.h @@ -1,8 +1,8 @@ /* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 and MIT Licenses. */ -#ifndef __LOWSTAR_ENDIANNESS_H -#define __LOWSTAR_ENDIANNESS_H +#ifndef KRML_HEADER_LOWSTAR_ENDIANNESS_H +#define KRML_HEADER_LOWSTAR_ENDIANNESS_H #include #include @@ -228,4 +228,4 @@ inline static void store64(uint8_t *b, uint64_t i) { #define load128_be0 load128_be #define store128_be0 store128_be -#endif +#endif /* KRML_HEADER_LOWSTAR_ENDIANNESS_H */ diff --git a/Modules/_hacl/internal/Hacl_HMAC.h b/Modules/_hacl/internal/Hacl_HMAC.h index ad29d50760c..ef9f25f5d42 100644 --- a/Modules/_hacl/internal/Hacl_HMAC.h +++ b/Modules/_hacl/internal/Hacl_HMAC.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_HMAC_H -#define __internal_Hacl_HMAC_H +#ifndef internal_Hacl_HMAC_H +#define internal_Hacl_HMAC_H #if defined(__cplusplus) extern "C" { @@ -48,5 +48,5 @@ K___uint32_t_uint32_t; } #endif -#define __internal_Hacl_HMAC_H_DEFINED -#endif +#define internal_Hacl_HMAC_H_DEFINED +#endif /* internal_Hacl_HMAC_H */ diff --git a/Modules/_hacl/internal/Hacl_Hash_Blake2b.h b/Modules/_hacl/internal/Hacl_Hash_Blake2b.h index e74c320e073..7ca8e10e34c 100644 --- a/Modules/_hacl/internal/Hacl_Hash_Blake2b.h +++ b/Modules/_hacl/internal/Hacl_Hash_Blake2b.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Hash_Blake2b_H -#define __internal_Hacl_Hash_Blake2b_H +#ifndef internal_Hacl_Hash_Blake2b_H +#define internal_Hacl_Hash_Blake2b_H #if defined(__cplusplus) extern "C" { @@ -91,5 +91,5 @@ Hacl_Hash_Blake2b_state_t; } #endif -#define __internal_Hacl_Hash_Blake2b_H_DEFINED -#endif +#define internal_Hacl_Hash_Blake2b_H_DEFINED +#endif /* internal_Hacl_Hash_Blake2b_H */ diff --git a/Modules/_hacl/internal/Hacl_Hash_Blake2b_Simd256.h b/Modules/_hacl/internal/Hacl_Hash_Blake2b_Simd256.h index 27633f22b24..6507d287922 100644 --- a/Modules/_hacl/internal/Hacl_Hash_Blake2b_Simd256.h +++ b/Modules/_hacl/internal/Hacl_Hash_Blake2b_Simd256.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Hash_Blake2b_Simd256_H -#define __internal_Hacl_Hash_Blake2b_Simd256_H +#ifndef internal_Hacl_Hash_Blake2b_Simd256_H +#define internal_Hacl_Hash_Blake2b_Simd256_H #if defined(__cplusplus) extern "C" { @@ -134,5 +134,5 @@ Hacl_Hash_Blake2b_Simd256_state_t; } #endif -#define __internal_Hacl_Hash_Blake2b_Simd256_H_DEFINED -#endif +#define internal_Hacl_Hash_Blake2b_Simd256_H_DEFINED +#endif /* internal_Hacl_Hash_Blake2b_Simd256_H */ diff --git a/Modules/_hacl/internal/Hacl_Hash_Blake2s.h b/Modules/_hacl/internal/Hacl_Hash_Blake2s.h index 0c5781df8ce..5fa5bb34efc 100644 --- a/Modules/_hacl/internal/Hacl_Hash_Blake2s.h +++ b/Modules/_hacl/internal/Hacl_Hash_Blake2s.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Hash_Blake2s_H -#define __internal_Hacl_Hash_Blake2s_H +#ifndef internal_Hacl_Hash_Blake2s_H +#define internal_Hacl_Hash_Blake2s_H #if defined(__cplusplus) extern "C" { @@ -90,5 +90,5 @@ Hacl_Hash_Blake2s_state_t; } #endif -#define __internal_Hacl_Hash_Blake2s_H_DEFINED -#endif +#define internal_Hacl_Hash_Blake2s_H_DEFINED +#endif /* internal_Hacl_Hash_Blake2s_H */ diff --git a/Modules/_hacl/internal/Hacl_Hash_Blake2s_Simd128.h b/Modules/_hacl/internal/Hacl_Hash_Blake2s_Simd128.h index 0f5db552d6c..3220cddac30 100644 --- a/Modules/_hacl/internal/Hacl_Hash_Blake2s_Simd128.h +++ b/Modules/_hacl/internal/Hacl_Hash_Blake2s_Simd128.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Hash_Blake2s_Simd128_H -#define __internal_Hacl_Hash_Blake2s_Simd128_H +#ifndef internal_Hacl_Hash_Blake2s_Simd128_H +#define internal_Hacl_Hash_Blake2s_Simd128_H #if defined(__cplusplus) extern "C" { @@ -134,5 +134,5 @@ Hacl_Hash_Blake2s_Simd128_state_t; } #endif -#define __internal_Hacl_Hash_Blake2s_Simd128_H_DEFINED -#endif +#define internal_Hacl_Hash_Blake2s_Simd128_H_DEFINED +#endif /* internal_Hacl_Hash_Blake2s_Simd128_H */ diff --git a/Modules/_hacl/internal/Hacl_Hash_MD5.h b/Modules/_hacl/internal/Hacl_Hash_MD5.h index 7fe71a49c6d..d8761de99f5 100644 --- a/Modules/_hacl/internal/Hacl_Hash_MD5.h +++ b/Modules/_hacl/internal/Hacl_Hash_MD5.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Hash_MD5_H -#define __internal_Hacl_Hash_MD5_H +#ifndef internal_Hacl_Hash_MD5_H +#define internal_Hacl_Hash_MD5_H #if defined(__cplusplus) extern "C" { @@ -53,5 +53,5 @@ void Hacl_Hash_MD5_hash_oneshot(uint8_t *output, uint8_t *input, uint32_t input_ } #endif -#define __internal_Hacl_Hash_MD5_H_DEFINED -#endif +#define internal_Hacl_Hash_MD5_H_DEFINED +#endif /* internal_Hacl_Hash_MD5_H */ diff --git a/Modules/_hacl/internal/Hacl_Hash_SHA1.h b/Modules/_hacl/internal/Hacl_Hash_SHA1.h index ed53be559fe..0e9f1c911f6 100644 --- a/Modules/_hacl/internal/Hacl_Hash_SHA1.h +++ b/Modules/_hacl/internal/Hacl_Hash_SHA1.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Hash_SHA1_H -#define __internal_Hacl_Hash_SHA1_H +#ifndef internal_Hacl_Hash_SHA1_H +#define internal_Hacl_Hash_SHA1_H #if defined(__cplusplus) extern "C" { @@ -52,5 +52,5 @@ void Hacl_Hash_SHA1_hash_oneshot(uint8_t *output, uint8_t *input, uint32_t input } #endif -#define __internal_Hacl_Hash_SHA1_H_DEFINED -#endif +#define internal_Hacl_Hash_SHA1_H_DEFINED +#endif /* internal_Hacl_Hash_SHA1_H */ diff --git a/Modules/_hacl/internal/Hacl_Hash_SHA2.h b/Modules/_hacl/internal/Hacl_Hash_SHA2.h index 98498ee9376..e6750207980 100644 --- a/Modules/_hacl/internal/Hacl_Hash_SHA2.h +++ b/Modules/_hacl/internal/Hacl_Hash_SHA2.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Hash_SHA2_H -#define __internal_Hacl_Hash_SHA2_H +#ifndef internal_Hacl_Hash_SHA2_H +#define internal_Hacl_Hash_SHA2_H #if defined(__cplusplus) extern "C" { @@ -161,5 +161,5 @@ void Hacl_Hash_SHA2_sha384_finish(uint64_t *st, uint8_t *h); } #endif -#define __internal_Hacl_Hash_SHA2_H_DEFINED -#endif +#define internal_Hacl_Hash_SHA2_H_DEFINED +#endif /* internal_Hacl_Hash_SHA2_H */ diff --git a/Modules/_hacl/internal/Hacl_Hash_SHA3.h b/Modules/_hacl/internal/Hacl_Hash_SHA3.h index e653c73b1d0..c08a4e7858b 100644 --- a/Modules/_hacl/internal/Hacl_Hash_SHA3.h +++ b/Modules/_hacl/internal/Hacl_Hash_SHA3.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Hash_SHA3_H -#define __internal_Hacl_Hash_SHA3_H +#ifndef internal_Hacl_Hash_SHA3_H +#define internal_Hacl_Hash_SHA3_H #if defined(__cplusplus) extern "C" { @@ -81,5 +81,5 @@ Hacl_Hash_SHA3_state_t; } #endif -#define __internal_Hacl_Hash_SHA3_H_DEFINED -#endif +#define internal_Hacl_Hash_SHA3_H_DEFINED +#endif /* internal_Hacl_Hash_SHA3_H */ diff --git a/Modules/_hacl/internal/Hacl_Impl_Blake2_Constants.h b/Modules/_hacl/internal/Hacl_Impl_Blake2_Constants.h index fb3a045cd5c..2726cc48478 100644 --- a/Modules/_hacl/internal/Hacl_Impl_Blake2_Constants.h +++ b/Modules/_hacl/internal/Hacl_Impl_Blake2_Constants.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Impl_Blake2_Constants_H -#define __internal_Hacl_Impl_Blake2_Constants_H +#ifndef internal_Hacl_Impl_Blake2_Constants_H +#define internal_Hacl_Impl_Blake2_Constants_H #if defined(__cplusplus) extern "C" { @@ -69,5 +69,5 @@ Hacl_Hash_Blake2b_ivTable_B[8U] = } #endif -#define __internal_Hacl_Impl_Blake2_Constants_H_DEFINED -#endif +#define internal_Hacl_Impl_Blake2_Constants_H_DEFINED +#endif /* internal_Hacl_Impl_Blake2_Constants_H */ diff --git a/Modules/_hacl/internal/Hacl_Streaming_HMAC.h b/Modules/_hacl/internal/Hacl_Streaming_HMAC.h index acc4f399602..fb44f707463 100644 --- a/Modules/_hacl/internal/Hacl_Streaming_HMAC.h +++ b/Modules/_hacl/internal/Hacl_Streaming_HMAC.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Streaming_HMAC_H -#define __internal_Hacl_Streaming_HMAC_H +#ifndef internal_Hacl_Streaming_HMAC_H +#define internal_Hacl_Streaming_HMAC_H #if defined(__cplusplus) extern "C" { @@ -90,5 +90,5 @@ Hacl_Streaming_HMAC_agile_state; } #endif -#define __internal_Hacl_Streaming_HMAC_H_DEFINED -#endif +#define internal_Hacl_Streaming_HMAC_H_DEFINED +#endif /* internal_Hacl_Streaming_HMAC_H */ diff --git a/Modules/_hacl/internal/Hacl_Streaming_Types.h b/Modules/_hacl/internal/Hacl_Streaming_Types.h index fed3cbd4259..d6c4fc0d625 100644 --- a/Modules/_hacl/internal/Hacl_Streaming_Types.h +++ b/Modules/_hacl/internal/Hacl_Streaming_Types.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Streaming_Types_H -#define __internal_Hacl_Streaming_Types_H +#ifndef internal_Hacl_Streaming_Types_H +#define internal_Hacl_Streaming_Types_H #if defined(__cplusplus) extern "C" { @@ -83,5 +83,5 @@ Hacl_Streaming_MD_state_64; } #endif -#define __internal_Hacl_Streaming_Types_H_DEFINED -#endif +#define internal_Hacl_Streaming_Types_H_DEFINED +#endif /* internal_Hacl_Streaming_Types_H */ diff --git a/Modules/_hacl/refresh.sh b/Modules/_hacl/refresh.sh index a6776282423..72ceb27b7f7 100755 --- a/Modules/_hacl/refresh.sh +++ b/Modules/_hacl/refresh.sh @@ -22,7 +22,7 @@ fi # Update this when updating to a new version after verifying that the changes # the update brings in are good. -expected_hacl_star_rev=4ef25b547b377dcef855db4289c6a00580e7221c +expected_hacl_star_rev=8ba599b2f6c9701b3dc961db895b0856a2210f76 hacl_dir="$(realpath "$1")" cd "$(dirname "$0")" From 2a2bc82cef9c6ae0b8de833e2b4aee37519de9d7 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 16 Oct 2025 10:54:41 +0300 Subject: [PATCH 184/373] gh-130567: Remove optimistic allocation in locale.strxfrm() (GH-137143) On modern systems, the result of wcsxfrm() is much larger the size of the input string (from 4+2*n on Windows to 4+5*n on Linux for simple ASCII strings), so optimistic allocation of the buffer of the same size never works. The exception is if the locale is "C" (or unset), but in that case the `wcsxfrm` call should be fast (and calling `locale.strxfrm()` doesn't make too much sense in the first place). --- Modules/_localemodule.c | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/Modules/_localemodule.c b/Modules/_localemodule.c index cb448b14d8c..7174eebd0c9 100644 --- a/Modules/_localemodule.c +++ b/Modules/_localemodule.c @@ -455,35 +455,23 @@ _locale_strxfrm_impl(PyObject *module, PyObject *str) goto exit; } - /* assume no change in size, first */ - n1 = n1 + 1; - /* Yet another +1 is needed to work around a platform bug in wcsxfrm() - * on macOS. See gh-130567. */ - buf = PyMem_New(wchar_t, n1+1); - if (!buf) { - PyErr_NoMemory(); - goto exit; - } errno = 0; - n2 = wcsxfrm(buf, s, n1); + n2 = wcsxfrm(NULL, s, 0); if (errno && errno != ERANGE) { PyErr_SetFromErrno(PyExc_OSError); goto exit; } - if (n2 >= (size_t)n1) { - /* more space needed */ - wchar_t * new_buf = PyMem_Realloc(buf, (n2+1)*sizeof(wchar_t)); - if (!new_buf) { - PyErr_NoMemory(); - goto exit; - } - buf = new_buf; - errno = 0; - n2 = wcsxfrm(buf, s, n2+1); - if (errno) { - PyErr_SetFromErrno(PyExc_OSError); - goto exit; - } + buf = PyMem_New(wchar_t, n2+1); + if (!buf) { + PyErr_NoMemory(); + goto exit; + } + + errno = 0; + n2 = wcsxfrm(buf, s, n2+1); + if (errno) { + PyErr_SetFromErrno(PyExc_OSError); + goto exit; } /* The result is just a sequence of integers, they are not necessary Unicode code points, so PyUnicode_FromWideChar() cannot be used From 7ac94fcb1d09796f55faeaf30e349a86a88f7ed6 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Thu, 16 Oct 2025 10:49:08 +0100 Subject: [PATCH 185/373] gh-140170: Fix test_site with -s flag (GH-140179) Commit --- Lib/test/test_site.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 32fcf3162e8..27ae3539b55 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -855,12 +855,15 @@ def get_excepted_output(self, *args): return 10, None def invoke_command_line(self, *args): - args = ["-m", "site", *args] + cmd_args = [] + if sys.flags.no_user_site: + cmd_args.append("-s") + cmd_args.extend(["-m", "site", *args]) with EnvironmentVarGuard() as env: env["PYTHONUTF8"] = "1" env["PYTHONIOENCODING"] = "utf-8" - proc = spawn_python(*args, text=True, env=env, + proc = spawn_python(*cmd_args, text=True, env=env, encoding='utf-8', errors='replace') output = kill_python(proc) From 4641925bf27d9ca09b88e3063a391931da3e7c0c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 16 Oct 2025 12:54:57 +0200 Subject: [PATCH 186/373] Set type names earlier in posixmodule.c (#140168) --- Lib/test/test_os/test_os.py | 14 ++++++++++++++ Modules/posixmodule.c | 15 +++++---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py index dd6f89e81aa..9bb4cb7e526 100644 --- a/Lib/test/test_os/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -164,6 +164,20 @@ def test_getcwdb(self): self.assertIsInstance(cwd, bytes) self.assertEqual(os.fsdecode(cwd), os.getcwd()) + def test_type_fqdn(self): + def fqdn(obj): + return (obj.__module__, obj.__qualname__) + + native = os.name + self.assertEqual(fqdn(os.stat_result), ("os", "stat_result")) + self.assertEqual(fqdn(os.times_result), (native, "times_result")) + if hasattr(os, "statvfs_result"): + self.assertEqual(fqdn(os.statvfs_result), ("os", "statvfs_result")) + if hasattr(os, "sched_param"): + self.assertEqual(fqdn(os.sched_param), (native, "sched_param")) + if hasattr(os, "waitid_result"): + self.assertEqual(fqdn(os.waitid_result), (native, "waitid_result")) + # Tests creating TESTFN class FileTests(unittest.TestCase): diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index e2b7146237f..5c1c508578a 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -2471,7 +2471,7 @@ static PyStructSequence_Field stat_result_fields[] = { #endif static PyStructSequence_Desc stat_result_desc = { - "stat_result", /* name */ + "os.stat_result", /* name; see issue gh-63408 */ stat_result__doc__, /* doc */ stat_result_fields, 10 @@ -2501,7 +2501,7 @@ static PyStructSequence_Field statvfs_result_fields[] = { }; static PyStructSequence_Desc statvfs_result_desc = { - "statvfs_result", /* name */ + "os.statvfs_result", /* name; see issue gh-63408 */ statvfs_result__doc__, /* doc */ statvfs_result_fields, 10 @@ -2526,7 +2526,7 @@ static PyStructSequence_Field waitid_result_fields[] = { }; static PyStructSequence_Desc waitid_result_desc = { - "waitid_result", /* name */ + MODNAME ".waitid_result", /* name */ waitid_result__doc__, /* doc */ waitid_result_fields, 5 @@ -8663,7 +8663,7 @@ static PyStructSequence_Field sched_param_fields[] = { }; static PyStructSequence_Desc sched_param_desc = { - "sched_param", /* name */ + MODNAME ".sched_param", /* name */ os_sched_param__doc__, /* doc */ sched_param_fields, 1 @@ -11057,7 +11057,7 @@ and elapsed.\n\ See os.times for more information."); static PyStructSequence_Desc times_result_desc = { - "times_result", /* name */ + MODNAME ".times_result", /* name */ times_result__doc__, /* doc */ times_result_fields, 5 @@ -18584,14 +18584,12 @@ posixmodule_exec(PyObject *m) } #if defined(HAVE_WAITID) - waitid_result_desc.name = MODNAME ".waitid_result"; state->WaitidResultType = (PyObject *)PyStructSequence_NewType(&waitid_result_desc); if (PyModule_AddObjectRef(m, "waitid_result", state->WaitidResultType) < 0) { return -1; } #endif - stat_result_desc.name = "os.stat_result"; /* see issue #19209 */ stat_result_desc.fields[7].name = PyStructSequence_UnnamedField; stat_result_desc.fields[8].name = PyStructSequence_UnnamedField; stat_result_desc.fields[9].name = PyStructSequence_UnnamedField; @@ -18602,14 +18600,12 @@ posixmodule_exec(PyObject *m) state->statresult_new_orig = ((PyTypeObject *)state->StatResultType)->tp_new; ((PyTypeObject *)state->StatResultType)->tp_new = statresult_new; - statvfs_result_desc.name = "os.statvfs_result"; /* see issue #19209 */ state->StatVFSResultType = (PyObject *)PyStructSequence_NewType(&statvfs_result_desc); if (PyModule_AddObjectRef(m, "statvfs_result", state->StatVFSResultType) < 0) { return -1; } #if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) - sched_param_desc.name = MODNAME ".sched_param"; state->SchedParamType = (PyObject *)PyStructSequence_NewType(&sched_param_desc); if (PyModule_AddObjectRef(m, "sched_param", state->SchedParamType) < 0) { return -1; @@ -18641,7 +18637,6 @@ posixmodule_exec(PyObject *m) return -1; } - times_result_desc.name = MODNAME ".times_result"; state->TimesResultType = (PyObject *)PyStructSequence_NewType(×_result_desc); if (PyModule_AddObjectRef(m, "times_result", state->TimesResultType) < 0) { return -1; From ea4cc585cd12ed73e5fe9978f943ceff5bb5cd51 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 16 Oct 2025 13:04:04 +0200 Subject: [PATCH 187/373] gh-139817: Fix refleak in TypeAliasType(qualname=non_string) (GH-140197) --- Objects/typevarobject.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Objects/typevarobject.c b/Objects/typevarobject.c index 8a3a1e98345..75a69d4bc3e 100644 --- a/Objects/typevarobject.c +++ b/Objects/typevarobject.c @@ -2123,11 +2123,6 @@ typealias_new_impl(PyTypeObject *type, PyObject *name, PyObject *value, return NULL; } - PyObject *module = caller(); - if (module == NULL) { - return NULL; - } - if (qualname == NULL || qualname == Py_None) { // If qualname was not set directly, we use name instead. qualname = name; @@ -2138,6 +2133,11 @@ typealias_new_impl(PyTypeObject *type, PyObject *name, PyObject *value, } } + PyObject *module = caller(); + if (module == NULL) { + return NULL; + } + PyObject *ta = (PyObject *)typealias_alloc( name, qualname, checked_params, NULL, value, module); Py_DECREF(module); From 5a31024da45eb00f5450269cad4481131d77c81f Mon Sep 17 00:00:00 2001 From: Jeffrey Bosboom Date: Thu, 16 Oct 2025 04:40:47 -0700 Subject: [PATCH 188/373] gh-83714: Check for struct statx.stx_atomic_write_unit_max_opt in configure (#140185) stx_atomic_write_unit_max_opt was added in Linux 6.16, but is controlled by the STATX_WRITE_ATOMIC mask bit added in Linux 6.11. That's safe at runtime because all kernels clear the reserved space in struct statx and zero is a valid value for stx_atomic_write_unit_max_opt, and it avoids allocating another mask bit, which are a limited resource. But it also means the kernel headers don't provide a way to check whether stx_atomic_write_unit_max_opt exists, so add a configure check. --- Doc/library/os.rst | 2 +- Modules/posixmodule.c | 2 +- configure | 15 +++++++++++++++ configure.ac | 7 +++++++ pyconfig.h.in | 4 ++++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 1ac87b32bad..c3ed6a97692 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3496,7 +3496,7 @@ features: Maximum optimized size for direct I/O with torn-write protection. .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel - userspace API headers >= 6.11. + userspace API headers >= 6.16. .. attribute:: stx_atomic_write_segments_max diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 5c1c508578a..1338be22e18 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -3369,7 +3369,7 @@ static PyMemberDef pystatx_result_members[] = { MM(stx_atomic_write_segments_max, Py_T_UINT, atomic_write_segments_max, "maximum iovecs for direct I/O with torn-write protection"), #endif -#if 0 +#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT MM(stx_atomic_write_unit_max_opt, Py_T_UINT, atomic_write_unit_max_opt, "maximum optimized size for direct I/O with torn-write protection"), #endif diff --git a/configure b/configure index 28005cd1924..267981250cd 100755 --- a/configure +++ b/configure @@ -25133,6 +25133,21 @@ printf "%s\n" "#define HAVE_SIGINFO_T_SI_BAND 1" >>confdefs.h fi +if test "$ac_cv_func_statx" = yes; then + # stx_atomic_write_unit_max_opt was added in Linux 6.16, but is controlled by + # the STATX_WRITE_ATOMIC mask bit added in Linux 6.11, so having the mask bit + # doesn't imply having the member. + ac_fn_c_check_member "$LINENO" "struct statx" "stx_atomic_write_unit_max_opt" "ac_cv_member_struct_statx_stx_atomic_write_unit_max_opt" "$ac_includes_default" +if test "x$ac_cv_member_struct_statx_stx_atomic_write_unit_max_opt" = xyes +then : + +printf "%s\n" "#define HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT 1" >>confdefs.h + + +fi + +fi + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for time.h that defines altzone" >&5 printf %s "checking for time.h that defines altzone... " >&6; } if test ${ac_cv_header_time_altzone+y} diff --git a/configure.ac b/configure.ac index d20f6f8c40a..382591952ef 100644 --- a/configure.ac +++ b/configure.ac @@ -5819,6 +5819,13 @@ AC_CHECK_MEMBERS([struct passwd.pw_gecos, struct passwd.pw_passwd], [], [], [[ # Issue #21085: In Cygwin, siginfo_t does not have si_band field. AC_CHECK_MEMBERS([siginfo_t.si_band], [], [], [[@%:@include ]]) +if test "$ac_cv_func_statx" = yes; then + # stx_atomic_write_unit_max_opt was added in Linux 6.16, but is controlled by + # the STATX_WRITE_ATOMIC mask bit added in Linux 6.11, so having the mask bit + # doesn't imply having the member. + AC_CHECK_MEMBERS([struct statx.stx_atomic_write_unit_max_opt]) +fi + AC_CACHE_CHECK([for time.h that defines altzone], [ac_cv_header_time_altzone], [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], [[return altzone;]])], [ac_cv_header_time_altzone=yes], diff --git a/pyconfig.h.in b/pyconfig.h.in index 611408d88f0..092f96e7152 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1330,6 +1330,10 @@ /* Define to 1 if 'pw_passwd' is a member of 'struct passwd'. */ #undef HAVE_STRUCT_PASSWD_PW_PASSWD +/* Define to 1 if 'stx_atomic_write_unit_max_opt' is a member of 'struct + statx'. */ +#undef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT + /* Define to 1 if 'st_birthtime' is a member of 'struct stat'. */ #undef HAVE_STRUCT_STAT_ST_BIRTHTIME From 379fd020a0116754f22b04fa2f7f27a8f7b372b0 Mon Sep 17 00:00:00 2001 From: bzoracler <50305397+bzoracler@users.noreply.github.com> Date: Fri, 17 Oct 2025 01:30:36 +1300 Subject: [PATCH 189/373] =?UTF-8?q?gh-138859:=20Account=20for=20`ParamSpec?= =?UTF-8?q?`=20defaults=20that=20are=20not=20lists=20=E2=80=A6=20(#138868)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/test/test_typing.py | 10 ++++++++++ Lib/typing.py | 2 +- .../2025-09-13-12-19-17.gh-issue-138859.PxjIoN.rst | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-09-13-12-19-17.gh-issue-138859.PxjIoN.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index bc7f14f90a7..db0501d70e3 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -762,6 +762,16 @@ class A(Generic[T, P, U]): ... self.assertEqual(A[float, [range]].__args__, (float, (range,), float)) self.assertEqual(A[float, [range], int].__args__, (float, (range,), int)) + def test_paramspec_and_typevar_specialization_2(self): + T = TypeVar("T") + P = ParamSpec('P', default=...) + U = TypeVar("U", default=float) + self.assertEqual(P.__default__, ...) + class A(Generic[T, P, U]): ... + self.assertEqual(A[float].__args__, (float, ..., float)) + self.assertEqual(A[float, [range]].__args__, (float, (range,), float)) + self.assertEqual(A[float, [range], int].__args__, (float, (range,), int)) + def test_typevartuple_none(self): U = TypeVarTuple('U') U_None = TypeVarTuple('U_None', default=None) diff --git a/Lib/typing.py b/Lib/typing.py index 03d5357d4cf..25234d2d707 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1113,7 +1113,7 @@ def _paramspec_prepare_subst(self, alias, args): params = alias.__parameters__ i = params.index(self) if i == len(args) and self.has_default(): - args = [*args, self.__default__] + args = (*args, self.__default__) if i >= len(args): raise TypeError(f"Too few arguments for {alias}") # Special case where Z[[int, str, bool]] == Z[int, str, bool] in PEP 612. diff --git a/Misc/NEWS.d/next/Library/2025-09-13-12-19-17.gh-issue-138859.PxjIoN.rst b/Misc/NEWS.d/next/Library/2025-09-13-12-19-17.gh-issue-138859.PxjIoN.rst new file mode 100644 index 00000000000..a5d4dd042fc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-13-12-19-17.gh-issue-138859.PxjIoN.rst @@ -0,0 +1 @@ +Fix generic type parameterization raising a :exc:`TypeError` when omitting a :class:`ParamSpec` that has a default which is not a list of types. From 5f357f3b0de0d937afb0154d0df7e0298c2523cf Mon Sep 17 00:00:00 2001 From: wangxiaolei Date: Thu, 16 Oct 2025 23:02:38 +0800 Subject: [PATCH 190/373] gh-140078: fix typo in tkinter docs (#140081) Remove extraneous word. --- Doc/library/tkinter.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst index f284988daf2..be26af02bce 100644 --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -392,7 +392,7 @@ by spaces. Without getting into too many details, notice the following: * Operations which are implemented as separate *commands* in Tcl (like ``grid`` or ``destroy``) are represented as *methods* on Tkinter widget objects. As you'll see shortly, at other times Tcl uses what appear to be - method calls on widget objects, which more closely mirror what would is + method calls on widget objects, which more closely mirror what is used in Tkinter. From 459d493ce3288cda7dcebb868970b199764502f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurycy=20Paw=C5=82owski-Wiero=C5=84ski?= <5383+maurycy@users.noreply.github.com> Date: Thu, 16 Oct 2025 19:24:34 +0200 Subject: [PATCH 191/373] gh-140149: Use PyBytesWriter in _build_concatenated_bytes() (#140150) Use PyBytesWriter in action_helpers.c _build_concatenated_bytes(). 3x faster bytes concat in the parser. Co-authored-by: Victor Stinner --- ...-10-15-17-12-32.gh-issue-140149.cy1m3d.rst | 2 + Parser/action_helpers.c | 39 ++++++++++++++++--- 2 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-15-17-12-32.gh-issue-140149.cy1m3d.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-15-17-12-32.gh-issue-140149.cy1m3d.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-15-17-12-32.gh-issue-140149.cy1m3d.rst new file mode 100644 index 00000000000..e98e28802cf --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-15-17-12-32.gh-issue-140149.cy1m3d.rst @@ -0,0 +1,2 @@ +Speed up parsing bytes literals concatenation by using PyBytesWriter API and +a single memory allocation (about 3x faster). diff --git a/Parser/action_helpers.c b/Parser/action_helpers.c index 57e46b4399c..b7a5b9d5e30 100644 --- a/Parser/action_helpers.c +++ b/Parser/action_helpers.c @@ -1612,19 +1612,46 @@ _build_concatenated_bytes(Parser *p, asdl_expr_seq *strings, int lineno, Py_ssize_t len = asdl_seq_LEN(strings); assert(len > 0); - PyObject* res = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); - /* Bytes literals never get a kind, but just for consistency since they are represented as Constant nodes, we'll mirror the same behavior as unicode strings for determining the kind. */ - PyObject* kind = asdl_seq_GET(strings, 0)->v.Constant.kind; + PyObject *kind = asdl_seq_GET(strings, 0)->v.Constant.kind; + + Py_ssize_t total = 0; for (Py_ssize_t i = 0; i < len; i++) { expr_ty elem = asdl_seq_GET(strings, i); - PyBytes_Concat(&res, elem->v.Constant.value); + PyObject *bytes = elem->v.Constant.value; + Py_ssize_t part = PyBytes_GET_SIZE(bytes); + if (part > PY_SSIZE_T_MAX - total) { + PyErr_NoMemory(); + return NULL; + } + total += part; } - if (!res || _PyArena_AddPyObject(arena, res) < 0) { - Py_XDECREF(res); + + PyBytesWriter *writer = PyBytesWriter_Create(total); + if (writer == NULL) { + return NULL; + } + char *out = PyBytesWriter_GetData(writer); + + for (Py_ssize_t i = 0; i < len; i++) { + expr_ty elem = asdl_seq_GET(strings, i); + PyObject *bytes = elem->v.Constant.value; + Py_ssize_t part = PyBytes_GET_SIZE(bytes); + if (part > 0) { + memcpy(out, PyBytes_AS_STRING(bytes), part); + out += part; + } + } + + PyObject *res = PyBytesWriter_Finish(writer); + if (res == NULL) { + return NULL; + } + if (_PyArena_AddPyObject(arena, res) < 0) { + Py_DECREF(res); return NULL; } return _PyAST_Constant(res, kind, lineno, col_offset, end_lineno, end_col_offset, p->arena); From 2ebd0cdb16a8824957ea588e1aab0a35d45e6b7b Mon Sep 17 00:00:00 2001 From: Tan Long Date: Fri, 17 Oct 2025 01:27:00 +0800 Subject: [PATCH 192/373] Remove duplicate words in the documentation (#140221) --- Doc/c-api/stable.rst | 2 +- Doc/library/importlib.rst | 2 +- Doc/library/stdtypes.rst | 2 +- Doc/whatsnew/3.14.rst | 4 ++-- Doc/whatsnew/3.15.rst | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Doc/c-api/stable.rst b/Doc/c-api/stable.rst index 290166696dd..b08a7bf1b2f 100644 --- a/Doc/c-api/stable.rst +++ b/Doc/c-api/stable.rst @@ -294,7 +294,7 @@ The full API is described below for advanced use cases. Default flags, based on current values of macros such as :c:macro:`Py_LIMITED_API` and :c:macro:`Py_GIL_DISABLED`. - Alternately, the field can be set to to the following flags, combined + Alternately, the field can be set to the following flags, combined by bitwise OR. Unused bits must be set to zero. diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 7eb048fcfc2..19296eb247a 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1257,7 +1257,7 @@ find and load modules. To accommodate this requirement, when running on iOS, extension module binaries are *not* packaged as ``.so`` files on ``sys.path``, but as individual standalone frameworks. To discover those frameworks, this loader - is be registered against the ``.fwork`` file extension, with a ``.fwork`` + is registered against the ``.fwork`` file extension, with a ``.fwork`` file acting as a placeholder in the original location of the binary on ``sys.path``. The ``.fwork`` file contains the path of the actual binary in the ``Frameworks`` folder, relative to the app bundle. To allow for diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 2c5b721093d..97e7e08364e 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -5944,7 +5944,7 @@ It is written as ``None``. The Ellipsis Object ------------------- -This object is commonly used used to indicate that something is omitted. +This object is commonly used to indicate that something is omitted. It supports no special operations. There is exactly one ellipsis object, named :const:`Ellipsis` (a built-in name). ``type(Ellipsis)()`` produces the :const:`Ellipsis` singleton. diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 677365c2f59..1a2fbda0c4c 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1140,7 +1140,7 @@ concurrent.futures .. _whatsnew314-concurrent-futures-start-method: * On Unix platforms other than macOS, :ref:`'forkserver' - ` is now the the default :ref:`start + ` is now the default :ref:`start method ` for :class:`~concurrent.futures.ProcessPoolExecutor` (replacing :ref:`'fork' `). @@ -1591,7 +1591,7 @@ multiprocessing .. _whatsnew314-multiprocessing-start-method: * On Unix platforms other than macOS, :ref:`'forkserver' - ` is now the the default :ref:`start + ` is now the default :ref:`start method ` (replacing :ref:`'fork' `). This change does not affect Windows or macOS, where :ref:`'spawn' diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 56028a92aa2..d3ae7c21a03 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -1010,7 +1010,7 @@ Deprecated C APIs use the :c:type:`PyBytesWriter` API instead. (Contributed by Victor Stinner in :gh:`129813`.) -* Deprecate :c:member:`~PyComplexObject.cval` field of the the +* Deprecate :c:member:`~PyComplexObject.cval` field of the :c:type:`PyComplexObject` type. Use :c:func:`PyComplex_AsCComplex` and :c:func:`PyComplex_FromCComplex` to convert a Python complex number to/from the C :c:type:`Py_complex` From 9a87ce8b57f5d698900d84b4f6f5aa47b5f37a89 Mon Sep 17 00:00:00 2001 From: Alper Date: Thu, 16 Oct 2025 11:27:51 -0700 Subject: [PATCH 193/373] gh-116738: test `uuid` module thread safety in free-threading (#140068) --- Lib/test/test_free_threading/test_uuid.py | 60 +++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100755 Lib/test/test_free_threading/test_uuid.py diff --git a/Lib/test/test_free_threading/test_uuid.py b/Lib/test/test_free_threading/test_uuid.py new file mode 100755 index 00000000000..d794afc552a --- /dev/null +++ b/Lib/test/test_free_threading/test_uuid.py @@ -0,0 +1,60 @@ +import os +import unittest + +from test.support import import_helper, threading_helper +from test.support.threading_helper import run_concurrently +from uuid import SafeUUID + +c_uuid = import_helper.import_module("_uuid") + +NTHREADS = 10 +UUID_PER_THREAD = 1000 + + +@threading_helper.requires_working_threading() +class UUIDTests(unittest.TestCase): + @unittest.skipUnless(os.name == "posix", "POSIX only") + def test_generate_time_safe(self): + uuids = [] + + def worker(): + local_uuids = [] + for _ in range(UUID_PER_THREAD): + uuid, is_safe = c_uuid.generate_time_safe() + self.assertIs(type(uuid), bytes) + self.assertEqual(len(uuid), 16) + # Collect the UUID only if it is safe. If not, we cannot ensure + # UUID uniqueness. According to uuid_generate_time_safe() man + # page, it is theoretically possible for two concurrently + # running processes to generate the same UUID(s) if the return + # value is not 0. + if is_safe == SafeUUID.safe: + local_uuids.append(uuid) + + # Merge all safe uuids + uuids.extend(local_uuids) + + run_concurrently(worker_func=worker, nthreads=NTHREADS) + self.assertEqual(len(uuids), len(set(uuids))) + + @unittest.skipUnless(os.name == "nt", "Windows only") + def test_UuidCreate(self): + uuids = [] + + def worker(): + local_uuids = [] + for _ in range(UUID_PER_THREAD): + uuid = c_uuid.UuidCreate() + self.assertIs(type(uuid), bytes) + self.assertEqual(len(uuid), 16) + local_uuids.append(uuid) + + # Merge all uuids + uuids.extend(local_uuids) + + run_concurrently(worker_func=worker, nthreads=NTHREADS) + self.assertEqual(len(uuids), len(set(uuids))) + + +if __name__ == "__main__": + unittest.main() From 869bb6948eb501b19db53cb27c523c3203a5ab11 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Fri, 17 Oct 2025 07:55:12 +0100 Subject: [PATCH 194/373] Standardize translation of `Doc/bugs.rst` (GH-137449) --- Doc/bugs.rst | 6 ++++++ Doc/conf.py | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/Doc/bugs.rst b/Doc/bugs.rst index faf13eeb6a7..0683eebbaf6 100644 --- a/Doc/bugs.rst +++ b/Doc/bugs.rst @@ -19,6 +19,12 @@ If you find a bug in this documentation or would like to propose an improvement, please submit a bug report on the :ref:`issue tracker `. If you have a suggestion on how to fix it, include that as well. +.. only:: translation + + If the bug or suggested improvement concerns the translation of this + documentation, submit the report to the + `translation’s repository `_ instead. + You can also open a discussion item on our `Documentation Discourse forum `_. diff --git a/Doc/conf.py b/Doc/conf.py index c1b07df08b1..a8e376c0ae4 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -444,6 +444,25 @@ # https://github.com/sphinx-doc/sphinx/issues/12359 epub_use_index = False +# translation tag +# --------------- + +language_code = None +for arg in sys.argv: + if arg.startswith('language='): + language_code = arg.split('=', 1)[1] + +if language_code: + tags.add('translation') # noqa: F821 + + rst_epilog += f"""\ +.. _TRANSLATION_REPO: https://github.com/python/python-docs-{language_code.replace("_", "-").lower()} +""" # noqa: F821 +else: + rst_epilog += """\ +.. _TRANSLATION_REPO: https://github.com/python +""" + # Options for the coverage checker # -------------------------------- From 999ab8926bd344a3c7f73b130ac773e2462f8320 Mon Sep 17 00:00:00 2001 From: Albert N Date: Fri, 17 Oct 2025 11:48:53 +0300 Subject: [PATCH 195/373] gh-133059: Fix `Tools/build/deepfreeze.py` for new nsmallposints (#139906) --- .github/workflows/mypy.yml | 1 + Tools/build/consts_getter.py | 20 ++++++++++++++++++++ Tools/build/deepfreeze.py | 5 ++++- Tools/build/generate_global_objects.py | 17 +++-------------- Tools/build/mypy.ini | 1 + 5 files changed, 29 insertions(+), 15 deletions(-) create mode 100644 Tools/build/consts_getter.py diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 5d5d77f29f6..fac0fa8aba3 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -16,6 +16,7 @@ on: - "Tools/build/check_extension_modules.py" - "Tools/build/check_warnings.py" - "Tools/build/compute-changes.py" + - "Tools/build/consts_getter.py" - "Tools/build/deepfreeze.py" - "Tools/build/generate-build-details.py" - "Tools/build/generate_sbom.py" diff --git a/Tools/build/consts_getter.py b/Tools/build/consts_getter.py new file mode 100644 index 00000000000..4cd454448ba --- /dev/null +++ b/Tools/build/consts_getter.py @@ -0,0 +1,20 @@ +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +INTERNAL = ROOT / "Include" / "internal" + +def get_nsmallnegints_and_nsmallposints() -> tuple[int, int]: + nsmallposints = None + nsmallnegints = None + with open(INTERNAL / "pycore_runtime_structs.h") as infile: + for line in infile: + if line.startswith("#define _PY_NSMALLPOSINTS"): + nsmallposints = int(line.split()[-1]) + elif line.startswith("#define _PY_NSMALLNEGINTS"): + nsmallnegints = int(line.split()[-1]) + break + else: + raise NotImplementedError + assert nsmallposints + assert nsmallnegints + return nsmallnegints, nsmallposints diff --git a/Tools/build/deepfreeze.py b/Tools/build/deepfreeze.py index 2b9f03aebb6..477c3d0f5b3 100644 --- a/Tools/build/deepfreeze.py +++ b/Tools/build/deepfreeze.py @@ -17,6 +17,7 @@ import time import types +import consts_getter import umarshal TYPE_CHECKING = False @@ -362,7 +363,9 @@ def _generate_int_for_bits(self, name: str, i: int, digit: int) -> None: self.write(f".ob_digit = {{ {ds} }},") def generate_int(self, name: str, i: int) -> str: - if -5 <= i <= 256: + nsmallnegints, nsmallposints = consts_getter.get_nsmallnegints_and_nsmallposints() + + if -nsmallnegints <= i <= nsmallposints: return f"(PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + {i}]" if i >= 0: name = f"const_int_{i}" diff --git a/Tools/build/generate_global_objects.py b/Tools/build/generate_global_objects.py index 94905b3756d..5b188e338ba 100644 --- a/Tools/build/generate_global_objects.py +++ b/Tools/build/generate_global_objects.py @@ -3,6 +3,8 @@ import os.path import re +import consts_getter + SCRIPT_NAME = 'Tools/build/generate_global_objects.py' __file__ = os.path.abspath(__file__) ROOT = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) @@ -274,20 +276,7 @@ def generate_global_strings(identifiers, strings): def generate_runtime_init(identifiers, strings): - # First get some info from the declarations. - nsmallposints = None - nsmallnegints = None - with open(os.path.join(INTERNAL, 'pycore_runtime_structs.h')) as infile: - for line in infile: - if line.startswith('#define _PY_NSMALLPOSINTS'): - nsmallposints = int(line.split()[-1]) - elif line.startswith('#define _PY_NSMALLNEGINTS'): - nsmallnegints = int(line.split()[-1]) - break - else: - raise NotImplementedError - assert nsmallposints - assert nsmallnegints + nsmallnegints, nsmallposints = consts_getter.get_nsmallnegints_and_nsmallposints() # Then target the runtime initializer. filename = os.path.join(INTERNAL, 'pycore_runtime_init_generated.h') diff --git a/Tools/build/mypy.ini b/Tools/build/mypy.ini index 331bada6f47..7d341afd1cd 100644 --- a/Tools/build/mypy.ini +++ b/Tools/build/mypy.ini @@ -6,6 +6,7 @@ files = Tools/build/check_extension_modules.py, Tools/build/check_warnings.py, Tools/build/compute-changes.py, + Tools/build/consts_getter.py, Tools/build/deepfreeze.py, Tools/build/generate-build-details.py, Tools/build/generate_sbom.py, From f1883852ed4cd1923e619e71437641d06873503d Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 17 Oct 2025 11:26:17 +0100 Subject: [PATCH 196/373] GH-135904: Implement assembler optimization for AArch64. (GH-139855) --- Python/jit.c | 27 ++++++++++++++++++----- Tools/jit/_optimizers.py | 47 ++++++++++++++++++++++++++++++++++++++-- Tools/jit/_schema.py | 2 ++ Tools/jit/_stencils.py | 2 ++ Tools/jit/_targets.py | 5 +++-- 5 files changed, 74 insertions(+), 9 deletions(-) diff --git a/Python/jit.c b/Python/jit.c index 01ec9c1fa6e..ebd0d90385e 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -167,11 +167,13 @@ set_bits(uint32_t *loc, uint8_t loc_start, uint64_t value, uint8_t value_start, // See https://developer.arm.com/documentation/ddi0602/2023-09/Base-Instructions // for instruction encodings: -#define IS_AARCH64_ADD_OR_SUB(I) (((I) & 0x11C00000) == 0x11000000) -#define IS_AARCH64_ADRP(I) (((I) & 0x9F000000) == 0x90000000) -#define IS_AARCH64_BRANCH(I) (((I) & 0x7C000000) == 0x14000000) -#define IS_AARCH64_LDR_OR_STR(I) (((I) & 0x3B000000) == 0x39000000) -#define IS_AARCH64_MOV(I) (((I) & 0x9F800000) == 0x92800000) +#define IS_AARCH64_ADD_OR_SUB(I) (((I) & 0x11C00000) == 0x11000000) +#define IS_AARCH64_ADRP(I) (((I) & 0x9F000000) == 0x90000000) +#define IS_AARCH64_BRANCH(I) (((I) & 0x7C000000) == 0x14000000) +#define IS_AARCH64_BRANCH_COND(I) (((I) & 0x7C000000) == 0x54000000) +#define IS_AARCH64_TEST_AND_BRANCH(I) (((I) & 0x7E000000) == 0x36000000) +#define IS_AARCH64_LDR_OR_STR(I) (((I) & 0x3B000000) == 0x39000000) +#define IS_AARCH64_MOV(I) (((I) & 0x9F800000) == 0x92800000) // LLD is a great reference for performing relocations... just keep in // mind that Tools/jit/build.py does filtering and preprocessing for us! @@ -332,6 +334,21 @@ patch_aarch64_21rx(unsigned char *location, uint64_t value) patch_aarch64_21r(location, value); } +// 21-bit relative branch. +void +patch_aarch64_19r(unsigned char *location, uint64_t value) +{ + uint32_t *loc32 = (uint32_t *)location; + assert(IS_AARCH64_BRANCH_COND(*loc32)); + value -= (uintptr_t)location; + // Check that we're not out of range of 21 signed bits: + assert((int64_t)value >= -(1 << 20)); + assert((int64_t)value < (1 << 20)); + // Since instructions are 4-byte aligned, only use 19 bits: + assert(get_bits(value, 0, 2) == 0); + set_bits(loc32, 5, value, 2, 19); +} + // 28-bit relative branch. void patch_aarch64_26r(unsigned char *location, uint64_t value) diff --git a/Tools/jit/_optimizers.py b/Tools/jit/_optimizers.py index 33db110b728..866417398b0 100644 --- a/Tools/jit/_optimizers.py +++ b/Tools/jit/_optimizers.py @@ -39,6 +39,34 @@ # Update with all of the inverted branches, too: _X86_BRANCHES |= {v: k for k, v in _X86_BRANCHES.items() if v} +_AARCH64_COND_CODES = { + # https://developer.arm.com/documentation/dui0801/b/CJAJIHAD?lang=en + "eq": "ne", + "ne": "eq", + "lt": "ge", + "ge": "lt", + "gt": "le", + "le": "gt", + "vs": "vc", + "vc": "vs", + "mi": "pl", + "pl": "mi", + "cs": "cc", + "cc": "cs", + "hs": "lo", + "lo": "hs", + "hi": "ls", + "ls": "hi", +} +# Branches are either b.{cond} or bc.{cond} +_AARCH64_BRANCHES = { + "b." + cond: ("b." + inverse if inverse else None) + for (cond, inverse) in _AARCH64_COND_CODES.items() +} | { + "bc." + cond: ("bc." + inverse if inverse else None) + for (cond, inverse) in _AARCH64_COND_CODES.items() +} + @dataclasses.dataclass class _Block: @@ -283,13 +311,28 @@ def run(self) -> None: self.path.write_text(self._body()) -class OptimizerAArch64(Optimizer): # pylint: disable = too-few-public-methods - """aarch64-apple-darwin/aarch64-pc-windows-msvc/aarch64-unknown-linux-gnu""" +# Mach-O does not support the 19 bit branch locations needed for branch reordering +class OptimizerAArch64_MachO(Optimizer): # pylint: disable = too-few-public-methods + """aarch64-apple-darwin""" # https://developer.arm.com/documentation/ddi0602/2025-03/Base-Instructions/B--Branch- _re_jump = re.compile(r"\s*b\s+(?P[\w.]+)") +class OptimizerAArch64(Optimizer): # pylint: disable = too-few-public-methods + """aarch64-pc-windows-msvc/aarch64-unknown-linux-gnu""" + + _branches = _AARCH64_BRANCHES + _re_branch = re.compile( + rf"\s*(?P{'|'.join(_AARCH64_BRANCHES)})\s+(.+,\s+)*(?P[\w.]+)" + ) + + # https://developer.arm.com/documentation/ddi0602/2025-03/Base-Instructions/B--Branch- + _re_jump = re.compile(r"\s*b\s+(?P[\w.]+)") + # https://developer.arm.com/documentation/ddi0602/2025-09/Base-Instructions/RET--Return-from-subroutine- + _re_return = re.compile(r"\s*ret\b") + + class OptimizerX86(Optimizer): # pylint: disable = too-few-public-methods """i686-pc-windows-msvc/x86_64-apple-darwin/x86_64-unknown-linux-gnu""" diff --git a/Tools/jit/_schema.py b/Tools/jit/_schema.py index 228fc389584..c47e9af924a 100644 --- a/Tools/jit/_schema.py +++ b/Tools/jit/_schema.py @@ -10,6 +10,7 @@ "ARM64_RELOC_PAGEOFF12", "ARM64_RELOC_UNSIGNED", "IMAGE_REL_AMD64_REL32", + "IMAGE_REL_ARM64_BRANCH19", "IMAGE_REL_ARM64_BRANCH26", "IMAGE_REL_ARM64_PAGEBASE_REL21", "IMAGE_REL_ARM64_PAGEOFFSET_12A", @@ -20,6 +21,7 @@ "R_AARCH64_ADR_GOT_PAGE", "R_AARCH64_ADR_PREL_PG_HI21", "R_AARCH64_CALL26", + "R_AARCH64_CONDBR19", "R_AARCH64_JUMP26", "R_AARCH64_ADD_ABS_LO12_NC", "R_AARCH64_LD64_GOT_LO12_NC", diff --git a/Tools/jit/_stencils.py b/Tools/jit/_stencils.py index 14606b036db..16bc1ea4e17 100644 --- a/Tools/jit/_stencils.py +++ b/Tools/jit/_stencils.py @@ -61,6 +61,7 @@ class HoleValue(enum.Enum): # x86_64-pc-windows-msvc: "IMAGE_REL_AMD64_REL32": "patch_x86_64_32rx", # aarch64-pc-windows-msvc: + "IMAGE_REL_ARM64_BRANCH19": "patch_aarch64_19r", "IMAGE_REL_ARM64_BRANCH26": "patch_aarch64_26r", "IMAGE_REL_ARM64_PAGEBASE_REL21": "patch_aarch64_21rx", "IMAGE_REL_ARM64_PAGEOFFSET_12A": "patch_aarch64_12", @@ -74,6 +75,7 @@ class HoleValue(enum.Enum): "R_AARCH64_ADR_GOT_PAGE": "patch_aarch64_21rx", "R_AARCH64_ADR_PREL_PG_HI21": "patch_aarch64_21r", "R_AARCH64_CALL26": "patch_aarch64_26r", + "R_AARCH64_CONDBR19": "patch_aarch64_19r", "R_AARCH64_JUMP26": "patch_aarch64_26r", "R_AARCH64_LD64_GOT_LO12_NC": "patch_aarch64_12x", "R_AARCH64_MOVW_UABS_G0_NC": "patch_aarch64_16a", diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index 9fc3522d23d..7ff7c4fba49 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -335,7 +335,8 @@ def _handle_relocation( "Offset": offset, "Symbol": s, "Type": { - "Name": "IMAGE_REL_ARM64_BRANCH26" + "Name": "IMAGE_REL_ARM64_BRANCH19" + | "IMAGE_REL_ARM64_BRANCH26" | "IMAGE_REL_ARM64_PAGEBASE_REL21" | "IMAGE_REL_ARM64_PAGEOFFSET_12A" | "IMAGE_REL_ARM64_PAGEOFFSET_12L" as kind @@ -564,7 +565,7 @@ def get_target(host: str) -> _COFF32 | _COFF64 | _ELF | _MachO: if re.fullmatch(r"aarch64-apple-darwin.*", host): host = "aarch64-apple-darwin" condition = "defined(__aarch64__) && defined(__APPLE__)" - optimizer = _optimizers.OptimizerAArch64 + optimizer = _optimizers.OptimizerAArch64_MachO target = _MachO(host, condition, optimizer=optimizer) elif re.fullmatch(r"aarch64-pc-windows-msvc", host): host = "aarch64-pc-windows-msvc" From 67c98ad8ef5530ddfd332a7e9d4257791dc35b1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20D=C4=85dela?= <39437649+tdadela@users.noreply.github.com> Date: Fri, 17 Oct 2025 12:32:02 +0200 Subject: [PATCH 197/373] gh-133059: Update documentation of preallocated integer range in `long.rst` (GH-140231) --- Doc/c-api/long.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index 8370dcecad3..fcb20f7c93c 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -43,7 +43,7 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. impl-detail:: CPython keeps an array of integer objects for all integers - between ``-5`` and ``256``. When you create an int in that range + between ``-5`` and ``1024``. When you create an int in that range you actually just get back a reference to the existing object. From fbf0843e39e01ec8a8295f6475065b08053f13dd Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 17 Oct 2025 14:21:39 +0300 Subject: [PATCH 198/373] gh-135801: Add tests for filtering warnings by module (GH-140240) --- Lib/test/test_warnings/__init__.py | 79 ++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index 260fae8fe24..157852cfa91 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -241,6 +241,85 @@ def test_once(self): 42) self.assertEqual(len(w), 0) + def test_filter_module(self): + MS_WINDOWS = (sys.platform == 'win32') + with self.module.catch_warnings(record=True) as w: + self.module.simplefilter('error') + self.module.filterwarnings('always', module=r'package\.module\z') + self.module.warn_explicit('msg', UserWarning, 'filename', 42, + module='package.module') + self.assertEqual(len(w), 1) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, '/path/to/package/module', 42) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, '/path/to/package/module.py', 42) + + with self.module.catch_warnings(record=True) as w: + self.module.simplefilter('error') + self.module.filterwarnings('always', module='package') + self.module.warn_explicit('msg', UserWarning, 'filename', 42, + module='package.module') + self.assertEqual(len(w), 1) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, 'filename', 42, + module='other.package.module') + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, '/path/to/otherpackage/module.py', 42) + + with self.module.catch_warnings(record=True) as w: + self.module.simplefilter('error') + self.module.filterwarnings('always', module=r'/path/to/package/module\z') + self.module.warn_explicit('msg', UserWarning, '/path/to/package/module', 42) + self.assertEqual(len(w), 1) + self.module.warn_explicit('msg', UserWarning, '/path/to/package/module.py', 42) + self.assertEqual(len(w), 2) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, '/PATH/TO/PACKAGE/MODULE', 42) + if MS_WINDOWS: + if self.module is py_warnings: + self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module.PY', 42) + self.assertEqual(len(w), 3) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module/__init__.py', 42) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module.pyw', 42) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, r'\path\to\package\module', 42) + + with self.module.catch_warnings(record=True) as w: + self.module.simplefilter('error') + self.module.filterwarnings('always', module=r'/path/to/package/__init__\z') + self.module.warn_explicit('msg', UserWarning, '/path/to/package/__init__.py', 42) + self.assertEqual(len(w), 1) + self.module.warn_explicit('msg', UserWarning, '/path/to/package/__init__', 42) + self.assertEqual(len(w), 2) + + if MS_WINDOWS: + with self.module.catch_warnings(record=True) as w: + self.module.simplefilter('error') + self.module.filterwarnings('always', module=r'C:\\path\\to\\package\\module\z') + self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module', 42) + self.assertEqual(len(w), 1) + self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.py', 42) + self.assertEqual(len(w), 2) + if self.module is py_warnings: + self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PY', 42) + self.assertEqual(len(w), 3) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.pyw', 42) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, r'C:\PATH\TO\PACKAGE\MODULE', 42) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, r'C:/path/to/package/module', 42) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module\__init__.py', 42) + + with self.module.catch_warnings(record=True) as w: + self.module.simplefilter('error') + self.module.filterwarnings('always', module=r'\z') + self.module.warn_explicit('msg', UserWarning, '', 42) + self.assertEqual(len(w), 1) + def test_module_globals(self): with self.module.catch_warnings(record=True) as w: self.module.simplefilter("always", UserWarning) From 92025ea2c8a10aa0e3dd5a2a1548f9bb17fe7dbc Mon Sep 17 00:00:00 2001 From: Ayappan Perumal Date: Fri, 17 Oct 2025 23:10:16 +0530 Subject: [PATCH 199/373] gh-140239: Check statx availability only on Linux (#140249) --- ...25-10-17-11-33-45.gh-issue-140239._k-GgW.rst | 1 + configure | 17 +++++++++++------ configure.ac | 7 ++++++- 3 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2025-10-17-11-33-45.gh-issue-140239._k-GgW.rst diff --git a/Misc/NEWS.d/next/Build/2025-10-17-11-33-45.gh-issue-140239._k-GgW.rst b/Misc/NEWS.d/next/Build/2025-10-17-11-33-45.gh-issue-140239._k-GgW.rst new file mode 100644 index 00000000000..f196ab0964d --- /dev/null +++ b/Misc/NEWS.d/next/Build/2025-10-17-11-33-45.gh-issue-140239._k-GgW.rst @@ -0,0 +1 @@ +Check ``statx`` availability only in Linux platforms diff --git a/configure b/configure index 267981250cd..3ea9c104431 100755 --- a/configure +++ b/configure @@ -20191,12 +20191,6 @@ if test "x$ac_cv_func_splice" = xyes then : printf "%s\n" "#define HAVE_SPLICE 1" >>confdefs.h -fi -ac_fn_c_check_func "$LINENO" "statx" "ac_cv_func_statx" -if test "x$ac_cv_func_statx" = xyes -then : - printf "%s\n" "#define HAVE_STATX 1" >>confdefs.h - fi ac_fn_c_check_func "$LINENO" "strftime" "ac_cv_func_strftime" if test "x$ac_cv_func_strftime" = xyes @@ -20398,6 +20392,17 @@ then : fi +# Check statx availability in Linux +if test "$MACHDEP" = "linux"; then + ac_fn_c_check_func "$LINENO" "statx" "ac_cv_func_statx" +if test "x$ac_cv_func_statx" = xyes +then : + printf "%s\n" "#define HAVE_STATX 1" >>confdefs.h + +fi + +fi + # Force lchmod off for Linux. Linux disallows changing the mode of symbolic # links. Some libc implementations have a stub lchmod implementation that always # returns an error. diff --git a/configure.ac b/configure.ac index 382591952ef..8c920da1997 100644 --- a/configure.ac +++ b/configure.ac @@ -5251,12 +5251,17 @@ AC_CHECK_FUNCS([ \ setitimer setlocale setpgid setpgrp setpriority setregid setresgid \ setresuid setreuid setsid setuid setvbuf shutdown sigaction sigaltstack \ sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ - sigwaitinfo snprintf splice statx strftime strlcpy strsignal symlinkat sync \ + sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ tmpnam tmpnam_r truncate ttyname_r umask uname unlinkat unlockpt utimensat utimes vfork \ wait wait3 wait4 waitid waitpid wcscoll wcsftime wcsxfrm wmemcmp writev \ ]) +# Check statx availability in Linux +if test "$MACHDEP" = "linux"; then + AC_CHECK_FUNCS([statx]) +fi + # Force lchmod off for Linux. Linux disallows changing the mode of symbolic # links. Some libc implementations have a stub lchmod implementation that always # returns an error. From f4e51f253ad6c27583438f6182c33cf368bfa45f Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Fri, 17 Oct 2025 22:57:51 +0100 Subject: [PATCH 200/373] GH-133789: Fix unpickling of pathlib objects pickled in Python 3.13 (#133831) In Python 3.13 (but not 3.12 or 3.14), pathlib classes are defined in `pathlib._local` rather than `pathlib`. In hindsight this was a mistake, but it was difficult to predict how the abstract/local split would pan out. In this patch we re-introduce `pathlib._local` as a stub module that re-exports the classes from `pathlib`. This allows path objects pickled in 3.13 to be unpicked in 3.14+ --- Lib/pathlib/_local.py | 12 ++++++++++++ Lib/test/test_pathlib/test_pathlib.py | 6 ++++++ .../2025-05-10-15-10-54.gh-issue-133789.I-ZlUX.rst | 1 + 3 files changed, 19 insertions(+) create mode 100644 Lib/pathlib/_local.py create mode 100644 Misc/NEWS.d/next/Library/2025-05-10-15-10-54.gh-issue-133789.I-ZlUX.rst diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py new file mode 100644 index 00000000000..58e137f2a92 --- /dev/null +++ b/Lib/pathlib/_local.py @@ -0,0 +1,12 @@ +""" +This module exists so that pathlib objects pickled under Python 3.13 can be +unpickled in 3.14+. +""" + +from pathlib import * + +__all__ = [ + "UnsupportedOperation", + "PurePath", "PurePosixPath", "PureWindowsPath", + "Path", "PosixPath", "WindowsPath", +] diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index a1105aae635..ef9ea0d11d0 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -293,6 +293,12 @@ def test_pickling_common(self): self.assertEqual(hash(pp), hash(p)) self.assertEqual(str(pp), str(p)) + def test_unpicking_3_13(self): + data = (b"\x80\x04\x95'\x00\x00\x00\x00\x00\x00\x00\x8c\x0e" + b"pathlib._local\x94\x8c\rPurePosixPath\x94\x93\x94)R\x94.") + p = pickle.loads(data) + self.assertIsInstance(p, pathlib.PurePosixPath) + def test_repr_common(self): for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): with self.subTest(pathstr=pathstr): diff --git a/Misc/NEWS.d/next/Library/2025-05-10-15-10-54.gh-issue-133789.I-ZlUX.rst b/Misc/NEWS.d/next/Library/2025-05-10-15-10-54.gh-issue-133789.I-ZlUX.rst new file mode 100644 index 00000000000..d2a4f7f42c3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-10-15-10-54.gh-issue-133789.I-ZlUX.rst @@ -0,0 +1 @@ +Fix unpickling of :mod:`pathlib` objects that were pickled in Python 3.13. From 1bfe86caaaaca3ec16351d69fa778f1212f1dd84 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sat, 18 Oct 2025 02:13:25 +0100 Subject: [PATCH 201/373] GH-139174: Prepare `pathlib.Path.info` for new methods (part 2) (#140155) Merge `_Info`, `_StatResultInfo` and `_DirEntryInfo` into a single `_Info` class. No other changes. This will allow us to use a cached `os.stat()` result from our upcoming `_Info.stat()` method even when we have a backing `os.DirEntry`. --- Lib/pathlib/__init__.py | 240 +++++++++++++++++----------------------- 1 file changed, 102 insertions(+), 138 deletions(-) diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py index 51359cec8b0..44f967eb12d 100644 --- a/Lib/pathlib/__init__.py +++ b/Lib/pathlib/__init__.py @@ -611,11 +611,20 @@ class PureWindowsPath(PurePath): __slots__ = () -class _Info: - __slots__ = ('_path',) +_STAT_RESULT_ERROR = [] # falsy sentinel indicating stat() failed. - def __init__(self, path): + +class _Info: + """Implementation of pathlib.types.PathInfo that provides status + information by querying a wrapped os.stat_result object. Don't try to + construct it yourself.""" + __slots__ = ('_path', '_entry', '_stat_result', '_lstat_result') + + def __init__(self, path, entry=None): self._path = path + self._entry = entry + self._stat_result = None + self._lstat_result = None def __repr__(self): path_type = "WindowsPath" if os.name == "nt" else "PosixPath" @@ -623,7 +632,94 @@ def __repr__(self): def _stat(self, *, follow_symlinks=True): """Return the status as an os.stat_result.""" - raise NotImplementedError + if self._entry: + return self._entry.stat(follow_symlinks=follow_symlinks) + if follow_symlinks: + if not self._stat_result: + try: + self._stat_result = os.stat(self._path) + except (OSError, ValueError): + self._stat_result = _STAT_RESULT_ERROR + raise + return self._stat_result + else: + if not self._lstat_result: + try: + self._lstat_result = os.lstat(self._path) + except (OSError, ValueError): + self._lstat_result = _STAT_RESULT_ERROR + raise + return self._lstat_result + + def exists(self, *, follow_symlinks=True): + """Whether this path exists.""" + if self._entry: + if not follow_symlinks: + return True + if follow_symlinks: + if self._stat_result is _STAT_RESULT_ERROR: + return False + else: + if self._lstat_result is _STAT_RESULT_ERROR: + return False + try: + self._stat(follow_symlinks=follow_symlinks) + except (OSError, ValueError): + return False + return True + + def is_dir(self, *, follow_symlinks=True): + """Whether this path is a directory.""" + if self._entry: + try: + return self._entry.is_dir(follow_symlinks=follow_symlinks) + except OSError: + return False + if follow_symlinks: + if self._stat_result is _STAT_RESULT_ERROR: + return False + else: + if self._lstat_result is _STAT_RESULT_ERROR: + return False + try: + st = self._stat(follow_symlinks=follow_symlinks) + except (OSError, ValueError): + return False + return S_ISDIR(st.st_mode) + + def is_file(self, *, follow_symlinks=True): + """Whether this path is a regular file.""" + if self._entry: + try: + return self._entry.is_file(follow_symlinks=follow_symlinks) + except OSError: + return False + if follow_symlinks: + if self._stat_result is _STAT_RESULT_ERROR: + return False + else: + if self._lstat_result is _STAT_RESULT_ERROR: + return False + try: + st = self._stat(follow_symlinks=follow_symlinks) + except (OSError, ValueError): + return False + return S_ISREG(st.st_mode) + + def is_symlink(self): + """Whether this path is a symbolic link.""" + if self._entry: + try: + return self._entry.is_symlink() + except OSError: + return False + if self._lstat_result is _STAT_RESULT_ERROR: + return False + try: + st = self._stat(follow_symlinks=False) + except (OSError, ValueError): + return False + return S_ISLNK(st.st_mode) def _posix_permissions(self, *, follow_symlinks=True): """Return the POSIX file permissions.""" @@ -661,138 +757,6 @@ def _xattrs(self, *, follow_symlinks=True): return [] -_STAT_RESULT_ERROR = [] # falsy sentinel indicating stat() failed. - - -class _StatResultInfo(_Info): - """Implementation of pathlib.types.PathInfo that provides status - information by querying a wrapped os.stat_result object. Don't try to - construct it yourself.""" - __slots__ = ('_stat_result', '_lstat_result') - - def __init__(self, path): - super().__init__(path) - self._stat_result = None - self._lstat_result = None - - def _stat(self, *, follow_symlinks=True): - """Return the status as an os.stat_result.""" - if follow_symlinks: - if not self._stat_result: - try: - self._stat_result = os.stat(self._path) - except (OSError, ValueError): - self._stat_result = _STAT_RESULT_ERROR - raise - return self._stat_result - else: - if not self._lstat_result: - try: - self._lstat_result = os.lstat(self._path) - except (OSError, ValueError): - self._lstat_result = _STAT_RESULT_ERROR - raise - return self._lstat_result - - def exists(self, *, follow_symlinks=True): - """Whether this path exists.""" - if follow_symlinks: - if self._stat_result is _STAT_RESULT_ERROR: - return False - else: - if self._lstat_result is _STAT_RESULT_ERROR: - return False - try: - self._stat(follow_symlinks=follow_symlinks) - except (OSError, ValueError): - return False - return True - - def is_dir(self, *, follow_symlinks=True): - """Whether this path is a directory.""" - if follow_symlinks: - if self._stat_result is _STAT_RESULT_ERROR: - return False - else: - if self._lstat_result is _STAT_RESULT_ERROR: - return False - try: - st = self._stat(follow_symlinks=follow_symlinks) - except (OSError, ValueError): - return False - return S_ISDIR(st.st_mode) - - def is_file(self, *, follow_symlinks=True): - """Whether this path is a regular file.""" - if follow_symlinks: - if self._stat_result is _STAT_RESULT_ERROR: - return False - else: - if self._lstat_result is _STAT_RESULT_ERROR: - return False - try: - st = self._stat(follow_symlinks=follow_symlinks) - except (OSError, ValueError): - return False - return S_ISREG(st.st_mode) - - def is_symlink(self): - """Whether this path is a symbolic link.""" - if self._lstat_result is _STAT_RESULT_ERROR: - return False - try: - st = self._stat(follow_symlinks=False) - except (OSError, ValueError): - return False - return S_ISLNK(st.st_mode) - - -class _DirEntryInfo(_Info): - """Implementation of pathlib.types.PathInfo that provides status - information by querying a wrapped os.DirEntry object. Don't try to - construct it yourself.""" - __slots__ = ('_entry',) - - def __init__(self, entry): - super().__init__(entry.path) - self._entry = entry - - def _stat(self, *, follow_symlinks=True): - """Return the status as an os.stat_result.""" - return self._entry.stat(follow_symlinks=follow_symlinks) - - def exists(self, *, follow_symlinks=True): - """Whether this path exists.""" - if not follow_symlinks: - return True - try: - self._stat(follow_symlinks=follow_symlinks) - except OSError: - return False - return True - - def is_dir(self, *, follow_symlinks=True): - """Whether this path is a directory.""" - try: - return self._entry.is_dir(follow_symlinks=follow_symlinks) - except OSError: - return False - - def is_file(self, *, follow_symlinks=True): - """Whether this path is a regular file.""" - try: - return self._entry.is_file(follow_symlinks=follow_symlinks) - except OSError: - return False - - def is_symlink(self): - """Whether this path is a symbolic link.""" - try: - return self._entry.is_symlink() - except OSError: - return False - - def _copy_info(info, target, follow_symlinks=True): """Copy metadata from the given PathInfo to the given local path.""" copy_times_ns = ( @@ -877,7 +841,7 @@ def info(self): try: return self._info except AttributeError: - self._info = _StatResultInfo(str(self)) + self._info = _Info(str(self)) return self._info def stat(self, *, follow_symlinks=True): @@ -1057,7 +1021,7 @@ def _filter_trailing_slash(self, paths): def _from_dir_entry(self, dir_entry, path_str): path = self.with_segments(path_str) path._str = path_str - path._info = _DirEntryInfo(dir_entry) + path._info = _Info(dir_entry.path, dir_entry) return path def iterdir(self): From f937468e7c88c768a28ff4e653da051ffa30d86c Mon Sep 17 00:00:00 2001 From: Shamil Date: Sat, 18 Oct 2025 12:27:58 +0300 Subject: [PATCH 202/373] gh-140272: Fix memory leak in _gdbm.gdbm.clear() (GH-140274) --- .../2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst | 1 + Modules/_gdbmmodule.c | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst new file mode 100644 index 00000000000..666a45055f5 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst @@ -0,0 +1 @@ +Fix memory leak in the :meth:`!clear` method of the :mod:`dbm.gnu` database. diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index 87b84976f49..a6e0662ae74 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -673,8 +673,10 @@ _gdbm_gdbm_clear_impl(gdbmobject *self, PyTypeObject *cls) } if (gdbm_delete(self->di_dbm, key) < 0) { PyErr_SetString(state->gdbm_error, "cannot delete item from database"); + free(key.dptr); return NULL; } + free(key.dptr); } Py_RETURN_NONE; } From 936de256a9542c58da72a8986658bb307b2f2175 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 18 Oct 2025 13:54:40 +0300 Subject: [PATCH 203/373] Move the NEWS entry for gh-140272 to the correct place (GH-140290) --- .../2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Misc/NEWS.d/next/{Core_and_Builtins => Library}/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst (100%) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst b/Misc/NEWS.d/next/Library/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst similarity index 100% rename from Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst rename to Misc/NEWS.d/next/Library/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst From 78e1d65a4d656e783bc28a02e113f59e4323e41a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 18 Oct 2025 13:55:26 +0300 Subject: [PATCH 204/373] gh-140241: Fix documentation for the registry parameter of warnings.warn_explicit() (GH-140242) Co-authored-by: Petr Viktorin --- Doc/library/warnings.rst | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst index f9c8c4fc3a8..03b7a8dc378 100644 --- a/Doc/library/warnings.rst +++ b/Doc/library/warnings.rst @@ -480,14 +480,21 @@ Available Functions .. function:: warn_explicit(message, category, filename, lineno, module=None, registry=None, module_globals=None, source=None) This is a low-level interface to the functionality of :func:`warn`, passing in - explicitly the message, category, filename and line number, and optionally the - module name and the registry (which should be the ``__warningregistry__`` - dictionary of the module). The module name defaults to the filename with - ``.py`` stripped; if no registry is passed, the warning is never suppressed. + explicitly the message, category, filename and line number, and optionally + other arguments. *message* must be a string and *category* a subclass of :exc:`Warning` or *message* may be a :exc:`Warning` instance, in which case *category* will be ignored. + *module*, if supplied, should be the module name. + If no module is passed, the filename with ``.py`` stripped is used. + + *registry*, if supplied, should be the ``__warningregistry__`` dictionary + of the module. + If no registry is passed, each warning is treated as the first occurrence, + that is, filter actions ``"default"``, ``"module"`` and ``"once"`` are + handled as ``"always"``. + *module_globals*, if supplied, should be the global namespace in use by the code for which the warning is issued. (This argument is used to support displaying source for modules found in zipfiles or other non-filesystem import From c8729c9909e150989050ce2a435d018955bea41d Mon Sep 17 00:00:00 2001 From: Shamil Date: Sat, 18 Oct 2025 14:01:53 +0300 Subject: [PATCH 205/373] gh-140257: fix data race on eval_breaker during finalization (#140265) --- .../2025-10-17-20-23-19.gh-issue-140257.8Txmem.rst | 2 ++ Python/pystate.c | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-20-23-19.gh-issue-140257.8Txmem.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-20-23-19.gh-issue-140257.8Txmem.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-20-23-19.gh-issue-140257.8Txmem.rst new file mode 100644 index 00000000000..50f7e0e48ae --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-20-23-19.gh-issue-140257.8Txmem.rst @@ -0,0 +1,2 @@ +Fix data race between interpreter_clear() and take_gil() on eval_breaker +during finalization with daemon threads. diff --git a/Python/pystate.c b/Python/pystate.c index dbed609f29a..c402d89a161 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -763,10 +763,11 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) Py_CLEAR(interp->audit_hooks); - // At this time, all the threads should be cleared so we don't need atomic - // operations for instrumentation_version or eval_breaker. + // gh-140257: Threads have already been cleared, but daemon threads may + // still access eval_breaker atomically via take_gil() right before they + // hang. Use an atomic store to prevent data races during finalization. interp->ceval.instrumentation_version = 0; - tstate->eval_breaker = 0; + _Py_atomic_store_uintptr(&tstate->eval_breaker, 0); for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) { interp->monitors.tools[i] = 0; From 58c44c2bf2e6d251548652a21d9ee27265ee6dea Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Sat, 18 Oct 2025 16:36:58 +0530 Subject: [PATCH 206/373] gh-140067: Fix memory leak in sub-interpreter creation (#140111) (#140261) Fix memory leak in sub-interpreter creation caused by overwriting of the previously used `_malloced` field. Now the pointer is stored in the first word of the memory block to avoid it being overwritten accidentally. Co-authored-by: Kumar Aditya --- Include/internal/pycore_interp_structs.h | 6 ------ Lib/test/test_threading.py | 1 + ...2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst | 1 + Python/pystate.c | 15 +++++++++------ 4 files changed, 11 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst diff --git a/Include/internal/pycore_interp_structs.h b/Include/internal/pycore_interp_structs.h index 2124e76514f..badc97808c6 100644 --- a/Include/internal/pycore_interp_structs.h +++ b/Include/internal/pycore_interp_structs.h @@ -769,12 +769,6 @@ struct _is { * and should be placed at the beginning. */ struct _ceval_state ceval; - /* This structure is carefully allocated so that it's correctly aligned - * to avoid undefined behaviors during LOAD and STORE. The '_malloced' - * field stores the allocated pointer address that will later be freed. - */ - void *_malloced; - PyInterpreterState *next; int64_t id; diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index d0f0e8ab2f7..efd69a1f4fe 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1776,6 +1776,7 @@ def task(): self.assertEqual(os.read(r_interp, 1), DONE) @cpython_only + @support.skip_if_sanitizer(thread=True, memory=True) def test_daemon_threads_fatal_error(self): import_module("_testcapi") subinterp_code = f"""if 1: diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst new file mode 100644 index 00000000000..3c5a828101d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst @@ -0,0 +1 @@ +Fix memory leak in sub-interpreter creation. diff --git a/Python/pystate.c b/Python/pystate.c index c402d89a161..af7828d6a03 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -457,16 +457,19 @@ _PyInterpreterState_Enable(_PyRuntimeState *runtime) static PyInterpreterState * alloc_interpreter(void) { + // Aligned allocation for PyInterpreterState. + // the first word of the memory block is used to store + // the original pointer to be used later to free the memory. size_t alignment = _Alignof(PyInterpreterState); - size_t allocsize = sizeof(PyInterpreterState) + alignment - 1; + size_t allocsize = sizeof(PyInterpreterState) + sizeof(void *) + alignment - 1; void *mem = PyMem_RawCalloc(1, allocsize); if (mem == NULL) { return NULL; } - PyInterpreterState *interp = _Py_ALIGN_UP(mem, alignment); - assert(_Py_IS_ALIGNED(interp, alignment)); - interp->_malloced = mem; - return interp; + void *ptr = _Py_ALIGN_UP((char *)mem + sizeof(void *), alignment); + ((void **)ptr)[-1] = mem; + assert(_Py_IS_ALIGNED(ptr, alignment)); + return ptr; } static void @@ -481,7 +484,7 @@ free_interpreter(PyInterpreterState *interp) interp->obmalloc = NULL; } assert(_Py_IS_ALIGNED(interp, _Alignof(PyInterpreterState))); - PyMem_RawFree(interp->_malloced); + PyMem_RawFree(((void **)interp)[-1]); } } From d86ad870cc112d4bd36165398368826746555730 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Sat, 18 Oct 2025 22:35:24 +0800 Subject: [PATCH 207/373] gh-140251: colorize import statement formatting in asyncio console (#140252) --- Lib/asyncio/__main__.py | 5 ++++- .../Library/2025-10-17-12-33-01.gh-issue-140251.esM-OX.rst | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-17-12-33-01.gh-issue-140251.esM-OX.rst diff --git a/Lib/asyncio/__main__.py b/Lib/asyncio/__main__.py index 10bfca3cf96..d078ebfa4ce 100644 --- a/Lib/asyncio/__main__.py +++ b/Lib/asyncio/__main__.py @@ -107,7 +107,10 @@ def run(self): if CAN_USE_PYREPL: theme = get_theme().syntax ps1 = f"{theme.prompt}{ps1}{theme.reset}" - console.write(f"{ps1}import asyncio\n") + import_line = f'{theme.keyword}import{theme.reset} asyncio' + else: + import_line = "import asyncio" + console.write(f"{ps1}{import_line}\n") if CAN_USE_PYREPL: from _pyrepl.simple_interact import ( diff --git a/Misc/NEWS.d/next/Library/2025-10-17-12-33-01.gh-issue-140251.esM-OX.rst b/Misc/NEWS.d/next/Library/2025-10-17-12-33-01.gh-issue-140251.esM-OX.rst new file mode 100644 index 00000000000..cb08e02429b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-17-12-33-01.gh-issue-140251.esM-OX.rst @@ -0,0 +1 @@ +Colorize the default import statement ``import asyncio`` in asyncio REPL. From 920de7ccdcfa7284b6d23a124771b17c66dd3e4f Mon Sep 17 00:00:00 2001 From: Parham MohammadAlizadeh Date: Sat, 18 Oct 2025 19:47:04 +0100 Subject: [PATCH 208/373] gh-128571: Document UTF-16/32 native byte order (#139974) Closes #128571 Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> --- Doc/library/codecs.rst | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst index 194b8054ca5..2a5994b11d8 100644 --- a/Doc/library/codecs.rst +++ b/Doc/library/codecs.rst @@ -989,17 +989,22 @@ defined in Unicode. A simple and straightforward way that can store each Unicode code point, is to store each code point as four consecutive bytes. There are two possibilities: store the bytes in big endian or in little endian order. These two encodings are called ``UTF-32-BE`` and ``UTF-32-LE`` respectively. Their -disadvantage is that if e.g. you use ``UTF-32-BE`` on a little endian machine you -will always have to swap bytes on encoding and decoding. ``UTF-32`` avoids this -problem: bytes will always be in natural endianness. When these bytes are read -by a CPU with a different endianness, then bytes have to be swapped though. To -be able to detect the endianness of a ``UTF-16`` or ``UTF-32`` byte sequence, -there's the so called BOM ("Byte Order Mark"). This is the Unicode character -``U+FEFF``. This character can be prepended to every ``UTF-16`` or ``UTF-32`` -byte sequence. The byte swapped version of this character (``0xFFFE``) is an -illegal character that may not appear in a Unicode text. So when the -first character in a ``UTF-16`` or ``UTF-32`` byte sequence -appears to be a ``U+FFFE`` the bytes have to be swapped on decoding. +disadvantage is that if, for example, you use ``UTF-32-BE`` on a little endian +machine you will always have to swap bytes on encoding and decoding. +Python's ``UTF-16`` and ``UTF-32`` codecs avoid this problem by using the +platform's native byte order when no BOM is present. +Python follows prevailing platform +practice, so native-endian data round-trips without redundant byte swapping, +even though the Unicode Standard defaults to big-endian when the byte order is +unspecified. When these bytes are read by a CPU with a different endianness, +the bytes have to be swapped. To be able to detect the endianness of a +``UTF-16`` or ``UTF-32`` byte sequence, a BOM ("Byte Order Mark") is used. +This is the Unicode character ``U+FEFF``. This character can be prepended to every +``UTF-16`` or ``UTF-32`` byte sequence. The byte swapped version of this character +(``0xFFFE``) is an illegal character that may not appear in a Unicode text. +When the first character of a ``UTF-16`` or ``UTF-32`` byte sequence is +``U+FFFE``, the bytes have to be swapped on decoding. + Unfortunately the character ``U+FEFF`` had a second purpose as a ``ZERO WIDTH NO-BREAK SPACE``: a character that has no width and doesn't allow a word to be split. It can e.g. be used to give hints to a ligature algorithm. From bedaea05987738c4c6b958d19cec9621bec09f07 Mon Sep 17 00:00:00 2001 From: Shamil Date: Sun, 19 Oct 2025 02:20:04 +0300 Subject: [PATCH 209/373] gh-139269: Fix unaligned memory access in JIT code patching functions (GH-139271) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use memcpy for patching values instead of direct assignment Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- ...-09-23-21-01-12.gh-issue-139269.1rIaxy.rst | 1 + Python/jit.c | 30 ++++++++++++------- 2 files changed, 20 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst new file mode 100644 index 00000000000..e36be529d2a --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst @@ -0,0 +1 @@ +Fix undefined behavior when using unaligned store in JIT's ``patch_*`` functions. diff --git a/Python/jit.c b/Python/jit.c index ebd0d90385e..c3f3d686013 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -157,12 +157,18 @@ set_bits(uint32_t *loc, uint8_t loc_start, uint64_t value, uint8_t value_start, uint8_t width) { assert(loc_start + width <= 32); + uint32_t temp_val; + // Use memcpy to safely read the value, avoiding potential alignment + // issues and strict aliasing violations. + memcpy(&temp_val, loc, sizeof(temp_val)); // Clear the bits we're about to patch: - *loc &= ~(((1ULL << width) - 1) << loc_start); - assert(get_bits(*loc, loc_start, width) == 0); + temp_val &= ~(((1ULL << width) - 1) << loc_start); + assert(get_bits(temp_val, loc_start, width) == 0); // Patch the bits: - *loc |= get_bits(value, value_start, width) << loc_start; - assert(get_bits(*loc, loc_start, width) == get_bits(value, value_start, width)); + temp_val |= get_bits(value, value_start, width) << loc_start; + assert(get_bits(temp_val, loc_start, width) == get_bits(value, value_start, width)); + // Safely write the modified value back to memory. + memcpy(loc, &temp_val, sizeof(temp_val)); } // See https://developer.arm.com/documentation/ddi0602/2023-09/Base-Instructions @@ -204,30 +210,29 @@ set_bits(uint32_t *loc, uint8_t loc_start, uint64_t value, uint8_t value_start, void patch_32(unsigned char *location, uint64_t value) { - uint32_t *loc32 = (uint32_t *)location; // Check that we're not out of range of 32 unsigned bits: assert(value < (1ULL << 32)); - *loc32 = (uint32_t)value; + uint32_t final_value = (uint32_t)value; + memcpy(location, &final_value, sizeof(final_value)); } // 32-bit relative address. void patch_32r(unsigned char *location, uint64_t value) { - uint32_t *loc32 = (uint32_t *)location; value -= (uintptr_t)location; // Check that we're not out of range of 32 signed bits: assert((int64_t)value >= -(1LL << 31)); assert((int64_t)value < (1LL << 31)); - *loc32 = (uint32_t)value; + uint32_t final_value = (uint32_t)value; + memcpy(location, &final_value, sizeof(final_value)); } // 64-bit absolute address. void patch_64(unsigned char *location, uint64_t value) { - uint64_t *loc64 = (uint64_t *)location; - *loc64 = value; + memcpy(location, &value, sizeof(value)); } // 12-bit low part of an absolute address. Pairs nicely with patch_aarch64_21r @@ -410,7 +415,10 @@ patch_x86_64_32rx(unsigned char *location, uint64_t value) { uint8_t *loc8 = (uint8_t *)location; // Try to relax the GOT load into an immediate value: - uint64_t relaxed = *(uint64_t *)(value + 4) - 4; + uint64_t relaxed; + memcpy(&relaxed, (void *)(value + 4), sizeof(relaxed)); + relaxed -= 4; + if ((int64_t)relaxed - (int64_t)location >= -(1LL << 31) && (int64_t)relaxed - (int64_t)location + 1 < (1LL << 31)) { From 115a04b80259ac3116571eb601c5efc51004e90c Mon Sep 17 00:00:00 2001 From: Marat Khagazheev Date: Sun, 19 Oct 2025 06:45:52 +0300 Subject: [PATCH 210/373] gh-138997: Remove false justify `fill` option from Tkinter docs (#139023) Co-authored-by: marat --- Doc/library/tkinter.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst index be26af02bce..22e08c45d01 100644 --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -839,8 +839,7 @@ geometry For example: ``fred["geometry"] = "200x100"``. justify - Legal values are the strings: ``"left"``, ``"center"``, ``"right"``, and - ``"fill"``. + Legal values are the strings: ``"left"``, ``"center"``, and ``"right"``. region This is a string with four space-delimited elements, each of which is a legal From 790cdae5a0295586a55f00a57ed24a86b83928dc Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Sun, 19 Oct 2025 05:16:13 +0100 Subject: [PATCH 211/373] `Lib/idlelib/idle_test/__init__.py`: remove commented out duplicate code (#140259) --- Lib/idlelib/idle_test/__init__.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Lib/idlelib/idle_test/__init__.py b/Lib/idlelib/idle_test/__init__.py index 79b5d102dd7..8eed2699c41 100644 --- a/Lib/idlelib/idle_test/__init__.py +++ b/Lib/idlelib/idle_test/__init__.py @@ -20,8 +20,4 @@ def load_tests(loader, standard_tests, pattern): pattern='test_*.py', # Insert here. top_level_dir=top_dir) standard_tests.addTests(module_tests) -## module_tests = loader.discover(start_dir=this_dir, -## pattern='test_*.py', # Insert here. -## top_level_dir=top_dir) -## standard_tests.addTests(module_tests) return standard_tests From bad8d6de37c67b6f8ead5682cadb43d5e3f75f5a Mon Sep 17 00:00:00 2001 From: stratakis Date: Sun, 19 Oct 2025 16:23:49 +0200 Subject: [PATCH 212/373] gh-101525: [BOLT] Add GCC's LTO-generated function clones, with computed gotos, to BOLT skip list (gh-139840) --- configure | 2 +- configure.ac | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 3ea9c104431..95e231f406a 100755 --- a/configure +++ b/configure @@ -9396,7 +9396,7 @@ fi printf %s "checking BOLT_COMMON_FLAGS... " >&6; } if test -z "${BOLT_COMMON_FLAGS}" then - BOLT_COMMON_FLAGS=" -update-debug-sections -skip-funcs=_PyEval_EvalFrameDefault,sre_ucs1_match/1,sre_ucs2_match/1,sre_ucs4_match/1 " + BOLT_COMMON_FLAGS=" -update-debug-sections -skip-funcs=_PyEval_EvalFrameDefault,sre_ucs1_match/1,sre_ucs2_match/1,sre_ucs4_match/1,sre_ucs1_match.lto_priv.0/1,sre_ucs2_match.lto_priv.0/1,sre_ucs4_match.lto_priv.0/1 " fi diff --git a/configure.ac b/configure.ac index 8c920da1997..65c37579633 100644 --- a/configure.ac +++ b/configure.ac @@ -2162,7 +2162,8 @@ then dnl At least LLVM 19.x doesn't support computed gotos in PIC compiled code. dnl Exclude functions containing computed gotos. dnl TODO this may be fixed in LLVM 20.x via https://github.com/llvm/llvm-project/pull/120267. - [-skip-funcs=_PyEval_EvalFrameDefault,sre_ucs1_match/1,sre_ucs2_match/1,sre_ucs4_match/1] + dnl GCC's LTO creates .lto_priv.0 clones of these functions. + [-skip-funcs=_PyEval_EvalFrameDefault,sre_ucs1_match/1,sre_ucs2_match/1,sre_ucs4_match/1,sre_ucs1_match.lto_priv.0/1,sre_ucs2_match.lto_priv.0/1,sre_ucs4_match.lto_priv.0/1] ")] ) fi From f9323213c98c9f1f7f3bf5af883b73047432fe50 Mon Sep 17 00:00:00 2001 From: Shamil Date: Sun, 19 Oct 2025 22:24:28 +0300 Subject: [PATCH 213/373] gh-140306: Fix memory leaks in cross-interpreter data handling (GH-140307) --- ...2025-10-18-21-29-45.gh-issue-140306.xS5CcS.rst | 2 ++ Modules/_interpchannelsmodule.c | 2 +- Modules/_interpqueuesmodule.c | 2 +- Python/crossinterp.c | 15 ++++++++++++--- 4 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-18-21-29-45.gh-issue-140306.xS5CcS.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-18-21-29-45.gh-issue-140306.xS5CcS.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-18-21-29-45.gh-issue-140306.xS5CcS.rst new file mode 100644 index 00000000000..2178c496063 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-18-21-29-45.gh-issue-140306.xS5CcS.rst @@ -0,0 +1,2 @@ +Fix memory leaks in cross-interpreter channel operations and shared +namespace handling. diff --git a/Modules/_interpchannelsmodule.c b/Modules/_interpchannelsmodule.c index 274bfacfed8..ef9cf01ecbe 100644 --- a/Modules/_interpchannelsmodule.c +++ b/Modules/_interpchannelsmodule.c @@ -580,7 +580,7 @@ _channelitem_clear_data(_channelitem *item, int removed) { if (item->data != NULL) { // It was allocated in channel_send(). - (void)_release_xid_data(item->data, XID_IGNORE_EXC & XID_FREE); + (void)_release_xid_data(item->data, XID_IGNORE_EXC | XID_FREE); item->data = NULL; } diff --git a/Modules/_interpqueuesmodule.c b/Modules/_interpqueuesmodule.c index 1db08628d29..417c5fbcee2 100644 --- a/Modules/_interpqueuesmodule.c +++ b/Modules/_interpqueuesmodule.c @@ -436,7 +436,7 @@ _queueitem_clear_data(_queueitem *item) return; } // It was allocated in queue_put(). - (void)_release_xid_data(item->data, XID_IGNORE_EXC & XID_FREE); + (void)_release_xid_data(item->data, XID_IGNORE_EXC | XID_FREE); item->data = NULL; } diff --git a/Python/crossinterp.c b/Python/crossinterp.c index 16a23f0351c..542253c14de 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -1153,8 +1153,8 @@ _release_xid_data(_PyXIData_t *xidata, int rawfree) { PyObject *exc = PyErr_GetRaisedException(); int res = rawfree - ? _PyXIData_Release(xidata) - : _PyXIData_ReleaseAndRawFree(xidata); + ? _PyXIData_ReleaseAndRawFree(xidata) + : _PyXIData_Release(xidata); if (res < 0) { /* The owning interpreter is already destroyed. */ _PyXIData_Clear(NULL, xidata); @@ -1805,6 +1805,15 @@ _PyXI_InitFailureUTF8(_PyXI_failure *failure, int _PyXI_InitFailure(_PyXI_failure *failure, _PyXI_errcode code, PyObject *obj) { + *failure = (_PyXI_failure){ + .code = code, + .msg = NULL, + .msg_owned = 0, + }; + if (obj == NULL) { + return 0; + } + PyObject *msgobj = PyObject_Str(obj); if (msgobj == NULL) { return -1; @@ -1813,7 +1822,7 @@ _PyXI_InitFailure(_PyXI_failure *failure, _PyXI_errcode code, PyObject *obj) // That happens automatically in _capture_current_exception(). const char *msg = _copy_string_obj_raw(msgobj, NULL); Py_DECREF(msgobj); - if (PyErr_Occurred()) { + if (msg == NULL) { return -1; } *failure = (_PyXI_failure){ From ed672f7a8a3c843d8e6e6b673d5a7c1f752f208c Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 19 Oct 2025 16:16:35 -0400 Subject: [PATCH 214/373] docs: be clearer that glob results are unordered (#140184) * docs: be clearer that glob results are unordered * trim down the opening paragraph --- Doc/library/glob.rst | 20 ++++++++++++-------- Lib/glob.py | 6 ++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Doc/library/glob.rst b/Doc/library/glob.rst index 59ad1b07f27..52c44928153 100644 --- a/Doc/library/glob.rst +++ b/Doc/library/glob.rst @@ -18,23 +18,27 @@ single: - (minus); in glob-style wildcards single: . (dot); in glob-style wildcards -The :mod:`glob` module finds all the pathnames matching a specified pattern -according to the rules used by the Unix shell, although results are returned in -arbitrary order. No tilde expansion is done, but ``*``, ``?``, and character +The :mod:`!glob` module finds pathnames +using pattern matching rules similar to the Unix shell. +No tilde expansion is done, but ``*``, ``?``, and character ranges expressed with ``[]`` will be correctly matched. This is done by using the :func:`os.scandir` and :func:`fnmatch.fnmatch` functions in concert, and not by actually invoking a subshell. -Note that files beginning with a dot (``.``) can only be matched by +.. note:: + The pathnames are returned in no particular order. If you need a specific + order, sort the results. + +Files beginning with a dot (``.``) can only be matched by patterns that also start with a dot, unlike :func:`fnmatch.fnmatch` or :func:`pathlib.Path.glob`. -(For tilde and shell variable expansion, use :func:`os.path.expanduser` and -:func:`os.path.expandvars`.) +For tilde and shell variable expansion, use :func:`os.path.expanduser` and +:func:`os.path.expandvars`. For a literal match, wrap the meta-characters in brackets. For example, ``'[?]'`` matches the character ``'?'``. -The :mod:`glob` module defines the following functions: +The :mod:`!glob` module defines the following functions: .. function:: glob(pathname, *, root_dir=None, dir_fd=None, recursive=False, \ @@ -51,7 +55,7 @@ The :mod:`glob` module defines the following functions: If *root_dir* is not ``None``, it should be a :term:`path-like object` specifying the root directory for searching. It has the same effect on - :func:`glob` as changing the current directory before calling it. If + :func:`!glob` as changing the current directory before calling it. If *pathname* is relative, the result will contain paths relative to *root_dir*. diff --git a/Lib/glob.py b/Lib/glob.py index 5d42077003a..c2f8ce279ab 100644 --- a/Lib/glob.py +++ b/Lib/glob.py @@ -22,6 +22,9 @@ def glob(pathname, *, root_dir=None, dir_fd=None, recursive=False, dot are special cases that are not matched by '*' and '?' patterns by default. + The order of the returned list is undefined. Sort it if you need a + particular order. + If `include_hidden` is true, the patterns '*', '?', '**' will match hidden directories. @@ -40,6 +43,9 @@ def iglob(pathname, *, root_dir=None, dir_fd=None, recursive=False, dot are special cases that are not matched by '*' and '?' patterns. + The order of the returned paths is undefined. Sort them if you need a + particular order. + If recursive is true, the pattern '**' will match any files and zero or more directories and subdirectories. """ From 091e8513bb58747ade56a03fccb98e220c7722cc Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Mon, 20 Oct 2025 05:22:39 +0100 Subject: [PATCH 215/373] `zlib.rst`: Link to constants and deduplicate text (GH-140115) * Link to compression setting constants from compression functions * De-duplicate descriptions of the constants Co-authored-by: Victor Stinner --- Doc/library/zlib.rst | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/Doc/library/zlib.rst b/Doc/library/zlib.rst index e37be69d6a6..b961f7113d3 100644 --- a/Doc/library/zlib.rst +++ b/Doc/library/zlib.rst @@ -56,21 +56,20 @@ The available exception and functions in this module are: .. versionadded:: 3.15 -.. function:: compress(data, /, level=-1, wbits=MAX_WBITS) +.. function:: compress(data, /, level=Z_DEFAULT_COMPRESSION, wbits=MAX_WBITS) Compresses the bytes in *data*, returning a bytes object containing compressed data. *level* is an integer from ``0`` to ``9`` or ``-1`` controlling the level of compression; - ``1`` (Z_BEST_SPEED) is fastest and produces the least compression, ``9`` (Z_BEST_COMPRESSION) - is slowest and produces the most. ``0`` (Z_NO_COMPRESSION) is no compression. - The default value is ``-1`` (Z_DEFAULT_COMPRESSION). Z_DEFAULT_COMPRESSION represents a default - compromise between speed and compression (currently equivalent to level 6). + See :const:`Z_BEST_SPEED` (``1``), :const:`Z_BEST_COMPRESSION` (``9``), + :const:`Z_NO_COMPRESSION` (``0``), and the default, + :const:`Z_DEFAULT_COMPRESSION` (``-1``) for more information about these values. .. _compress-wbits: The *wbits* argument controls the size of the history buffer (or the "window size") used when compressing data, and whether a header and trailer is included in the output. It can take several ranges of values, - defaulting to ``15`` (MAX_WBITS): + defaulting to ``15`` (:const:`MAX_WBITS`): * +9 to +15: The base-two logarithm of the window size, which therefore ranges between 512 and 32768. Larger values produce @@ -94,17 +93,15 @@ The available exception and functions in this module are: The *wbits* parameter is now available to set window bits and compression type. -.. function:: compressobj(level=-1, method=DEFLATED, wbits=MAX_WBITS, memLevel=DEF_MEM_LEVEL, strategy=Z_DEFAULT_STRATEGY[, zdict]) +.. function:: compressobj(level=Z_DEFAULT_COMPRESSION, method=DEFLATED, wbits=MAX_WBITS, memLevel=DEF_MEM_LEVEL, strategy=Z_DEFAULT_STRATEGY[, zdict]) Returns a compression object, to be used for compressing data streams that won't fit into memory at once. *level* is the compression level -- an integer from ``0`` to ``9`` or ``-1``. - A value of ``1`` (Z_BEST_SPEED) is fastest and produces the least compression, - while a value of ``9`` (Z_BEST_COMPRESSION) is slowest and produces the most. - ``0`` (Z_NO_COMPRESSION) is no compression. The default value is ``-1`` (Z_DEFAULT_COMPRESSION). - Z_DEFAULT_COMPRESSION represents a default compromise between speed and compression - (currently equivalent to level 6). + See :const:`Z_BEST_SPEED` (``1``), :const:`Z_BEST_COMPRESSION` (``9``), + :const:`Z_NO_COMPRESSION` (``0``), and the default, + :const:`Z_DEFAULT_COMPRESSION` (``-1``) for more information about these values. *method* is the compression algorithm. Currently, the only supported value is :const:`DEFLATED`. @@ -119,7 +116,7 @@ The available exception and functions in this module are: *strategy* is used to tune the compression algorithm. Possible values are :const:`Z_DEFAULT_STRATEGY`, :const:`Z_FILTERED`, :const:`Z_HUFFMAN_ONLY`, - :const:`Z_RLE` (zlib 1.2.0.1) and :const:`Z_FIXED` (zlib 1.2.2.2). + :const:`Z_RLE` and :const:`Z_FIXED`. *zdict* is a predefined compression dictionary. This is a sequence of bytes (such as a :class:`bytes` object) containing subsequences that are expected @@ -247,7 +244,7 @@ Compression objects support the following methods: All pending input is processed, and a bytes object containing the remaining compressed output is returned. *mode* can be selected from the constants :const:`Z_NO_FLUSH`, :const:`Z_PARTIAL_FLUSH`, :const:`Z_SYNC_FLUSH`, - :const:`Z_FULL_FLUSH`, :const:`Z_BLOCK` (zlib 1.2.3.4), or :const:`Z_FINISH`, + :const:`Z_FULL_FLUSH`, :const:`Z_BLOCK`, or :const:`Z_FINISH`, defaulting to :const:`Z_FINISH`. Except :const:`Z_FINISH`, all constants allow compressing further bytestrings of data, while :const:`Z_FINISH` finishes the compressed stream and prevents compressing any more data. After calling :meth:`flush` @@ -365,24 +362,25 @@ behavior: .. data:: Z_NO_COMPRESSION - Compression level ``0``. + Compression level ``0``; no compression. .. versionadded:: 3.6 .. data:: Z_BEST_SPEED - Compression level ``1``. + Compression level ``1``; fastest and produces the least compression. .. data:: Z_BEST_COMPRESSION - Compression level ``9``. + Compression level ``9``; slowest and produces the most compression. .. data:: Z_DEFAULT_COMPRESSION - Default compression level (``-1``). + Default compression level (``-1``); a compromise between speed and + compression. Currently equivalent to compression level ``6``. .. data:: Z_DEFAULT_STRATEGY From 9c18f75e7c7fd65d7c923a1693121334a5e3f93b Mon Sep 17 00:00:00 2001 From: Weilin Du <108666168+LamentXU123@users.noreply.github.com> Date: Mon, 20 Oct 2025 15:15:40 +0800 Subject: [PATCH 216/373] gh-101100: Fix sphinx warnings in `library/resource.rst` (#140023) --- Doc/library/resource.rst | 74 ++++++++++++++++++++-------------------- Doc/library/signal.rst | 6 ++++ Doc/tools/.nitignore | 1 - 3 files changed, 43 insertions(+), 38 deletions(-) diff --git a/Doc/library/resource.rst b/Doc/library/resource.rst index 9f6c3a88f0a..c58dc4243ec 100644 --- a/Doc/library/resource.rst +++ b/Doc/library/resource.rst @@ -141,7 +141,7 @@ platform. .. data:: RLIMIT_CPU The maximum amount of processor time (in seconds) that a process can use. If - this limit is exceeded, a :const:`SIGXCPU` signal is sent to the process. (See + this limit is exceeded, a :const:`~signal.SIGXCPU` signal is sent to the process. (See the :mod:`signal` module documentation for information about how to catch this signal and do something useful, e.g. flush open files to disk.) @@ -363,47 +363,47 @@ These functions are used to retrieve resource usage information: For backward compatibility, the return value is also accessible as a tuple of 16 elements. - The fields :attr:`ru_utime` and :attr:`ru_stime` of the return value are + The fields :attr:`!ru_utime` and :attr:`!ru_stime` of the return value are floating-point values representing the amount of time spent executing in user mode and the amount of time spent executing in system mode, respectively. The remaining values are integers. Consult the :manpage:`getrusage(2)` man page for detailed information about these values. A brief summary is presented here: - +--------+---------------------+---------------------------------------+ - | Index | Field | Resource | - +========+=====================+=======================================+ - | ``0`` | :attr:`ru_utime` | time in user mode (float seconds) | - +--------+---------------------+---------------------------------------+ - | ``1`` | :attr:`ru_stime` | time in system mode (float seconds) | - +--------+---------------------+---------------------------------------+ - | ``2`` | :attr:`ru_maxrss` | maximum resident set size | - +--------+---------------------+---------------------------------------+ - | ``3`` | :attr:`ru_ixrss` | shared memory size | - +--------+---------------------+---------------------------------------+ - | ``4`` | :attr:`ru_idrss` | unshared memory size | - +--------+---------------------+---------------------------------------+ - | ``5`` | :attr:`ru_isrss` | unshared stack size | - +--------+---------------------+---------------------------------------+ - | ``6`` | :attr:`ru_minflt` | page faults not requiring I/O | - +--------+---------------------+---------------------------------------+ - | ``7`` | :attr:`ru_majflt` | page faults requiring I/O | - +--------+---------------------+---------------------------------------+ - | ``8`` | :attr:`ru_nswap` | number of swap outs | - +--------+---------------------+---------------------------------------+ - | ``9`` | :attr:`ru_inblock` | block input operations | - +--------+---------------------+---------------------------------------+ - | ``10`` | :attr:`ru_oublock` | block output operations | - +--------+---------------------+---------------------------------------+ - | ``11`` | :attr:`ru_msgsnd` | messages sent | - +--------+---------------------+---------------------------------------+ - | ``12`` | :attr:`ru_msgrcv` | messages received | - +--------+---------------------+---------------------------------------+ - | ``13`` | :attr:`ru_nsignals` | signals received | - +--------+---------------------+---------------------------------------+ - | ``14`` | :attr:`ru_nvcsw` | voluntary context switches | - +--------+---------------------+---------------------------------------+ - | ``15`` | :attr:`ru_nivcsw` | involuntary context switches | - +--------+---------------------+---------------------------------------+ + +--------+----------------------+---------------------------------------+ + | Index | Field | Resource | + +========+======================+=======================================+ + | ``0`` | :attr:`!ru_utime` | time in user mode (float seconds) | + +--------+----------------------+---------------------------------------+ + | ``1`` | :attr:`!ru_stime` | time in system mode (float seconds) | + +--------+----------------------+---------------------------------------+ + | ``2`` | :attr:`!ru_maxrss` | maximum resident set size | + +--------+----------------------+---------------------------------------+ + | ``3`` | :attr:`!ru_ixrss` | shared memory size | + +--------+----------------------+---------------------------------------+ + | ``4`` | :attr:`!ru_idrss` | unshared memory size | + +--------+----------------------+---------------------------------------+ + | ``5`` | :attr:`!ru_isrss` | unshared stack size | + +--------+----------------------+---------------------------------------+ + | ``6`` | :attr:`!ru_minflt` | page faults not requiring I/O | + +--------+----------------------+---------------------------------------+ + | ``7`` | :attr:`!ru_majflt` | page faults requiring I/O | + +--------+----------------------+---------------------------------------+ + | ``8`` | :attr:`!ru_nswap` | number of swap outs | + +--------+----------------------+---------------------------------------+ + | ``9`` | :attr:`!ru_inblock` | block input operations | + +--------+----------------------+---------------------------------------+ + | ``10`` | :attr:`!ru_oublock` | block output operations | + +--------+----------------------+---------------------------------------+ + | ``11`` | :attr:`!ru_msgsnd` | messages sent | + +--------+----------------------+---------------------------------------+ + | ``12`` | :attr:`!ru_msgrcv` | messages received | + +--------+----------------------+---------------------------------------+ + | ``13`` | :attr:`!ru_nsignals` | signals received | + +--------+----------------------+---------------------------------------+ + | ``14`` | :attr:`!ru_nvcsw` | voluntary context switches | + +--------+----------------------+---------------------------------------+ + | ``15`` | :attr:`!ru_nivcsw` | involuntary context switches | + +--------+----------------------+---------------------------------------+ This function will raise a :exc:`ValueError` if an invalid *who* parameter is specified. It may also raise :exc:`error` exception in unusual circumstances. diff --git a/Doc/library/signal.rst b/Doc/library/signal.rst index bbc4b91b921..cdefcd29ef7 100644 --- a/Doc/library/signal.rst +++ b/Doc/library/signal.rst @@ -265,6 +265,12 @@ The variables defined in the :mod:`signal` module are: .. availability:: Unix. +.. data:: SIGXCPU + + CPU time limit exceeded. + + .. availability:: Unix. + .. data:: SIG* All the signal numbers are defined symbolically. For example, the hangup signal diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 29fd3cfda45..6fee1c192c3 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -27,7 +27,6 @@ Doc/library/os.rst Doc/library/pickletools.rst Doc/library/profile.rst Doc/library/pyexpat.rst -Doc/library/resource.rst Doc/library/select.rst Doc/library/socket.rst Doc/library/ssl.rst From 3c2781dc5f2f400ab4cae79d63051365d15b22e4 Mon Sep 17 00:00:00 2001 From: Peter Holloway Date: Mon, 20 Oct 2025 09:53:57 +0100 Subject: [PATCH 217/373] Fix typo in PyIter_Send docs (#140336) --- Doc/c-api/iter.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/iter.rst b/Doc/c-api/iter.rst index bf9df62c6f1..6cfd24c5ae6 100644 --- a/Doc/c-api/iter.rst +++ b/Doc/c-api/iter.rst @@ -54,6 +54,6 @@ There are two functions specifically for working with iterators. - ``PYGEN_RETURN`` if iterator returns. Return value is returned via *presult*. - ``PYGEN_NEXT`` if iterator yields. Yielded value is returned via *presult*. - - ``PYGEN_ERROR`` if iterator has raised and exception. *presult* is set to ``NULL``. + - ``PYGEN_ERROR`` if iterator has raised an exception. *presult* is set to ``NULL``. .. versionadded:: 3.10 From 7babe31443d40f42a0b562a1712bc378aade5f5c Mon Sep 17 00:00:00 2001 From: Brandon Hubacher Date: Mon, 20 Oct 2025 03:55:01 -0500 Subject: [PATCH 218/373] Remove typo in ``functools.lru_cache`` docs (#140278) --- Doc/library/functools.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index f8ffb3f41d1..37d9f87e779 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -190,7 +190,7 @@ The :mod:`functools` module defines the following functions: Note, type specificity applies only to the function's immediate arguments rather than their contents. The scalar arguments, ``Decimal(42)`` and - ``Fraction(42)`` are be treated as distinct calls with distinct results. + ``Fraction(42)`` are treated as distinct calls with distinct results. In contrast, the tuple arguments ``('answer', Decimal(42))`` and ``('answer', Fraction(42))`` are treated as equivalent. From baf45159e53313000f188da5394cf1af12615f7f Mon Sep 17 00:00:00 2001 From: mdehoon Date: Mon, 20 Oct 2025 17:55:57 +0900 Subject: [PATCH 219/373] fix typo in comments PyOS_CallInputHook -> PyOS_InputHook (#140237) --- Modules/clinic/posixmodule.c.h | 6 +++--- Modules/posixmodule.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 7bd3d0ebfaa..d880fc52bb3 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -12781,7 +12781,7 @@ PyDoc_STRVAR(os__inputhook__doc__, "_inputhook($module, /)\n" "--\n" "\n" -"Calls PyOS_CallInputHook droppong the GIL first"); +"Calls PyOS_InputHook dropping the GIL first"); #define OS__INPUTHOOK_METHODDEF \ {"_inputhook", (PyCFunction)os__inputhook, METH_NOARGS, os__inputhook__doc__}, @@ -12799,7 +12799,7 @@ PyDoc_STRVAR(os__is_inputhook_installed__doc__, "_is_inputhook_installed($module, /)\n" "--\n" "\n" -"Checks if PyOS_CallInputHook is set"); +"Checks if PyOS_InputHook is set"); #define OS__IS_INPUTHOOK_INSTALLED_METHODDEF \ {"_is_inputhook_installed", (PyCFunction)os__is_inputhook_installed, METH_NOARGS, os__is_inputhook_installed__doc__}, @@ -13610,4 +13610,4 @@ exit: #ifndef OS__EMSCRIPTEN_LOG_METHODDEF #define OS__EMSCRIPTEN_LOG_METHODDEF #endif /* !defined(OS__EMSCRIPTEN_LOG_METHODDEF) */ -/*[clinic end generated code: output=44f7a1a16dad2e08 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=82f60940338c70e4 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 1338be22e18..e9a3b6d29b8 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -17308,12 +17308,12 @@ os__supports_virtual_terminal_impl(PyObject *module) /*[clinic input] os._inputhook -Calls PyOS_CallInputHook droppong the GIL first +Calls PyOS_InputHook dropping the GIL first [clinic start generated code]*/ static PyObject * os__inputhook_impl(PyObject *module) -/*[clinic end generated code: output=525aca4ef3c6149f input=fc531701930d064f]*/ +/*[clinic end generated code: output=525aca4ef3c6149f input=b5018fa1ec3aa440]*/ { int result = 0; if (PyOS_InputHook) { @@ -17327,12 +17327,12 @@ os__inputhook_impl(PyObject *module) /*[clinic input] os._is_inputhook_installed -Checks if PyOS_CallInputHook is set +Checks if PyOS_InputHook is set [clinic start generated code]*/ static PyObject * os__is_inputhook_installed_impl(PyObject *module) -/*[clinic end generated code: output=3b3eab4f672c689a input=ff177c9938dd76d8]*/ +/*[clinic end generated code: output=3b3eab4f672c689a input=757820f79f48820c]*/ { return PyBool_FromLong(PyOS_InputHook != NULL); } From a615fb49c948902a982c3256899507abcc9f9bc8 Mon Sep 17 00:00:00 2001 From: Shamil Date: Mon, 20 Oct 2025 12:29:23 +0300 Subject: [PATCH 220/373] gh-140301: Fix memory leak in subinterpreter `PyConfig` cleanup (#140303) Co-authored-by: Kumar Aditya --- .../2025-10-18-18-08-36.gh-issue-140301.m-2HxC.rst | 1 + Python/pystate.c | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-18-18-08-36.gh-issue-140301.m-2HxC.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-18-18-08-36.gh-issue-140301.m-2HxC.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-18-18-08-36.gh-issue-140301.m-2HxC.rst new file mode 100644 index 00000000000..8b1c81c04ec --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-18-18-08-36.gh-issue-140301.m-2HxC.rst @@ -0,0 +1 @@ +Fix memory leak of ``PyConfig`` in subinterpreters. diff --git a/Python/pystate.c b/Python/pystate.c index af7828d6a03..955457cdcf5 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -963,6 +963,8 @@ PyInterpreterState_Delete(PyInterpreterState *interp) _PyObject_FiniState(interp); + PyConfig_Clear(&interp->config); + free_interpreter(interp); } From 5d0abb69715b9638f26c896444643055ec2ef038 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Mon, 20 Oct 2025 13:45:07 +0300 Subject: [PATCH 221/373] gh-139951: Test on GC collection disabled if threshold is zero (GH-140304) --- Lib/test/test_gc.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index 43289090534..70d9d009fa8 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -1559,6 +1559,20 @@ def test_indirect_calls_with_gc_disabled(self): finally: gc.enable() + # Ensure that setting *threshold0* to zero disables collection. + @gc_threshold(0) + def test_threshold_zero(self): + junk = [] + i = 0 + detector = GC_Detector() + while not detector.gc_happened: + i += 1 + if i > 50000: + break + junk.append([]) # this may eventually trigger gc (if it is enabled) + + self.assertEqual(i, 50001) + class PythonFinalizationTests(unittest.TestCase): def test_ast_fini(self): From faa169afa0dad9586b294ea9ab6e5e17c5712861 Mon Sep 17 00:00:00 2001 From: Furkan Onder Date: Mon, 20 Oct 2025 21:15:30 +0900 Subject: [PATCH 222/373] gh-66646: Explain __base__ attribute in the docs (GH-102554) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Éric --- Doc/reference/datamodel.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 29f82fc12da..28ef5825159 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1185,6 +1185,7 @@ Special attributes single: __module__ (class attribute) single: __dict__ (class attribute) single: __bases__ (class attribute) + single: __base__ (class attribute) single: __doc__ (class attribute) single: __annotations__ (class attribute) single: __annotate__ (class attribute) @@ -1219,6 +1220,13 @@ Special attributes In most cases, for a class defined as ``class X(A, B, C)``, ``X.__bases__`` will be exactly equal to ``(A, B, C)``. + * - .. attribute:: type.__base__ + - .. impl-detail:: + + The single base class in the inheritance chain that is responsible + for the memory layout of instances. This attribute corresponds to + :c:member:`~PyTypeObject.tp_base` at the C level. + * - .. attribute:: type.__doc__ - The class's documentation string, or ``None`` if undefined. Not inherited by subclasses. From 99c3c63d2b22359374ecb9645b027b8cd2082249 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 20 Oct 2025 15:20:44 +0300 Subject: [PATCH 223/373] gh-76007: Deprecate `__version__` attribute in `imaplib` (#140299) Co-authored-by: Victor Stinner --- Doc/deprecations/pending-removal-in-3.20.rst | 1 + Doc/whatsnew/3.15.rst | 1 + Lib/imaplib.py | 12 +++++++++--- Lib/test/test_imaplib.py | 10 ++++++++++ .../2025-10-18-14-30-21.gh-issue-76007.peEgcr.rst | 1 + 5 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-18-14-30-21.gh-issue-76007.peEgcr.rst diff --git a/Doc/deprecations/pending-removal-in-3.20.rst b/Doc/deprecations/pending-removal-in-3.20.rst index c86979c8ff9..21a561e7952 100644 --- a/Doc/deprecations/pending-removal-in-3.20.rst +++ b/Doc/deprecations/pending-removal-in-3.20.rst @@ -8,6 +8,7 @@ Pending removal in Python 3.20 - :mod:`argparse` - :mod:`csv` - :mod:`!ctypes.macholib` + - :mod:`imaplib` - :mod:`ipaddress` - :mod:`json` - :mod:`logging` (``__date__`` also deprecated) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index d3ae7c21a03..b360769b7e5 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -825,6 +825,7 @@ New deprecations - :mod:`argparse` - :mod:`csv` - :mod:`!ctypes.macholib` + - :mod:`imaplib` - :mod:`ipaddress` - :mod:`json` - :mod:`logging` (``__date__`` also deprecated) diff --git a/Lib/imaplib.py b/Lib/imaplib.py index cbe129b3e7c..c1767365481 100644 --- a/Lib/imaplib.py +++ b/Lib/imaplib.py @@ -21,8 +21,6 @@ # GET/SETANNOTATION contributed by Tomas Lindroos June 2005. # IDLE contributed by Forest August 2024. -__version__ = "2.60" - import binascii, errno, random, re, socket, subprocess, sys, time, calendar from datetime import datetime, timezone, timedelta from io import DEFAULT_BUFFER_SIZE @@ -247,7 +245,6 @@ def _connect(self): self._cmd_log_idx = 0 self._cmd_log = {} # Last '_cmd_log_len' interactions if self.debug >= 1: - self._mesg('imaplib version %s' % __version__) self._mesg('new IMAP4 connection, tag=%s' % self.tagpre) self.welcome = self._get_response() @@ -1965,3 +1962,12 @@ def run(cmd, args): ''' % sys.argv[0]) raise + + +def __getattr__(name): + if name == "__version__": + from warnings import _deprecated + + _deprecated("__version__", remove=(3, 20)) + return "2.60" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index 999e6e69ab4..430fa71fa29 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -1117,5 +1117,15 @@ def test_ssl_verified(self): client.shutdown() +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(imaplib, "__version__") + self.assertEqual(cm.filename, __file__) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2025-10-18-14-30-21.gh-issue-76007.peEgcr.rst b/Misc/NEWS.d/next/Library/2025-10-18-14-30-21.gh-issue-76007.peEgcr.rst new file mode 100644 index 00000000000..be56b2ca6a1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-18-14-30-21.gh-issue-76007.peEgcr.rst @@ -0,0 +1 @@ +Deprecate ``__version__`` from a :mod:`imaplib`. Patch by Hugo van Kemenade. From e4f6445f308109fdeb2c0e668c795fdb1ad335d0 Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Mon, 20 Oct 2025 09:14:23 -0400 Subject: [PATCH 224/373] gh-140166: Use `application/texinfo` as sole MIME type for `.texi` and `.texinfo` files (#140165) Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> --- Doc/whatsnew/3.15.rst | 2 ++ Lib/mimetypes.py | 4 ++-- Lib/test/test_mimetypes.py | 2 +- .../Library/2025-10-15-15-10-34.gh-issue-140166.NtxRez.rst | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-15-15-10-34.gh-issue-140166.NtxRez.rst diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index b360769b7e5..231ae565192 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -682,6 +682,8 @@ mimetypes --------- * Add ``application/toml``. (Contributed by Gil Forcada in :gh:`139959`.) +* Rename ``application/x-texinfo`` to ``application/texinfo``. + (Contributed by Charlie Lin in :gh:`140165`) pathlib diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py index 6c0efb67197..48a9f430d45 100644 --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -500,6 +500,8 @@ def _default_mime_types(): '.ps' : 'application/postscript', '.ai' : 'application/postscript', '.eps' : 'application/postscript', + '.texi' : 'application/texinfo', + '.texinfo': 'application/texinfo', '.toml' : 'application/toml', '.trig' : 'application/trig', '.m3u' : 'application/vnd.apple.mpegurl', @@ -549,8 +551,6 @@ def _default_mime_types(): '.tar' : 'application/x-tar', '.tcl' : 'application/x-tcl', '.tex' : 'application/x-tex', - '.texi' : 'application/x-texinfo', - '.texinfo': 'application/x-texinfo', '.roff' : 'application/x-troff', '.t' : 'application/x-troff', '.tr' : 'application/x-troff', diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index a29815bf49b..746984ec0ca 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -230,6 +230,7 @@ def check_extensions(): ("application/gzip", ".gz"), ("application/ogg", ".ogx"), ("application/postscript", ".ps"), + ("application/texinfo", ".texi"), ("application/toml", ".toml"), ("application/vnd.apple.mpegurl", ".m3u"), ("application/vnd.ms-excel", ".xls"), @@ -247,7 +248,6 @@ def check_extensions(): ("application/x-debian-package", ".deb"), ("application/x-httpd-php", ".php"), ("application/x-rpm", ".rpm"), - ("application/x-texinfo", ".texi"), ("application/x-troff", ".roff"), ("application/xml", ".xsl"), ("application/yaml", ".yaml"), diff --git a/Misc/NEWS.d/next/Library/2025-10-15-15-10-34.gh-issue-140166.NtxRez.rst b/Misc/NEWS.d/next/Library/2025-10-15-15-10-34.gh-issue-140166.NtxRez.rst new file mode 100644 index 00000000000..c140db9dcd5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-15-15-10-34.gh-issue-140166.NtxRez.rst @@ -0,0 +1 @@ +:mod:`mimetypes`: Per the `IANA assignment `_, update the MIME type for the ``.texi`` and ``.texinfo`` file formats to ``application/texinfo``, instead of ``application/x-texinfo``. From f11ec6e643f54f4ee698f7dfc878812a315f2af4 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Mon, 20 Oct 2025 09:58:05 -0400 Subject: [PATCH 225/373] gh-140263: Fix data race in test_lock_two_threads (gh-140264) Clang-20 detects a data race between the unlock and the non-atomic read of the lock state. Use a relaxed load for the assertion to avoid the race. --- Modules/_testinternalcapi/test_lock.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_testinternalcapi/test_lock.c b/Modules/_testinternalcapi/test_lock.c index 8d8cb992b0e..ded76ca9fe6 100644 --- a/Modules/_testinternalcapi/test_lock.c +++ b/Modules/_testinternalcapi/test_lock.c @@ -91,7 +91,8 @@ test_lock_two_threads(PyObject *self, PyObject *obj) } while (v != 3 && iters < 200); // both the "locked" and the "has parked" bits should be set - assert(test_data.m._bits == 3); + v = _Py_atomic_load_uint8_relaxed(&test_data.m._bits); + assert(v == 3); PyMutex_Unlock(&test_data.m); PyEvent_Wait(&test_data.done); From 237dca52ba1e850758a3dd7e46adb78a4f70d43c Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 20 Oct 2025 10:01:20 -0400 Subject: [PATCH 226/373] Docs: replace an esoteric Von Neumann mention (#137598) * Docs: replace an esoteric Von Neumann mention * oops, don't need to edit topics.py --- Doc/reference/datamodel.rst | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 28ef5825159..882b05e8731 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -16,9 +16,8 @@ Objects, values and types single: data :dfn:`Objects` are Python's abstraction for data. All data in a Python program -is represented by objects or by relations between objects. (In a sense, and in -conformance to Von Neumann's model of a "stored program computer", code is also -represented by objects.) +is represented by objects or by relations between objects. Even code is +represented by objects. .. index:: pair: built-in function; id @@ -29,9 +28,6 @@ represented by objects.) single: mutable object single: immutable object -.. XXX it *is* now possible in some cases to change an object's - type, under certain controlled conditions - Every object has an identity, a type and a value. An object's *identity* never changes once it has been created; you may think of it as the object's address in memory. The :keyword:`is` operator compares the identity of two objects; the From 3222ea0f1444cac0733f89b36f1d48ae67a08ffb Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 20 Oct 2025 17:59:12 +0200 Subject: [PATCH 227/373] gh-83714: Set os.statx() members to None if missing from stx_mask (#140216) --- Doc/library/os.rst | 31 +++++- Modules/posixmodule.c | 216 ++++++++++++++++++++++++++++-------------- 2 files changed, 176 insertions(+), 71 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index c3ed6a97692..ef08e1bfa51 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3451,7 +3451,10 @@ features: .. attribute:: stx_mnt_id - Mount ID. + Mount identifier. + + Equal to ``None`` if :data:`STATX_MNT_ID` is missing from + :attr:`~os.statx_result.stx_mask`. .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel userspace API headers >= 5.8. @@ -3460,6 +3463,9 @@ features: Direct I/O memory buffer alignment requirement. + Equal to ``None`` if :data:`STATX_DIOALIGN` is missing from + :attr:`~os.statx_result.stx_mask`. + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel userspace API headers >= 6.1. @@ -3467,12 +3473,18 @@ features: Direct I/O file offset alignment requirement. + Equal to ``None`` if :data:`STATX_DIOALIGN` is missing from + :attr:`~os.statx_result.stx_mask`. + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel userspace API headers >= 6.1. .. attribute:: stx_subvol - Subvolume ID. + Subvolume identifier. + + Equal to ``None`` if :data:`STATX_SUBVOL` is missing from + :attr:`~os.statx_result.stx_mask`. .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel userspace API headers >= 6.10. @@ -3481,6 +3493,9 @@ features: Minimum size for direct I/O with torn-write protection. + Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from + :attr:`~os.statx_result.stx_mask`. + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel userspace API headers >= 6.11. @@ -3488,6 +3503,9 @@ features: Maximum size for direct I/O with torn-write protection. + Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from + :attr:`~os.statx_result.stx_mask`. + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel userspace API headers >= 6.11. @@ -3495,6 +3513,9 @@ features: Maximum optimized size for direct I/O with torn-write protection. + Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from + :attr:`~os.statx_result.stx_mask`. + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel userspace API headers >= 6.16. @@ -3502,6 +3523,9 @@ features: Maximum iovecs for direct I/O with torn-write protection. + Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from + :attr:`~os.statx_result.stx_mask`. + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel userspace API headers >= 6.11. @@ -3509,6 +3533,9 @@ features: Direct I/O file offset alignment requirement for reads. + Equal to ``None`` if :data:`STATX_DIO_READ_ALIGN` is missing from + :attr:`~os.statx_result.stx_mask`. + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel userspace API headers >= 6.14. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index e9a3b6d29b8..1cfb068680c 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -3319,6 +3319,8 @@ typedef struct { struct statx stx; } Py_statx_result; +#define Py_statx_result_CAST(op) _Py_CAST(Py_statx_result*, (op)) + #define M(attr, type, offset, doc) \ {attr, type, offset, Py_READONLY, PyDoc_STR(doc)} #define MM(attr, type, member, doc) \ @@ -3330,53 +3332,15 @@ static PyMemberDef pystatx_result_members[] = { MM(stx_mask, Py_T_UINT, mask, "member validity mask"), MM(st_blksize, Py_T_UINT, blksize, "blocksize for filesystem I/O"), MM(stx_attributes, Py_T_ULONGLONG, attributes, "Linux inode attribute bits"), - MM(st_nlink, Py_T_UINT, nlink, "number of hard links"), - MM(st_uid, Py_T_UINT, uid, "user ID of owner"), - MM(st_gid, Py_T_UINT, gid, "group ID of owner"), MM(st_mode, Py_T_USHORT, mode, "protection bits"), - MM(st_ino, Py_T_ULONGLONG, ino, "inode"), - MM(st_size, Py_T_ULONGLONG, size, "total size, in bytes"), - MM(st_blocks, Py_T_ULONGLONG, blocks, "number of blocks allocated"), MM(stx_attributes_mask, Py_T_ULONGLONG, attributes_mask, "Mask of supported bits in stx_attributes"), - MX(st_atime, Py_T_DOUBLE, atime_sec, "time of last access"), - MX(st_birthtime, Py_T_DOUBLE, btime_sec, "time of creation"), - MX(st_ctime, Py_T_DOUBLE, ctime_sec, "time of last change"), - MX(st_mtime, Py_T_DOUBLE, mtime_sec, "time of last modification"), MM(stx_rdev_major, Py_T_UINT, rdev_major, "represented device major number"), MM(stx_rdev_minor, Py_T_UINT, rdev_minor, "represented device minor number"), MX(st_rdev, Py_T_ULONGLONG, rdev, "device type (if inode device)"), MM(stx_dev_major, Py_T_UINT, dev_major, "containing device major number"), MM(stx_dev_minor, Py_T_UINT, dev_minor, "containing device minor number"), MX(st_dev, Py_T_ULONGLONG, dev, "device"), -#ifdef STATX_MNT_ID - MM(stx_mnt_id, Py_T_ULONGLONG, mnt_id, "mount ID"), -#endif -#ifdef STATX_DIOALIGN - MM(stx_dio_mem_align, Py_T_UINT, dio_mem_align, - "direct I/O memory buffer alignment"), - MM(stx_dio_offset_align, Py_T_UINT, dio_offset_align, - "direct I/O file offset alignment"), -#endif -#ifdef STATX_SUBVOL - MM(stx_subvol, Py_T_ULONGLONG, subvol, "subvolume ID"), -#endif -#ifdef STATX_WRITE_ATOMIC - MM(stx_atomic_write_unit_min, Py_T_UINT, atomic_write_unit_min, - "minimum size for direct I/O with torn-write protection"), - MM(stx_atomic_write_unit_max, Py_T_UINT, atomic_write_unit_max, - "maximum size for direct I/O with torn-write protection"), - MM(stx_atomic_write_segments_max, Py_T_UINT, atomic_write_segments_max, - "maximum iovecs for direct I/O with torn-write protection"), -#endif -#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT - MM(stx_atomic_write_unit_max_opt, Py_T_UINT, atomic_write_unit_max_opt, - "maximum optimized size for direct I/O with torn-write protection"), -#endif -#ifdef STATX_DIO_READ_ALIGN - MM(stx_dio_read_offset_align, Py_T_UINT, dio_read_offset_align, - "direct I/O file offset alignment for reads"), -#endif {NULL}, }; @@ -3384,31 +3348,150 @@ static PyMemberDef pystatx_result_members[] = { #undef MM #undef M -static PyObject * -pystatx_result_get_nsec(PyObject *op, void *context) -{ - uint16_t offset = (uintptr_t)context; - struct statx_timestamp *ts = (void*)op + offset; - _posixstate *state = PyType_GetModuleState(Py_TYPE(op)); - assert(state != NULL); - return stat_nanosecond_timestamp(state, ts->tv_sec, ts->tv_nsec); -} -/* The low 16 bits of the context pointer are the offset from the start of - Py_statx_result to the struct statx member. */ -#define GM(attr, type, member, doc) \ - {#attr, pystatx_result_get_##type, NULL, PyDoc_STR(doc), \ - (void *)(offsetof(Py_statx_result, stx.stx_##member))} +#define STATX_GET_UINT(ATTR, MEMBER, MASK) \ + static PyObject* \ + pystatx_result_get_##ATTR(PyObject *op, void *Py_UNUSED(context)) \ + { \ + Py_statx_result *self = Py_statx_result_CAST(op); \ + if (!(self->stx.stx_mask & MASK)) { \ + Py_RETURN_NONE; \ + } \ + unsigned long value = self->stx.MEMBER; \ + return PyLong_FromUnsignedLong(value); \ + } + +STATX_GET_UINT(st_uid, stx_uid, STATX_UID) +STATX_GET_UINT(st_gid, stx_gid, STATX_GID) +STATX_GET_UINT(st_nlink, stx_nlink, STATX_NLINK) +#ifdef STATX_DIOALIGN +STATX_GET_UINT(stx_dio_mem_align, stx_dio_mem_align, STATX_DIOALIGN) +STATX_GET_UINT(stx_dio_offset_align, stx_dio_offset_align, STATX_DIOALIGN) +#endif +#ifdef STATX_DIO_READ_ALIGN +STATX_GET_UINT(stx_dio_read_offset_align, stx_dio_read_offset_align, + STATX_DIO_READ_ALIGN) +#endif +#ifdef STATX_WRITE_ATOMIC +STATX_GET_UINT(stx_atomic_write_unit_min, stx_atomic_write_unit_min, + STATX_WRITE_ATOMIC) +STATX_GET_UINT(stx_atomic_write_unit_max, stx_atomic_write_unit_max, + STATX_WRITE_ATOMIC) +STATX_GET_UINT(stx_atomic_write_segments_max, stx_atomic_write_segments_max, + STATX_WRITE_ATOMIC) +#endif +#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT +STATX_GET_UINT(stx_atomic_write_unit_max_opt, stx_atomic_write_unit_max_opt, + STATX_WRITE_ATOMIC) +#endif + + +#define STATX_GET_ULONGLONG(ATTR, MEMBER, MASK) \ + static PyObject* \ + pystatx_result_get_##ATTR(PyObject *op, void *Py_UNUSED(context)) \ + { \ + Py_statx_result *self = Py_statx_result_CAST(op); \ + if (!(self->stx.stx_mask & MASK)) { \ + Py_RETURN_NONE; \ + } \ + unsigned long long value = self->stx.MEMBER; \ + return PyLong_FromUnsignedLongLong(value); \ + } + +STATX_GET_ULONGLONG(st_blocks, stx_blocks, STATX_BLOCKS) +STATX_GET_ULONGLONG(st_ino, stx_ino, STATX_INO) +STATX_GET_ULONGLONG(st_size, stx_size, STATX_SIZE) +#ifdef STATX_MNT_ID +STATX_GET_ULONGLONG(stx_mnt_id, stx_mnt_id, STATX_MNT_ID) +#endif +#ifdef STATX_SUBVOL +STATX_GET_ULONGLONG(stx_subvol, stx_subvol, STATX_SUBVOL) +#endif + + +#define STATX_GET_DOUBLE(ATTR, MEMBER, MASK) \ + static PyObject* \ + pystatx_result_get_##ATTR(PyObject *op, void *Py_UNUSED(context)) \ + { \ + Py_statx_result *self = Py_statx_result_CAST(op); \ + if (!(self->stx.stx_mask & MASK)) { \ + Py_RETURN_NONE; \ + } \ + double sec = self->MEMBER; \ + return PyFloat_FromDouble(sec); \ + } + +STATX_GET_DOUBLE(st_atime, atime_sec, STATX_ATIME) +STATX_GET_DOUBLE(st_birthtime, btime_sec, STATX_BTIME) +STATX_GET_DOUBLE(st_ctime, ctime_sec, STATX_CTIME) +STATX_GET_DOUBLE(st_mtime, mtime_sec, STATX_MTIME) + +#define STATX_GET_NSEC(ATTR, MEMBER, MASK) \ + static PyObject* \ + pystatx_result_get_##ATTR(PyObject *op, void *context) \ + { \ + Py_statx_result *self = Py_statx_result_CAST(op); \ + if (!(self->stx.stx_mask & MASK)) { \ + Py_RETURN_NONE; \ + } \ + struct statx_timestamp *ts = &self->stx.MEMBER; \ + _posixstate *state = PyType_GetModuleState(Py_TYPE(op)); \ + assert(state != NULL); \ + return stat_nanosecond_timestamp(state, ts->tv_sec, ts->tv_nsec); \ + } + +STATX_GET_NSEC(st_atime_ns, stx_atime, STATX_ATIME) +STATX_GET_NSEC(st_birthtime_ns, stx_btime, STATX_BTIME) +STATX_GET_NSEC(st_ctime_ns, stx_ctime, STATX_CTIME) +STATX_GET_NSEC(st_mtime_ns, stx_mtime, STATX_MTIME) + +#define G(attr, doc) \ + {#attr, pystatx_result_get_##attr, NULL, PyDoc_STR(doc), NULL} static PyGetSetDef pystatx_result_getset[] = { - GM(st_atime_ns, nsec, atime, "time of last access in nanoseconds"), - GM(st_birthtime_ns, nsec, btime, "time of creation in nanoseconds"), - GM(st_ctime_ns, nsec, ctime, "time of last change in nanoseconds"), - GM(st_mtime_ns, nsec, mtime, "time of last modification in nanoseconds"), + G(st_nlink, "number of hard links"), + G(st_uid, "user ID of owner"), + G(st_gid, "group ID of owner"), + G(st_ino, "inode"), + G(st_size, "total size, in bytes"), + G(st_blocks, "number of blocks allocated"), + G(st_atime, "time of last access"), + G(st_atime_ns, "time of last access in nanoseconds"), + G(st_birthtime, "time of creation"), + G(st_birthtime_ns, "time of creation in nanoseconds"), + G(st_ctime, "time of last change"), + G(st_ctime_ns, "time of last change in nanoseconds"), + G(st_mtime, "time of last modification"), + G(st_mtime_ns, "time of last modification in nanoseconds"), +#ifdef STATX_MNT_ID + G(stx_mnt_id, "mount ID"), +#endif +#ifdef STATX_DIOALIGN + G(stx_dio_mem_align, "direct I/O memory buffer alignment"), + G(stx_dio_offset_align, "direct I/O file offset alignment"), +#endif +#ifdef STATX_SUBVOL + G(stx_subvol, "subvolume ID"), +#endif +#ifdef STATX_WRITE_ATOMIC + G(stx_atomic_write_unit_min, + "minimum size for direct I/O with torn-write protection"), + G(stx_atomic_write_unit_max, + "maximum size for direct I/O with torn-write protection"), + G(stx_atomic_write_segments_max, + "maximum iovecs for direct I/O with torn-write protection"), +#endif +#ifdef STATX_DIO_READ_ALIGN + G(stx_dio_read_offset_align, "direct I/O file offset alignment for reads"), +#endif +#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT + G(stx_atomic_write_unit_max_opt, + "maximum optimized size for direct I/O with torn-write protection"), +#endif {NULL}, }; -#undef GM +#undef G static PyObject * pystatx_result_repr(PyObject *op) @@ -3446,24 +3529,19 @@ pystatx_result_repr(PyObject *op) Py_DECREF(o); } - if (Py_ARRAY_LENGTH(pystatx_result_members) > 1 - && Py_ARRAY_LENGTH(pystatx_result_getset) > 1) { - WRITE_ASCII(", "); - } - for (size_t i = 0; i < Py_ARRAY_LENGTH(pystatx_result_getset) - 1; ++i) { - if (i > 0) { - WRITE_ASCII(", "); - } - PyGetSetDef *d = &pystatx_result_getset[i]; - WRITE_ASCII(d->name); - WRITE_ASCII("="); - PyObject *o = d->get(op, d->closure); if (o == NULL) { goto error; } + if (o == Py_None) { + continue; + } + + WRITE_ASCII(", "); + WRITE_ASCII(d->name); + WRITE_ASCII("="); if (PyUnicodeWriter_WriteRepr(writer, o) < 0) { Py_DECREF(o); goto error; From 38d4b436ca767351db834189b3a5379406cd52a8 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 20 Oct 2025 20:08:47 +0300 Subject: [PATCH 228/373] gh-63161: Fix tokenize.detect_encoding() (GH-139446) * Support non-UTF-8 shebang and comments if non-UTF-8 encoding is specified. * Detect decoding error for non-UTF-8 encoding. * Detect null bytes in source code. --- Lib/test/test_tokenize.py | 77 +++++++++++++++++++ Lib/tokenize.py | 22 ++++-- ...5-09-30-12-52-54.gh-issue-63161.mECM1A.rst | 3 + 3 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-09-30-12-52-54.gh-issue-63161.mECM1A.rst diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index 8fdd03f347b..d274726eed2 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -1495,6 +1495,61 @@ def test_cookie_second_line_noncommented_first_line(self): expected = [b"print('\xc2\xa3')\n"] self.assertEqual(consumed_lines, expected) + def test_first_non_utf8_coding_line(self): + lines = ( + b'#coding:iso-8859-15 \xa4\n', + b'print(something)\n' + ) + encoding, consumed_lines = tokenize.detect_encoding(self.get_readline(lines)) + self.assertEqual(encoding, 'iso-8859-15') + self.assertEqual(consumed_lines, list(lines[:1])) + + def test_first_utf8_coding_line_error(self): + lines = ( + b'#coding:ascii \xc3\xa4\n', + b'print(something)\n' + ) + with self.assertRaises(SyntaxError): + tokenize.detect_encoding(self.get_readline(lines)) + + def test_second_non_utf8_coding_line(self): + lines = ( + b'#!/usr/bin/python\n', + b'#coding:iso-8859-15 \xa4\n', + b'print(something)\n' + ) + encoding, consumed_lines = tokenize.detect_encoding(self.get_readline(lines)) + self.assertEqual(encoding, 'iso-8859-15') + self.assertEqual(consumed_lines, list(lines[:2])) + + def test_second_utf8_coding_line_error(self): + lines = ( + b'#!/usr/bin/python\n', + b'#coding:ascii \xc3\xa4\n', + b'print(something)\n' + ) + with self.assertRaises(SyntaxError): + tokenize.detect_encoding(self.get_readline(lines)) + + def test_non_utf8_shebang(self): + lines = ( + b'#!/home/\xa4/bin/python\n', + b'#coding:iso-8859-15\n', + b'print(something)\n' + ) + encoding, consumed_lines = tokenize.detect_encoding(self.get_readline(lines)) + self.assertEqual(encoding, 'iso-8859-15') + self.assertEqual(consumed_lines, list(lines[:2])) + + def test_utf8_shebang_error(self): + lines = ( + b'#!/home/\xc3\xa4/bin/python\n', + b'#coding:ascii\n', + b'print(something)\n' + ) + with self.assertRaises(SyntaxError): + tokenize.detect_encoding(self.get_readline(lines)) + def test_cookie_second_line_empty_first_line(self): lines = ( b'\n', @@ -1548,6 +1603,28 @@ def test_double_coding_utf8(self): self.assertEqual(encoding, 'utf-8') self.assertEqual(consumed_lines, list(lines[:1])) + def test_nul_in_first_coding_line(self): + lines = ( + b'#coding:iso8859-15\x00\n', + b'\n', + b'\n', + b'print(something)\n' + ) + with self.assertRaisesRegex(SyntaxError, + "source code cannot contain null bytes"): + tokenize.detect_encoding(self.get_readline(lines)) + + def test_nul_in_second_coding_line(self): + lines = ( + b'#!/usr/bin/python\n', + b'#coding:iso8859-15\x00\n', + b'\n', + b'print(something)\n' + ) + with self.assertRaisesRegex(SyntaxError, + "source code cannot contain null bytes"): + tokenize.detect_encoding(self.get_readline(lines)) + def test_latin1_normalization(self): # See get_normal_name() in Parser/tokenizer/helpers.c. encodings = ("latin-1", "iso-8859-1", "iso-latin-1", "latin-1-unix", diff --git a/Lib/tokenize.py b/Lib/tokenize.py index 7e71755068e..1f31258ce36 100644 --- a/Lib/tokenize.py +++ b/Lib/tokenize.py @@ -36,7 +36,7 @@ from token import EXACT_TOKEN_TYPES import _tokenize -cookie_re = re.compile(r'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)', re.ASCII) +cookie_re = re.compile(br'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)', re.ASCII) blank_re = re.compile(br'^[ \t\f]*(?:[#\r\n]|$)', re.ASCII) import token @@ -385,22 +385,23 @@ def read_or_stop(): except StopIteration: return b'' - def find_cookie(line): + def check(line, encoding): + # Check if the line matches the encoding. + if 0 in line: + raise SyntaxError("source code cannot contain null bytes") try: - # Decode as UTF-8. Either the line is an encoding declaration, - # in which case it should be pure ASCII, or it must be UTF-8 - # per default encoding. - line_string = line.decode('utf-8') + line.decode(encoding) except UnicodeDecodeError: msg = "invalid or missing encoding declaration" if filename is not None: msg = '{} for {!r}'.format(msg, filename) raise SyntaxError(msg) - match = cookie_re.match(line_string) + def find_cookie(line): + match = cookie_re.match(line) if not match: return None - encoding = _get_normal_name(match.group(1)) + encoding = _get_normal_name(match.group(1).decode()) try: codec = lookup(encoding) except LookupError: @@ -433,18 +434,23 @@ def find_cookie(line): encoding = find_cookie(first) if encoding: + check(first, encoding) return encoding, [first] if not blank_re.match(first): + check(first, default) return default, [first] second = read_or_stop() if not second: + check(first, default) return default, [first] encoding = find_cookie(second) if encoding: + check(first + second, encoding) return encoding, [first, second] + check(first + second, default) return default, [first, second] diff --git a/Misc/NEWS.d/next/Library/2025-09-30-12-52-54.gh-issue-63161.mECM1A.rst b/Misc/NEWS.d/next/Library/2025-09-30-12-52-54.gh-issue-63161.mECM1A.rst new file mode 100644 index 00000000000..3daed20d099 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-30-12-52-54.gh-issue-63161.mECM1A.rst @@ -0,0 +1,3 @@ +Fix :func:`tokenize.detect_encoding`. Support non-UTF-8 shebang and comments +if non-UTF-8 encoding is specified. Detect decoding error for non-UTF-8 +encoding. Detect null bytes in source code. From d467f24d0f886b8a76944726eb46bf0e3bb667a8 Mon Sep 17 00:00:00 2001 From: yihong Date: Tue, 21 Oct 2025 03:43:10 +0800 Subject: [PATCH 229/373] gh-133656: Remove unused import and private global in `Lib/zipimport.py` (GH-140325) --- Lib/zipimport.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Lib/zipimport.py b/Lib/zipimport.py index 188c4bca977..340a7e07112 100644 --- a/Lib/zipimport.py +++ b/Lib/zipimport.py @@ -10,15 +10,12 @@ to Zip archives. """ -#from importlib import _bootstrap_external -#from importlib import _bootstrap # for _verbose_message import _frozen_importlib_external as _bootstrap_external from _frozen_importlib_external import _unpack_uint16, _unpack_uint32, _unpack_uint64 import _frozen_importlib as _bootstrap # for _verbose_message import _imp # for check_hash_based_pycs import _io # for open import marshal # for loads -import sys # for modules import time # for mktime __all__ = ['ZipImportError', 'zipimporter'] @@ -34,8 +31,6 @@ class ZipImportError(ImportError): # _read_directory() cache _zip_directory_cache = {} -_module_type = type(sys) - END_CENTRAL_DIR_SIZE = 22 END_CENTRAL_DIR_SIZE_64 = 56 END_CENTRAL_DIR_LOCATOR_SIZE_64 = 20 From c6be6e453730228053783f3444cb62e1425a3feb Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Mon, 20 Oct 2025 21:55:08 +0200 Subject: [PATCH 230/373] gh-138891: fix star-unpack in get_annotations (#138951) --- Lib/annotationlib.py | 22 +++++++++++-------- Lib/test/test_annotationlib.py | 6 +++++ ...-09-15-21-03-11.gh-issue-138891.oZFdtR.rst | 2 ++ 3 files changed, 21 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-09-15-21-03-11.gh-issue-138891.oZFdtR.rst diff --git a/Lib/annotationlib.py b/Lib/annotationlib.py index 43e1d51bc4b..544e069626d 100644 --- a/Lib/annotationlib.py +++ b/Lib/annotationlib.py @@ -241,15 +241,8 @@ def __forward_code__(self): if self.__code__ is not None: return self.__code__ arg = self.__forward_arg__ - # If we do `def f(*args: *Ts)`, then we'll have `arg = '*Ts'`. - # Unfortunately, this isn't a valid expression on its own, so we - # do the unpacking manually. - if arg.startswith("*"): - arg_to_compile = f"({arg},)[0]" # E.g. (*Ts,)[0] or (*tuple[int, int],)[0] - else: - arg_to_compile = arg try: - self.__code__ = compile(arg_to_compile, "", "eval") + self.__code__ = compile(_rewrite_star_unpack(arg), "", "eval") except SyntaxError: raise SyntaxError(f"Forward reference must be an expression -- got {arg!r}") return self.__code__ @@ -1025,7 +1018,8 @@ def get_annotations( locals = {param.__name__: param for param in type_params} | locals return_value = { - key: value if not isinstance(value, str) else eval(value, globals, locals) + key: value if not isinstance(value, str) + else eval(_rewrite_star_unpack(value), globals, locals) for key, value in ann.items() } return return_value @@ -1062,6 +1056,16 @@ def annotations_to_string(annotations): } +def _rewrite_star_unpack(arg): + """If the given argument annotation expression is a star unpack e.g. `'*Ts'` + rewrite it to a valid expression. + """ + if arg.startswith("*"): + return f"({arg},)[0]" # E.g. (*Ts,)[0] or (*tuple[int, int],)[0] + else: + return arg + + def _get_and_call_annotate(obj, format): """Get the __annotate__ function and call it. diff --git a/Lib/test/test_annotationlib.py b/Lib/test/test_annotationlib.py index a8a8bcec76a..2c5bf2b3417 100644 --- a/Lib/test/test_annotationlib.py +++ b/Lib/test/test_annotationlib.py @@ -787,6 +787,12 @@ def test_stringized_annotations_in_empty_module(self): self.assertEqual(get_annotations(isa2, eval_str=True), {}) self.assertEqual(get_annotations(isa2, eval_str=False), {}) + def test_stringized_annotations_with_star_unpack(self): + def f(*args: *tuple[int, ...]): ... + self.assertEqual(get_annotations(f, eval_str=True), + {'args': (*tuple[int, ...],)[0]}) + + def test_stringized_annotations_on_wrapper(self): isa = inspect_stringized_annotations wrapped = times_three(isa.function) diff --git a/Misc/NEWS.d/next/Library/2025-09-15-21-03-11.gh-issue-138891.oZFdtR.rst b/Misc/NEWS.d/next/Library/2025-09-15-21-03-11.gh-issue-138891.oZFdtR.rst new file mode 100644 index 00000000000..f7ecb05d20c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-15-21-03-11.gh-issue-138891.oZFdtR.rst @@ -0,0 +1,2 @@ +Fix ``SyntaxError`` when ``inspect.get_annotations(f, eval_str=True)`` is +called on a function annotated with a :pep:`646` ``star_expression`` From a752f58d6b628ab4fe6e4263137ec4cc244ff390 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 20 Oct 2025 21:09:33 +0100 Subject: [PATCH 231/373] [Docs] Add missing note about Required/NotRequired/ReadOnly in get_type_hints() (#139565) Co-authored-by: Jelle Zijlstra --- Doc/library/typing.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 41f22aa72cd..e5d116c702e 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -3354,7 +3354,8 @@ Introspection helpers ``__annotations__`` dictionaries. Annotations on classes appearing earlier in the :term:`method resolution order` always take precedence over annotations on classes appearing later in the method resolution order. - * The function recursively replaces all occurrences of ``Annotated[T, ...]`` + * The function recursively replaces all occurrences of + ``Annotated[T, ...]``, ``Required[T]``, ``NotRequired[T]``, and ``ReadOnly[T]`` with ``T``, unless *include_extras* is set to ``True`` (see :class:`Annotated` for more information). From e09837fcbf39d07c0af51aa936e95c50aed4787c Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 21 Oct 2025 00:10:44 +0300 Subject: [PATCH 232/373] gh-133601: Remove deprecated `typing.no_type_check_decorator` (#133602) --- Doc/deprecations/pending-removal-in-3.15.rst | 2 +- Doc/library/typing.rst | 15 --------- Doc/whatsnew/3.13.rst | 2 +- Doc/whatsnew/3.15.rst | 3 ++ Lib/test/test_typing.py | 31 +------------------ Lib/typing.py | 18 ----------- Misc/NEWS.d/3.13.0a1.rst | 2 +- ...-05-07-22-09-28.gh-issue-133601.9kUL3P.rst | 1 + 8 files changed, 8 insertions(+), 66 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-05-07-22-09-28.gh-issue-133601.9kUL3P.rst diff --git a/Doc/deprecations/pending-removal-in-3.15.rst b/Doc/deprecations/pending-removal-in-3.15.rst index 076346be621..09cbd6f01a0 100644 --- a/Doc/deprecations/pending-removal-in-3.15.rst +++ b/Doc/deprecations/pending-removal-in-3.15.rst @@ -92,7 +92,7 @@ Pending removal in Python 3.15 Use ``class TD(TypedDict): pass`` or ``TD = TypedDict("TD", {})`` to create a TypedDict with zero field. - * The :func:`typing.no_type_check_decorator` decorator function + * The :func:`!typing.no_type_check_decorator` decorator function has been deprecated since Python 3.13. After eight years in the :mod:`typing` module, it has yet to be supported by any major type checker. diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index e5d116c702e..58f3a8ecd7c 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -3258,17 +3258,6 @@ Functions and decorators ``@no_type_check`` mutates the decorated object in place. -.. decorator:: no_type_check_decorator - - Decorator to give another decorator the :func:`no_type_check` effect. - - This wraps the decorator with something that wraps the decorated - function in :func:`no_type_check`. - - .. deprecated-removed:: 3.13 3.15 - No type checker ever added support for ``@no_type_check_decorator``. It - is therefore deprecated, and will be removed in Python 3.15. - .. decorator:: override Decorator to indicate that a method in a subclass is intended to override a @@ -4114,10 +4103,6 @@ convenience. This is subject to change, and not all deprecations are listed. - 3.12 - Undecided - :pep:`695` - * - :func:`@typing.no_type_check_decorator ` - - 3.13 - - 3.15 - - :gh:`106309` * - :data:`typing.AnyStr` - 3.13 - 3.18 diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 499f6f0ff4e..dc105156f33 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1993,7 +1993,7 @@ New Deprecations use ``class TD(TypedDict): pass`` or ``TD = TypedDict("TD", {})``. (Contributed by Alex Waygood in :gh:`105566` and :gh:`105570`.) - * Deprecate the :func:`typing.no_type_check_decorator` decorator function, + * Deprecate the :func:`!typing.no_type_check_decorator` decorator function, to be removed in Python 3.15. After eight years in the :mod:`typing` module, it has yet to be supported by any major type checker. diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 231ae565192..d406b263f17 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -764,6 +764,9 @@ typing :func:`issubclass`, but warnings were not previously emitted if it was merely imported or accessed from the :mod:`!typing` module. +* Deprecated :func:`!typing.no_type_check_decorator` has been removed. + (Contributed by Nikita Sobolev in :gh:`133601`.) + unicodedata ----------- diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index db0501d70e3..4076004bc13 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -33,7 +33,7 @@ from typing import is_typeddict, is_protocol from typing import reveal_type from typing import dataclass_transform -from typing import no_type_check, no_type_check_decorator +from typing import no_type_check from typing import Type from typing import NamedTuple, NotRequired, Required, ReadOnly, TypedDict, NoExtraItems from typing import IO, TextIO, BinaryIO @@ -6376,35 +6376,6 @@ class F: for clazz in [C, D, E, F]: self.assertEqual(get_type_hints(clazz), expected_result) - def test_meta_no_type_check(self): - depr_msg = ( - "'typing.no_type_check_decorator' is deprecated " - "and slated for removal in Python 3.15" - ) - with self.assertWarnsRegex(DeprecationWarning, depr_msg): - @no_type_check_decorator - def magic_decorator(func): - return func - - self.assertEqual(magic_decorator.__name__, 'magic_decorator') - - @magic_decorator - def foo(a: 'whatevers') -> {}: - pass - - @magic_decorator - class C: - def foo(a: 'whatevers') -> {}: - pass - - self.assertEqual(foo.__name__, 'foo') - th = get_type_hints(foo) - self.assertEqual(th, {}) - cth = get_type_hints(C.foo) - self.assertEqual(cth, {}) - ith = get_type_hints(C().foo) - self.assertEqual(ith, {}) - class InternalsTests(BaseTestCase): def test_collect_parameters(self): diff --git a/Lib/typing.py b/Lib/typing.py index 25234d2d707..eb0519986a8 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -139,7 +139,6 @@ 'Never', 'NewType', 'no_type_check', - 'no_type_check_decorator', 'NoDefault', 'NoExtraItems', 'NoReturn', @@ -2623,23 +2622,6 @@ def no_type_check(arg): return arg -def no_type_check_decorator(decorator): - """Decorator to give another decorator the @no_type_check effect. - - This wraps the decorator with something that wraps the decorated - function in @no_type_check. - """ - import warnings - warnings._deprecated("typing.no_type_check_decorator", remove=(3, 15)) - @functools.wraps(decorator) - def wrapped_decorator(*args, **kwds): - func = decorator(*args, **kwds) - func = no_type_check(func) - return func - - return wrapped_decorator - - def _overload_dummy(*args, **kwds): """Helper for @overload to raise when called.""" raise NotImplementedError( diff --git a/Misc/NEWS.d/3.13.0a1.rst b/Misc/NEWS.d/3.13.0a1.rst index a5f0161e8f5..1391d670575 100644 --- a/Misc/NEWS.d/3.13.0a1.rst +++ b/Misc/NEWS.d/3.13.0a1.rst @@ -3426,7 +3426,7 @@ This bug was introduced in Python 3.12.0 beta 1. .. nonce: hSlB17 .. section: Library -Deprecate :func:`typing.no_type_check_decorator`. No major type checker ever +Deprecate :func:`!typing.no_type_check_decorator`. No major type checker ever added support for this decorator. Patch by Alex Waygood. .. diff --git a/Misc/NEWS.d/next/Library/2025-05-07-22-09-28.gh-issue-133601.9kUL3P.rst b/Misc/NEWS.d/next/Library/2025-05-07-22-09-28.gh-issue-133601.9kUL3P.rst new file mode 100644 index 00000000000..62f40aee7aa --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-07-22-09-28.gh-issue-133601.9kUL3P.rst @@ -0,0 +1 @@ +Remove deprecated :func:`!typing.no_type_check_decorator`. From b2f9fb9db29296410f76009ac65cf81afa8a92a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20Gr=C3=B8n=C3=A5s=20Drange?= Date: Tue, 21 Oct 2025 01:54:44 +0200 Subject: [PATCH 233/373] gh-140358: Bring back elapsed time and unreachable count to gc debug output (#140359) --- Lib/test/test_gc.py | 26 +++++++++++++++++++ ...-10-20-11-24-36.gh-issue-140358.UQuKdV.rst | 4 +++ Python/gc.c | 13 ++++++++++ 3 files changed, 43 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-20-11-24-36.gh-issue-140358.UQuKdV.rst diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index 70d9d009fa8..08055bb8e6b 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -801,6 +801,32 @@ def __del__(self): rc, out, err = assert_python_ok('-c', code) self.assertEqual(out.strip(), b'__del__ called') + @unittest.skipIf(Py_GIL_DISABLED, "requires GC generations or increments") + def test_gc_debug_stats(self): + # Checks that debug information is printed to stderr + # when DEBUG_STATS is set. + code = """if 1: + import gc + gc.set_debug(%s) + gc.collect() + """ + _, _, err = assert_python_ok("-c", code % "gc.DEBUG_STATS") + self.assertRegex(err, b"gc: collecting generation [0-9]+") + self.assertRegex( + err, + b"gc: objects in each generation: [0-9]+ [0-9]+ [0-9]+", + ) + self.assertRegex( + err, b"gc: objects in permanent generation: [0-9]+" + ) + self.assertRegex( + err, + b"gc: done, .* unreachable, .* uncollectable, .* elapsed", + ) + + _, _, err = assert_python_ok("-c", code % "0") + self.assertNotIn(b"elapsed", err) + def test_global_del_SystemExit(self): code = """if 1: class ClassWithDel: diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-20-11-24-36.gh-issue-140358.UQuKdV.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-20-11-24-36.gh-issue-140358.UQuKdV.rst new file mode 100644 index 00000000000..739228f7e36 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-20-11-24-36.gh-issue-140358.UQuKdV.rst @@ -0,0 +1,4 @@ +Restore elapsed time and unreachable object count in GC debug output. These +were inadvertently removed during a refactor of ``gc.c``. The debug log now +again reports elapsed collection time and the number of unreachable objects. +Contributed by Pål Grønås Drange. diff --git a/Python/gc.c b/Python/gc.c index 79c7476f4a9..fd724301c76 100644 --- a/Python/gc.c +++ b/Python/gc.c @@ -2077,8 +2077,10 @@ _PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason) if (reason != _Py_GC_REASON_SHUTDOWN) { invoke_gc_callback(gcstate, "start", generation, &stats); } + PyTime_t t1; if (gcstate->debug & _PyGC_DEBUG_STATS) { PySys_WriteStderr("gc: collecting generation %d...\n", generation); + (void)PyTime_PerfCounterRaw(&t1); show_stats_each_generations(gcstate); } if (PyDTrace_GC_START_ENABLED()) { @@ -2115,6 +2117,17 @@ _PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason) #endif validate_spaces(gcstate); _Py_atomic_store_int(&gcstate->collecting, 0); + + if (gcstate->debug & _PyGC_DEBUG_STATS) { + PyTime_t t2; + (void)PyTime_PerfCounterRaw(&t2); + double d = PyTime_AsSecondsDouble(t2 - t1); + PySys_WriteStderr( + "gc: done, %zd unreachable, %zd uncollectable, %.4fs elapsed\n", + stats.collected + stats.uncollectable, stats.uncollectable, d + ); + } + return stats.uncollectable + stats.collected; } From 47d2f68df215b5bcbc76a50d3f4e0b68791e3e79 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Tue, 21 Oct 2025 08:12:04 +0100 Subject: [PATCH 234/373] gh-139707: Better `ModuleNotFoundError` message for missing stdlib modules (GH-140219) --- Lib/test/test_traceback.py | 16 ++++++++++++++-- Lib/traceback.py | 13 ++++++++----- ...025-10-16-16-10-11.gh-issue-139707.zR6Qtn.rst | 2 ++ 3 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-16-16-10-11.gh-issue-139707.zR6Qtn.rst diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index bd3ecfd9a38..bf57867a871 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -5034,7 +5034,8 @@ def test_no_site_package_flavour(self): self.assertIn( (b"Site initialization is disabled, did you forget to " - b"add the site-packages directory to sys.path?"), stderr + b"add the site-packages directory to sys.path " + b"or to enable your virtual environment?"), stderr ) code = """ @@ -5046,9 +5047,20 @@ def test_no_site_package_flavour(self): self.assertNotIn( (b"Site initialization is disabled, did you forget to " - b"add the site-packages directory to sys.path?"), stderr + b"add the site-packages directory to sys.path " + b"or to enable your virtual environment?"), stderr ) + def test_missing_stdlib_package(self): + code = """ + import sys + sys.stdlib_module_names |= {'spam'} + import spam + """ + _, _, stderr = assert_python_failure('-S', '-c', code) + + self.assertIn(b"Standard library module 'spam' was not found", stderr) + class TestColorizedTraceback(unittest.TestCase): maxDiff = None diff --git a/Lib/traceback.py b/Lib/traceback.py index 692d4483793..9b4b8c7d566 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -1107,11 +1107,14 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, suggestion = _compute_suggestion_error(exc_value, exc_traceback, wrong_name) if suggestion: self._str += f". Did you mean: '{suggestion}'?" - elif exc_type and issubclass(exc_type, ModuleNotFoundError) and \ - sys.flags.no_site and \ - getattr(exc_value, "name", None) not in sys.stdlib_module_names: - self._str += (". Site initialization is disabled, did you forget to " - + "add the site-packages directory to sys.path?") + elif exc_type and issubclass(exc_type, ModuleNotFoundError): + module_name = getattr(exc_value, "name", None) + if module_name in sys.stdlib_module_names: + self._str = f"Standard library module '{module_name}' was not found" + elif sys.flags.no_site: + self._str += (". Site initialization is disabled, did you forget to " + + "add the site-packages directory to sys.path " + + "or to enable your virtual environment?") elif exc_type and issubclass(exc_type, (NameError, AttributeError)) and \ getattr(exc_value, "name", None) is not None: wrong_name = getattr(exc_value, "name", None) diff --git a/Misc/NEWS.d/next/Library/2025-10-16-16-10-11.gh-issue-139707.zR6Qtn.rst b/Misc/NEWS.d/next/Library/2025-10-16-16-10-11.gh-issue-139707.zR6Qtn.rst new file mode 100644 index 00000000000..c5460aae8b3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-16-16-10-11.gh-issue-139707.zR6Qtn.rst @@ -0,0 +1,2 @@ +Improve :exc:`ModuleNotFoundError` error message when a :term:`standard library` +module is missing. From e8e0f411bae1dde634fb152a81e1db1ad1cd2eaa Mon Sep 17 00:00:00 2001 From: Shamil Date: Tue, 21 Oct 2025 10:54:34 +0300 Subject: [PATCH 235/373] gh-140398: fix memory leaks in `readline` module when `PySys_Audit` fails (#140400) --- .../2025-10-21-09-20-03.gh-issue-140398.SoABwJ.rst | 4 ++++ Modules/readline.c | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-21-09-20-03.gh-issue-140398.SoABwJ.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-21-09-20-03.gh-issue-140398.SoABwJ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-21-09-20-03.gh-issue-140398.SoABwJ.rst new file mode 100644 index 00000000000..481dac7f26d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-21-09-20-03.gh-issue-140398.SoABwJ.rst @@ -0,0 +1,4 @@ +Fix memory leaks in :mod:`readline` functions +:func:`~readline.read_init_file`, :func:`~readline.read_history_file`, +:func:`~readline.write_history_file`, and +:func:`~readline.append_history_file` when :c:func:`PySys_Audit` fails. diff --git a/Modules/readline.c b/Modules/readline.c index 630a6879990..e89755b0cb4 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -255,6 +255,7 @@ readline_read_init_file_impl(PyObject *module, PyObject *filename_obj) if (!PyUnicode_FSConverter(filename_obj, &filename_bytes)) return NULL; if (PySys_Audit("open", "OCi", filename_obj, 'r', 0) < 0) { + Py_DECREF(filename_bytes); return NULL; } errno = rl_read_init_file(PyBytes_AS_STRING(filename_bytes)); @@ -298,6 +299,7 @@ readline_read_history_file_impl(PyObject *module, PyObject *filename_obj) if (!PyUnicode_FSConverter(filename_obj, &filename_bytes)) return NULL; if (PySys_Audit("open", "OCi", filename_obj, 'r', 0) < 0) { + Py_DECREF(filename_bytes); return NULL; } errno = read_history(PyBytes_AS_STRING(filename_bytes)); @@ -343,6 +345,7 @@ readline_write_history_file_impl(PyObject *module, PyObject *filename_obj) return NULL; filename = PyBytes_AS_STRING(filename_bytes); if (PySys_Audit("open", "OCi", filename_obj, 'w', 0) < 0) { + Py_DECREF(filename_bytes); return NULL; } } else { @@ -400,6 +403,7 @@ readline_append_history_file_impl(PyObject *module, int nelements, return NULL; filename = PyBytes_AS_STRING(filename_bytes); if (PySys_Audit("open", "OCi", filename_obj, 'a', 0) < 0) { + Py_DECREF(filename_bytes); return NULL; } } else { From 5c41666ec48e643485628ef06e98373b48744c6e Mon Sep 17 00:00:00 2001 From: Cycloctane Date: Tue, 21 Oct 2025 16:52:57 +0800 Subject: [PATCH 236/373] gh-133951: Add venv changes to documentation and whatsnew (GH-139704) --- Doc/library/venv.rst | 5 +++++ Doc/whatsnew/3.15.rst | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst index b0eb8ee18fa..4c000a92e87 100644 --- a/Doc/library/venv.rst +++ b/Doc/library/venv.rst @@ -407,6 +407,8 @@ creation according to their needs, the :class:`EnvBuilder` class. * ``lib_path`` - The purelib path for the virtual environment. + * ``platlib_path`` - The platlib path for the virtual environment. + * ``bin_path`` - The script path for the virtual environment. * ``bin_name`` - The name of the script path relative to the virtual @@ -431,6 +433,9 @@ creation according to their needs, the :class:`EnvBuilder` class. The attribute ``lib_path`` was added to the context, and the context object was documented. + .. versionchanged:: 3.15 + The attribute ``platlib_path`` was added to the context. + .. method:: create_configuration(context) Creates the ``pyvenv.cfg`` configuration file in the environment. diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index d406b263f17..8503a4c7f97 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -601,6 +601,17 @@ unittest (Contributed by Garry Cairns in :gh:`134567`.) +venv +---- + +* On POSIX platforms, platlib directories will be created if needed when + creating virtual environments, instead of using ``lib64 -> lib`` symlink. + This means purelib and platlib of virtual environments no longer share the + same ``lib`` directory on platforms where :data:`sys.platlibdir` is not + equal to ``lib``. + (Contributed by Rui Xi in :gh:`133951`.) + + xml.parsers.expat ----------------- From fe4b60208e4edfdd781eac6b706b29a60be2bb07 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 21 Oct 2025 12:24:49 +0200 Subject: [PATCH 237/373] gh-83714: Fix os.statx() tests on tmpfs: st_birthtime can be None (#140407) --- Lib/test/test_os/test_os.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py index 9bb4cb7e526..67599537736 100644 --- a/Lib/test/test_os/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -648,9 +648,10 @@ def check_timestamp_agreement(self, result, names): # Make sure that the st_?time and st_?time_ns fields roughly agree # (they should always agree up to around tens-of-microseconds) for name in names: - floaty = int(getattr(result, name) * 100_000) - nanosecondy = getattr(result, name + "_ns") // 10_000 - self.assertAlmostEqual(floaty, nanosecondy, delta=2, msg=name) + with self.subTest(name=name): + floaty = int(getattr(result, name) * 100_000) + nanosecondy = getattr(result, name + "_ns") // 10_000 + self.assertAlmostEqual(floaty, nanosecondy, delta=2, msg=name) def check_stat_attributes(self, fname): result = os.stat(fname) @@ -741,14 +742,19 @@ def test_stat_result_pickle(self): unpickled = pickle.loads(p) self.assertEqual(result, unpickled) - def check_statx_attributes(self, fname): + def check_statx_attributes(self, filename): maximal_mask = 0 for name in dir(os): if name.startswith('STATX_'): maximal_mask |= getattr(os, name) - result = os.statx(self.fname, maximal_mask) + result = os.statx(filename, maximal_mask) + basic_result = os.stat(filename) time_attributes = ('st_atime', 'st_mtime', 'st_ctime', 'st_birthtime') + # gh-83714: st_birthtime can be None on tmpfs even if STATX_BTIME mask + # is used + time_attributes = [name for name in time_attributes + if getattr(result, name) is not None] self.check_timestamp_agreement(result, time_attributes) # Check that valid attributes match os.stat. @@ -773,7 +779,6 @@ def check_statx_attributes(self, fname): ('st_rdev', 0), ('st_dev', 0), ) - basic_result = os.stat(self.fname) for name, bits in requirements: if result.stx_mask & bits == bits and hasattr(basic_result, name): x = getattr(result, name) From 71db05a12d9953a96f809d84b4d0d452a464e431 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Tue, 21 Oct 2025 08:10:01 -0400 Subject: [PATCH 238/373] gh-140406: Fix memory leak upon `__hash__` returning a non-integer (GH-140411) --- Lib/test/test_builtin.py | 10 ++++++++++ .../2025-10-21-06-51-50.gh-issue-140406.0gJs8M.rst | 2 ++ Objects/typeobject.c | 1 + 3 files changed, 13 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-21-06-51-50.gh-issue-140406.0gJs8M.rst diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 85cfe5c90f4..034cd5f7f9e 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1184,6 +1184,16 @@ def __hash__(self): return self self.assertEqual(hash(Z(42)), hash(42)) + def test_invalid_hash_typeerror(self): + # GH-140406: The returned object from __hash__() would leak if it + # wasn't an integer. + class A: + def __hash__(self): + return 1.0 + + with self.assertRaises(TypeError): + hash(A()) + def test_hex(self): self.assertEqual(hex(16), '0x10') self.assertEqual(hex(-16), '-0x10') diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-21-06-51-50.gh-issue-140406.0gJs8M.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-21-06-51-50.gh-issue-140406.0gJs8M.rst new file mode 100644 index 00000000000..3506ba42581 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-21-06-51-50.gh-issue-140406.0gJs8M.rst @@ -0,0 +1,2 @@ +Fix memory leak when an object's :meth:`~object.__hash__` method returns an +object that isn't an :class:`int`. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 29233c1959c..721d4871244 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -10569,6 +10569,7 @@ slot_tp_hash(PyObject *self) return PyObject_HashNotImplemented(self); } if (!PyLong_Check(res)) { + Py_DECREF(res); PyErr_SetString(PyExc_TypeError, "__hash__ method should return an integer"); return -1; From 4adf08ae463e8b5311c5a7662ab26f8d0d14381d Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Tue, 21 Oct 2025 13:37:06 +0100 Subject: [PATCH 239/373] GH-139436: Remove references to downloadable PDF documentation (#140416) --- Doc/faq/general.rst | 2 +- README.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/faq/general.rst b/Doc/faq/general.rst index 578777d7f23..698f2117ff5 100644 --- a/Doc/faq/general.rst +++ b/Doc/faq/general.rst @@ -186,7 +186,7 @@ How do I get documentation on Python? ------------------------------------- The standard documentation for the current stable version of Python is available -at https://docs.python.org/3/. PDF, plain text, and downloadable HTML versions are +at https://docs.python.org/3/. EPUB, plain text, and downloadable HTML versions are also available at https://docs.python.org/3/download.html. The documentation is written in reStructuredText and processed by `the Sphinx diff --git a/README.rst b/README.rst index c6f4549f21b..a228aafb09c 100644 --- a/README.rst +++ b/README.rst @@ -153,7 +153,7 @@ Documentation updated daily. It can also be downloaded in many formats for faster access. The documentation -is downloadable in HTML, PDF, and reStructuredText formats; the latter version +is downloadable in HTML, EPUB, and reStructuredText formats; the latter version is primarily for documentation authors, translators, and people with special formatting requirements. @@ -232,4 +232,4 @@ This Python distribution contains *no* GNU General Public License (GPL) code, so it may be used in proprietary projects. There are interfaces to some GNU code but these are entirely optional. -All trademarks referenced herein are property of their respective holders. \ No newline at end of file +All trademarks referenced herein are property of their respective holders. From 0c01090ad957de4625f504ce4f29df0a05d09fba Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 21 Oct 2025 15:22:15 +0100 Subject: [PATCH 240/373] GH-139951: Fix major GC performance regression (GH-140262) * Count number of actually tracked objects, instead of trackable objects. This ensures that untracking tuples has the desired effect of reducing GC overhead * Do not track most untrackable tuples during creation. This prevents large numbers of small tuples causing execessive GCs. --- Include/internal/pycore_gc.h | 23 ++++++++-- Lib/test/test_gc.py | 17 ++++---- ...-10-17-18-03-12.gh-issue-139951.IdwM2O.rst | 7 +++ Objects/tupleobject.c | 39 +++++++++++++++-- Python/gc.c | 43 ++++++++++--------- 5 files changed, 93 insertions(+), 36 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-18-03-12.gh-issue-139951.IdwM2O.rst diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index a6519aa0863..fd284d0e4ec 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -205,6 +205,12 @@ static inline void _PyGC_CLEAR_FINALIZED(PyObject *op) { #endif } +extern void _Py_ScheduleGC(PyThreadState *tstate); + +#ifndef Py_GIL_DISABLED +extern void _Py_TriggerGC(struct _gc_runtime_state *gcstate); +#endif + /* Tell the GC to track this object. * @@ -238,14 +244,19 @@ static inline void _PyObject_GC_TRACK( "object is in generation which is garbage collected", filename, lineno, __func__); - PyInterpreterState *interp = _PyInterpreterState_GET(); - PyGC_Head *generation0 = &interp->gc.young.head; + struct _gc_runtime_state *gcstate = &_PyInterpreterState_GET()->gc; + PyGC_Head *generation0 = &gcstate->young.head; PyGC_Head *last = (PyGC_Head*)(generation0->_gc_prev); _PyGCHead_SET_NEXT(last, gc); _PyGCHead_SET_PREV(gc, last); - uintptr_t not_visited = 1 ^ interp->gc.visited_space; + uintptr_t not_visited = 1 ^ gcstate->visited_space; gc->_gc_next = ((uintptr_t)generation0) | not_visited; generation0->_gc_prev = (uintptr_t)gc; + gcstate->young.count++; /* number of tracked GC objects */ + gcstate->heap_size++; + if (gcstate->young.count > gcstate->young.threshold) { + _Py_TriggerGC(gcstate); + } #endif } @@ -280,6 +291,11 @@ static inline void _PyObject_GC_UNTRACK( _PyGCHead_SET_PREV(next, prev); gc->_gc_next = 0; gc->_gc_prev &= _PyGC_PREV_MASK_FINALIZED; + struct _gc_runtime_state *gcstate = &_PyInterpreterState_GET()->gc; + if (gcstate->young.count > 0) { + gcstate->young.count--; + } + gcstate->heap_size--; #endif } @@ -343,7 +359,6 @@ extern PyObject *_PyGC_GetReferrers(PyInterpreterState *interp, PyObject *objs); // Functions to clear types free lists extern void _PyGC_ClearAllFreeLists(PyInterpreterState *interp); -extern void _Py_ScheduleGC(PyThreadState *tstate); extern void _Py_RunGC(PyThreadState *tstate); union _PyStackRef; diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index 08055bb8e6b..10c3a622107 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -1473,10 +1473,11 @@ def callback(ignored): # The free-threaded build doesn't have multiple generations, so # just trigger a GC manually. gc.collect() + assert not detector.gc_happened while not detector.gc_happened: i += 1 - if i > 10000: - self.fail("gc didn't happen after 10000 iterations") + if i > 100000: + self.fail("gc didn't happen after 100000 iterations") self.assertEqual(len(ouch), 0) junk.append([]) # this will eventually trigger gc @@ -1548,8 +1549,8 @@ def __del__(self): gc.collect() while not detector.gc_happened: i += 1 - if i > 10000: - self.fail("gc didn't happen after 10000 iterations") + if i > 50000: + self.fail("gc didn't happen after 50000 iterations") self.assertEqual(len(ouch), 0) junk.append([]) # this will eventually trigger gc @@ -1566,8 +1567,8 @@ def test_indirect_calls_with_gc_disabled(self): detector = GC_Detector() while not detector.gc_happened: i += 1 - if i > 10000: - self.fail("gc didn't happen after 10000 iterations") + if i > 100000: + self.fail("gc didn't happen after 100000 iterations") junk.append([]) # this will eventually trigger gc try: @@ -1577,11 +1578,11 @@ def test_indirect_calls_with_gc_disabled(self): detector = GC_Detector() while not detector.gc_happened: i += 1 - if i > 10000: + if i > 100000: break junk.append([]) # this may eventually trigger gc (if it is enabled) - self.assertEqual(i, 10001) + self.assertEqual(i, 100001) finally: gc.enable() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-18-03-12.gh-issue-139951.IdwM2O.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-18-03-12.gh-issue-139951.IdwM2O.rst new file mode 100644 index 00000000000..e03996188a7 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-18-03-12.gh-issue-139951.IdwM2O.rst @@ -0,0 +1,7 @@ +Fixes a regression in GC performance for a growing heap composed mostly of +small tuples. + +* Counts number of actually tracked objects, instead of trackable objects. + This ensures that untracking tuples has the desired effect of reducing GC overhead. +* Does not track most untrackable tuples during creation. + This prevents large numbers of small tuples causing excessive GCs. diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index cd90b06d499..169ac69701d 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -156,6 +156,18 @@ _PyTuple_MaybeUntrack(PyObject *op) _PyObject_GC_UNTRACK(op); } +/* Fast, but conservative check if an object maybe tracked + May return true for an object that is not tracked, + Will always return true for an object that is tracked. + This is a temporary workaround until _PyObject_GC_IS_TRACKED + becomes fast and safe to call on non-GC objects. +*/ +static bool +maybe_tracked(PyObject *ob) +{ + return _PyType_IS_GC(Py_TYPE(ob)); +} + PyObject * PyTuple_Pack(Py_ssize_t n, ...) { @@ -163,6 +175,7 @@ PyTuple_Pack(Py_ssize_t n, ...) PyObject *o; PyObject **items; va_list vargs; + bool track = false; if (n == 0) { return tuple_get_empty(); @@ -177,10 +190,15 @@ PyTuple_Pack(Py_ssize_t n, ...) items = result->ob_item; for (i = 0; i < n; i++) { o = va_arg(vargs, PyObject *); + if (!track && maybe_tracked(o)) { + track = true; + } items[i] = Py_NewRef(o); } va_end(vargs); - _PyObject_GC_TRACK(result); + if (track) { + _PyObject_GC_TRACK(result); + } return (PyObject *)result; } @@ -377,11 +395,17 @@ PyTuple_FromArray(PyObject *const *src, Py_ssize_t n) return NULL; } PyObject **dst = tuple->ob_item; + bool track = false; for (Py_ssize_t i = 0; i < n; i++) { PyObject *item = src[i]; + if (!track && maybe_tracked(item)) { + track = true; + } dst[i] = Py_NewRef(item); } - _PyObject_GC_TRACK(tuple); + if (track) { + _PyObject_GC_TRACK(tuple); + } return (PyObject *)tuple; } @@ -396,10 +420,17 @@ _PyTuple_FromStackRefStealOnSuccess(const _PyStackRef *src, Py_ssize_t n) return NULL; } PyObject **dst = tuple->ob_item; + bool track = false; for (Py_ssize_t i = 0; i < n; i++) { - dst[i] = PyStackRef_AsPyObjectSteal(src[i]); + PyObject *item = PyStackRef_AsPyObjectSteal(src[i]); + if (!track && maybe_tracked(item)) { + track = true; + } + dst[i] = item; + } + if (track) { + _PyObject_GC_TRACK(tuple); } - _PyObject_GC_TRACK(tuple); return (PyObject *)tuple; } diff --git a/Python/gc.c b/Python/gc.c index fd724301c76..a1f3d86d910 100644 --- a/Python/gc.c +++ b/Python/gc.c @@ -1639,7 +1639,7 @@ assess_work_to_do(GCState *gcstate) scale_factor = 2; } intptr_t new_objects = gcstate->young.count; - intptr_t max_heap_fraction = new_objects*3/2; + intptr_t max_heap_fraction = new_objects*2; intptr_t heap_fraction = gcstate->heap_size / SCAN_RATE_DIVISOR / scale_factor; if (heap_fraction > max_heap_fraction) { heap_fraction = max_heap_fraction; @@ -1654,6 +1654,9 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats) GC_STAT_ADD(1, collections, 1); GCState *gcstate = &tstate->interp->gc; gcstate->work_to_do += assess_work_to_do(gcstate); + if (gcstate->work_to_do < 0) { + return; + } untrack_tuples(&gcstate->young.head); if (gcstate->phase == GC_PHASE_MARK) { Py_ssize_t objects_marked = mark_at_start(tstate); @@ -1696,7 +1699,6 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats) gc_collect_region(tstate, &increment, &survivors, stats); gc_list_merge(&survivors, visited); assert(gc_list_is_empty(&increment)); - gcstate->work_to_do += gcstate->heap_size / SCAN_RATE_DIVISOR / scale_factor; gcstate->work_to_do -= increment_size; add_stats(gcstate, 1, stats); @@ -2299,21 +2301,11 @@ _Py_ScheduleGC(PyThreadState *tstate) } void -_PyObject_GC_Link(PyObject *op) +_Py_TriggerGC(struct _gc_runtime_state *gcstate) { - PyGC_Head *gc = AS_GC(op); - // gc must be correctly aligned - _PyObject_ASSERT(op, ((uintptr_t)gc & (sizeof(uintptr_t)-1)) == 0); - PyThreadState *tstate = _PyThreadState_GET(); - GCState *gcstate = &tstate->interp->gc; - gc->_gc_next = 0; - gc->_gc_prev = 0; - gcstate->young.count++; /* number of allocated GC objects */ - gcstate->heap_size++; - if (gcstate->young.count > gcstate->young.threshold && - gcstate->enabled && - gcstate->young.threshold && + if (gcstate->enabled && + gcstate->young.threshold != 0 && !_Py_atomic_load_int_relaxed(&gcstate->collecting) && !_PyErr_Occurred(tstate)) { @@ -2321,6 +2313,17 @@ _PyObject_GC_Link(PyObject *op) } } +void +_PyObject_GC_Link(PyObject *op) +{ + PyGC_Head *gc = AS_GC(op); + // gc must be correctly aligned + _PyObject_ASSERT(op, ((uintptr_t)gc & (sizeof(uintptr_t)-1)) == 0); + gc->_gc_next = 0; + gc->_gc_prev = 0; + +} + void _Py_RunGC(PyThreadState *tstate) { @@ -2427,6 +2430,11 @@ PyObject_GC_Del(void *op) PyGC_Head *g = AS_GC(op); if (_PyObject_GC_IS_TRACKED(op)) { gc_list_remove(g); + GCState *gcstate = get_gc_state(); + if (gcstate->young.count > 0) { + gcstate->young.count--; + } + gcstate->heap_size--; #ifdef Py_DEBUG PyObject *exc = PyErr_GetRaisedException(); if (PyErr_WarnExplicitFormat(PyExc_ResourceWarning, "gc", 0, @@ -2440,11 +2448,6 @@ PyObject_GC_Del(void *op) PyErr_SetRaisedException(exc); #endif } - GCState *gcstate = get_gc_state(); - if (gcstate->young.count > 0) { - gcstate->young.count--; - } - gcstate->heap_size--; PyObject_Free(((char *)op)-presize); } From c788bfb80e0e8b312d31db810de4c090a1059a71 Mon Sep 17 00:00:00 2001 From: Jeffrey Bosboom Date: Tue, 21 Oct 2025 08:54:00 -0700 Subject: [PATCH 241/373] gh-140239: Check for statx on Android (#140395) Android has Linux's statx, but MACHDEP is "android" on Android, so configure doesn't check for statx on Android. Base the check for statx on ac_sys_system instead, which is "Linux-android" on Android, "Linux" on other Linux distributions, and "AIX" on AIX (which has an incompatible function named statx). --- .../2025-10-17-11-33-45.gh-issue-140239._k-GgW.rst | 2 +- configure | 13 +++++++++---- configure.ac | 9 +++++---- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Misc/NEWS.d/next/Build/2025-10-17-11-33-45.gh-issue-140239._k-GgW.rst b/Misc/NEWS.d/next/Build/2025-10-17-11-33-45.gh-issue-140239._k-GgW.rst index f196ab0964d..713f022c994 100644 --- a/Misc/NEWS.d/next/Build/2025-10-17-11-33-45.gh-issue-140239._k-GgW.rst +++ b/Misc/NEWS.d/next/Build/2025-10-17-11-33-45.gh-issue-140239._k-GgW.rst @@ -1 +1 @@ -Check ``statx`` availability only in Linux platforms +Check ``statx`` availability only on Linux (including Android). diff --git a/configure b/configure index 95e231f406a..f95355e2f57 100755 --- a/configure +++ b/configure @@ -20392,16 +20392,21 @@ then : fi -# Check statx availability in Linux -if test "$MACHDEP" = "linux"; then - ac_fn_c_check_func "$LINENO" "statx" "ac_cv_func_statx" +# os.statx uses Linux's statx function. AIX also has a function named statx, +# but it's unrelated. Check only on Linux (including Android). +case $ac_sys_system in #( + Linux*) : + ac_fn_c_check_func "$LINENO" "statx" "ac_cv_func_statx" if test "x$ac_cv_func_statx" = xyes then : printf "%s\n" "#define HAVE_STATX 1" >>confdefs.h fi -fi + ;; #( + *) : + ;; +esac # Force lchmod off for Linux. Linux disallows changing the mode of symbolic # links. Some libc implementations have a stub lchmod implementation that always diff --git a/configure.ac b/configure.ac index 65c37579633..4d0e5a1ca1d 100644 --- a/configure.ac +++ b/configure.ac @@ -5258,10 +5258,11 @@ AC_CHECK_FUNCS([ \ wait wait3 wait4 waitid waitpid wcscoll wcsftime wcsxfrm wmemcmp writev \ ]) -# Check statx availability in Linux -if test "$MACHDEP" = "linux"; then - AC_CHECK_FUNCS([statx]) -fi +# os.statx uses Linux's statx function. AIX also has a function named statx, +# but it's unrelated. Check only on Linux (including Android). +AS_CASE([$ac_sys_system], + [Linux*], [AC_CHECK_FUNCS([statx])] +) # Force lchmod off for Linux. Linux disallows changing the mode of symbolic # links. Some libc implementations have a stub lchmod implementation that always From 95c257e2e691456140e79bd98d1674cbd289eb38 Mon Sep 17 00:00:00 2001 From: David Ellis Date: Tue, 21 Oct 2025 16:57:43 +0100 Subject: [PATCH 242/373] gh-138764: annotationlib: Make `call_annotate_function` fallback to using `VALUE` annotations if both the requested format and `VALUE_WITH_FAKE_GLOBALS` are not implemented (#138803) --- Doc/library/annotationlib.rst | 15 ++ Lib/annotationlib.py | 15 ++ Lib/test/test_annotationlib.py | 176 ++++++++++++++++++ ...-09-12-09-34-37.gh-issue-138764.mokHoY.rst | 3 + 4 files changed, 209 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-09-12-09-34-37.gh-issue-138764.mokHoY.rst diff --git a/Doc/library/annotationlib.rst b/Doc/library/annotationlib.rst index d6f5055955e..40f2a6dc304 100644 --- a/Doc/library/annotationlib.rst +++ b/Doc/library/annotationlib.rst @@ -340,14 +340,29 @@ Functions * VALUE: :attr:`!object.__annotations__` is tried first; if that does not exist, the :attr:`!object.__annotate__` function is called if it exists. + * FORWARDREF: If :attr:`!object.__annotations__` exists and can be evaluated successfully, it is used; otherwise, the :attr:`!object.__annotate__` function is called. If it does not exist either, :attr:`!object.__annotations__` is tried again and any error from accessing it is re-raised. + + * When calling :attr:`!object.__annotate__` it is first called with :attr:`~Format.FORWARDREF`. + If this is not implemented, it will then check if :attr:`~Format.VALUE_WITH_FAKE_GLOBALS` + is supported and use that in the fake globals environment. + If neither of these formats are supported, it will fall back to using :attr:`~Format.VALUE`. + If :attr:`~Format.VALUE` fails, the error from this call will be raised. + * STRING: If :attr:`!object.__annotate__` exists, it is called first; otherwise, :attr:`!object.__annotations__` is used and stringified using :func:`annotations_to_string`. + * When calling :attr:`!object.__annotate__` it is first called with :attr:`~Format.STRING`. + If this is not implemented, it will then check if :attr:`~Format.VALUE_WITH_FAKE_GLOBALS` + is supported and use that in the fake globals environment. + If neither of these formats are supported, it will fall back to using :attr:`~Format.VALUE` + with the result converted using :func:`annotations_to_string`. + If :attr:`~Format.VALUE` fails, the error from this call will be raised. + Returns a dict. :func:`!get_annotations` returns a new dict every time it's called; calling it twice on the same object will return two different but equivalent dicts. diff --git a/Lib/annotationlib.py b/Lib/annotationlib.py index 544e069626d..81886a0467d 100644 --- a/Lib/annotationlib.py +++ b/Lib/annotationlib.py @@ -695,6 +695,18 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): # possibly constants if the annotate function uses them directly). We then # convert each of those into a string to get an approximation of the # original source. + + # Attempt to call with VALUE_WITH_FAKE_GLOBALS to check if it is implemented + # See: https://github.com/python/cpython/issues/138764 + # Only fail on NotImplementedError + try: + annotate(Format.VALUE_WITH_FAKE_GLOBALS) + except NotImplementedError: + # Both STRING and VALUE_WITH_FAKE_GLOBALS are not implemented: fallback to VALUE + return annotations_to_string(annotate(Format.VALUE)) + except Exception: + pass + globals = _StringifierDict({}, format=format) is_class = isinstance(owner, type) closure = _build_closure( @@ -753,6 +765,9 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): ) try: result = func(Format.VALUE_WITH_FAKE_GLOBALS) + except NotImplementedError: + # FORWARDREF and VALUE_WITH_FAKE_GLOBALS not supported, fall back to VALUE + return annotate(Format.VALUE) except Exception: pass else: diff --git a/Lib/test/test_annotationlib.py b/Lib/test/test_annotationlib.py index 2c5bf2b3417..8da4ff096e7 100644 --- a/Lib/test/test_annotationlib.py +++ b/Lib/test/test_annotationlib.py @@ -1194,6 +1194,25 @@ class RaisesAttributeError: }, ) + def test_raises_error_from_value(self): + # test that if VALUE is the only supported format, but raises an error + # that error is propagated from get_annotations + class DemoException(Exception): ... + + def annotate(format, /): + if format == Format.VALUE: + raise DemoException() + else: + raise NotImplementedError(format) + + def f(): ... + + f.__annotate__ = annotate + + for fmt in [Format.VALUE, Format.FORWARDREF, Format.STRING]: + with self.assertRaises(DemoException): + get_annotations(f, format=fmt) + class TestCallEvaluateFunction(unittest.TestCase): def test_evaluation(self): @@ -1214,6 +1233,163 @@ def evaluate(format, exc=NotImplementedError): ) +class TestCallAnnotateFunction(unittest.TestCase): + # Tests for user defined annotate functions. + + # Format and NotImplementedError are provided as arguments so they exist in + # the fake globals namespace. + # This avoids non-matching conditions passing by being converted to stringifiers. + # See: https://github.com/python/cpython/issues/138764 + + def test_user_annotate_value(self): + def annotate(format, /): + if format == Format.VALUE: + return {"x": str} + else: + raise NotImplementedError(format) + + annotations = annotationlib.call_annotate_function( + annotate, + Format.VALUE, + ) + + self.assertEqual(annotations, {"x": str}) + + def test_user_annotate_forwardref_supported(self): + # If Format.FORWARDREF is supported prefer it over Format.VALUE + def annotate(format, /, __Format=Format, __NotImplementedError=NotImplementedError): + if format == __Format.VALUE: + return {'x': str} + elif format == __Format.VALUE_WITH_FAKE_GLOBALS: + return {'x': int} + elif format == __Format.FORWARDREF: + return {'x': float} + else: + raise __NotImplementedError(format) + + annotations = annotationlib.call_annotate_function( + annotate, + Format.FORWARDREF + ) + + self.assertEqual(annotations, {"x": float}) + + def test_user_annotate_forwardref_fakeglobals(self): + # If Format.FORWARDREF is not supported, use Format.VALUE_WITH_FAKE_GLOBALS + # before falling back to Format.VALUE + def annotate(format, /, __Format=Format, __NotImplementedError=NotImplementedError): + if format == __Format.VALUE: + return {'x': str} + elif format == __Format.VALUE_WITH_FAKE_GLOBALS: + return {'x': int} + else: + raise __NotImplementedError(format) + + annotations = annotationlib.call_annotate_function( + annotate, + Format.FORWARDREF + ) + + self.assertEqual(annotations, {"x": int}) + + def test_user_annotate_forwardref_value_fallback(self): + # If Format.FORWARDREF and Format.VALUE_WITH_FAKE_GLOBALS are not supported + # use Format.VALUE + def annotate(format, /, __Format=Format, __NotImplementedError=NotImplementedError): + if format == __Format.VALUE: + return {"x": str} + else: + raise __NotImplementedError(format) + + annotations = annotationlib.call_annotate_function( + annotate, + Format.FORWARDREF, + ) + + self.assertEqual(annotations, {"x": str}) + + def test_user_annotate_string_supported(self): + # If Format.STRING is supported prefer it over Format.VALUE + def annotate(format, /, __Format=Format, __NotImplementedError=NotImplementedError): + if format == __Format.VALUE: + return {'x': str} + elif format == __Format.VALUE_WITH_FAKE_GLOBALS: + return {'x': int} + elif format == __Format.STRING: + return {'x': "float"} + else: + raise __NotImplementedError(format) + + annotations = annotationlib.call_annotate_function( + annotate, + Format.STRING, + ) + + self.assertEqual(annotations, {"x": "float"}) + + def test_user_annotate_string_fakeglobals(self): + # If Format.STRING is not supported but Format.VALUE_WITH_FAKE_GLOBALS is + # prefer that over Format.VALUE + def annotate(format, /, __Format=Format, __NotImplementedError=NotImplementedError): + if format == __Format.VALUE: + return {'x': str} + elif format == __Format.VALUE_WITH_FAKE_GLOBALS: + return {'x': int} + else: + raise __NotImplementedError(format) + + annotations = annotationlib.call_annotate_function( + annotate, + Format.STRING, + ) + + self.assertEqual(annotations, {"x": "int"}) + + def test_user_annotate_string_value_fallback(self): + # If Format.STRING and Format.VALUE_WITH_FAKE_GLOBALS are not + # supported fall back to Format.VALUE and convert to strings + def annotate(format, /, __Format=Format, __NotImplementedError=NotImplementedError): + if format == __Format.VALUE: + return {"x": str} + else: + raise __NotImplementedError(format) + + annotations = annotationlib.call_annotate_function( + annotate, + Format.STRING, + ) + + self.assertEqual(annotations, {"x": "str"}) + + def test_condition_not_stringified(self): + # Make sure the first condition isn't evaluated as True by being converted + # to a _Stringifier + def annotate(format, /): + if format == Format.FORWARDREF: + return {"x": str} + else: + raise NotImplementedError(format) + + with self.assertRaises(NotImplementedError): + annotationlib.call_annotate_function(annotate, Format.STRING) + + def test_error_from_value_raised(self): + # Test that the error from format.VALUE is raised + # if all formats fail + + class DemoException(Exception): ... + + def annotate(format, /): + if format == Format.VALUE: + raise DemoException() + else: + raise NotImplementedError(format) + + for fmt in [Format.VALUE, Format.FORWARDREF, Format.STRING]: + with self.assertRaises(DemoException): + annotationlib.call_annotate_function(annotate, format=fmt) + + class MetaclassTests(unittest.TestCase): def test_annotated_meta(self): class Meta(type): diff --git a/Misc/NEWS.d/next/Library/2025-09-12-09-34-37.gh-issue-138764.mokHoY.rst b/Misc/NEWS.d/next/Library/2025-09-12-09-34-37.gh-issue-138764.mokHoY.rst new file mode 100644 index 00000000000..85ebef8ff11 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-12-09-34-37.gh-issue-138764.mokHoY.rst @@ -0,0 +1,3 @@ +Prevent :func:`annotationlib.call_annotate_function` from calling ``__annotate__`` functions that don't support ``VALUE_WITH_FAKE_GLOBALS`` in a fake globals namespace with empty globals. + +Make ``FORWARDREF`` and ``STRING`` annotations fall back to using ``VALUE`` annotations in the case that neither their own format, nor ``VALUE_WITH_FAKE_GLOBALS`` are supported. From b4d230736709235f90fe635f3322973cc9fadd21 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Tue, 21 Oct 2025 17:02:43 +0100 Subject: [PATCH 243/373] Use ``sphinxext-opengraph`` v0.13.0 (GH-140425) --- Doc/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/requirements.txt b/Doc/requirements.txt index 7b7286429a1..d5f7b473c3a 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -11,7 +11,7 @@ sphinx~=8.2.0 blurb -sphinxext-opengraph~=0.12.0 +sphinxext-opengraph~=0.13.0 sphinx-notfound-page~=1.0.0 # The theme used by the documentation is stored separately, so we need From 60df1d7e0c8eef4bb7b9c41e2b65957ab4a4cd34 Mon Sep 17 00:00:00 2001 From: Jeffrey Bosboom Date: Tue, 21 Oct 2025 09:13:14 -0700 Subject: [PATCH 244/373] gh-83714: Check for struct statx members in configure (#140402) Some systems have the definitions of the mask bits without having the corresponding members in struct statx. Add configure checks for members added after Linux 4.11 (when statx itself was added). --- Doc/library/os.rst | 6 ++--- Modules/posixmodule.c | 20 ++++++++--------- configure | 51 +++++++++++++++++++++++++++++++++++++++++++ configure.ac | 11 ++++++++++ pyconfig.h.in | 17 +++++++++++++++ 5 files changed, 92 insertions(+), 13 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index ef08e1bfa51..50f6886c89d 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3566,9 +3566,9 @@ features: STATX_WRITE_ATOMIC STATX_DIO_READ_ALIGN - Bitflags for use in the *mask* parameter to :func:`os.statx`. Flags - including and after :const:`!STATX_MNT_ID` are only available when their - corresponding members in :class:`statx_result` are available. + Bitflags for use in the *mask* parameter to :func:`os.statx`. Some of these + flags may be available even when their corresponding members in + :class:`statx_result` are not available. .. availability:: Linux >= 4.11 with glibc >= 2.28. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 1cfb068680c..d0b9eed8c82 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -3364,15 +3364,15 @@ static PyMemberDef pystatx_result_members[] = { STATX_GET_UINT(st_uid, stx_uid, STATX_UID) STATX_GET_UINT(st_gid, stx_gid, STATX_GID) STATX_GET_UINT(st_nlink, stx_nlink, STATX_NLINK) -#ifdef STATX_DIOALIGN +#ifdef HAVE_STRUCT_STATX_STX_DIO_MEM_ALIGN STATX_GET_UINT(stx_dio_mem_align, stx_dio_mem_align, STATX_DIOALIGN) STATX_GET_UINT(stx_dio_offset_align, stx_dio_offset_align, STATX_DIOALIGN) #endif -#ifdef STATX_DIO_READ_ALIGN +#ifdef HAVE_STRUCT_STATX_STX_DIO_READ_OFFSET_ALIGN STATX_GET_UINT(stx_dio_read_offset_align, stx_dio_read_offset_align, STATX_DIO_READ_ALIGN) #endif -#ifdef STATX_WRITE_ATOMIC +#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MIN STATX_GET_UINT(stx_atomic_write_unit_min, stx_atomic_write_unit_min, STATX_WRITE_ATOMIC) STATX_GET_UINT(stx_atomic_write_unit_max, stx_atomic_write_unit_max, @@ -3401,10 +3401,10 @@ STATX_GET_UINT(stx_atomic_write_unit_max_opt, stx_atomic_write_unit_max_opt, STATX_GET_ULONGLONG(st_blocks, stx_blocks, STATX_BLOCKS) STATX_GET_ULONGLONG(st_ino, stx_ino, STATX_INO) STATX_GET_ULONGLONG(st_size, stx_size, STATX_SIZE) -#ifdef STATX_MNT_ID +#ifdef HAVE_STRUCT_STATX_STX_MNT_ID STATX_GET_ULONGLONG(stx_mnt_id, stx_mnt_id, STATX_MNT_ID) #endif -#ifdef STATX_SUBVOL +#ifdef HAVE_STRUCT_STATX_STX_SUBVOL STATX_GET_ULONGLONG(stx_subvol, stx_subvol, STATX_SUBVOL) #endif @@ -3463,17 +3463,17 @@ static PyGetSetDef pystatx_result_getset[] = { G(st_ctime_ns, "time of last change in nanoseconds"), G(st_mtime, "time of last modification"), G(st_mtime_ns, "time of last modification in nanoseconds"), -#ifdef STATX_MNT_ID +#ifdef HAVE_STRUCT_STATX_STX_MNT_ID G(stx_mnt_id, "mount ID"), #endif -#ifdef STATX_DIOALIGN +#ifdef HAVE_STRUCT_STATX_STX_DIO_MEM_ALIGN G(stx_dio_mem_align, "direct I/O memory buffer alignment"), G(stx_dio_offset_align, "direct I/O file offset alignment"), #endif -#ifdef STATX_SUBVOL +#ifdef HAVE_STRUCT_STATX_STX_SUBVOL G(stx_subvol, "subvolume ID"), #endif -#ifdef STATX_WRITE_ATOMIC +#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MIN G(stx_atomic_write_unit_min, "minimum size for direct I/O with torn-write protection"), G(stx_atomic_write_unit_max, @@ -3481,7 +3481,7 @@ static PyGetSetDef pystatx_result_getset[] = { G(stx_atomic_write_segments_max, "maximum iovecs for direct I/O with torn-write protection"), #endif -#ifdef STATX_DIO_READ_ALIGN +#ifdef HAVE_STRUCT_STATX_STX_DIO_READ_OFFSET_ALIGN G(stx_dio_read_offset_align, "direct I/O file offset alignment for reads"), #endif #ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT diff --git a/configure b/configure index f95355e2f57..31eb0baa924 100755 --- a/configure +++ b/configure @@ -25144,6 +25144,57 @@ fi if test "$ac_cv_func_statx" = yes; then + # Some systems have the definitions of the mask bits without having the + # corresponding members in struct statx. Check for members added after Linux + # 4.11 (when statx itself was added). + ac_fn_c_check_member "$LINENO" "struct statx" "stx_mnt_id" "ac_cv_member_struct_statx_stx_mnt_id" "$ac_includes_default" +if test "x$ac_cv_member_struct_statx_stx_mnt_id" = xyes +then : + +printf "%s\n" "#define HAVE_STRUCT_STATX_STX_MNT_ID 1" >>confdefs.h + + +fi + + ac_fn_c_check_member "$LINENO" "struct statx" "stx_dio_mem_align" "ac_cv_member_struct_statx_stx_dio_mem_align" "$ac_includes_default" +if test "x$ac_cv_member_struct_statx_stx_dio_mem_align" = xyes +then : + +printf "%s\n" "#define HAVE_STRUCT_STATX_STX_DIO_MEM_ALIGN 1" >>confdefs.h + + +fi + + # stx_dio_offset_align was added together with stx_dio_mem_align + ac_fn_c_check_member "$LINENO" "struct statx" "stx_subvol" "ac_cv_member_struct_statx_stx_subvol" "$ac_includes_default" +if test "x$ac_cv_member_struct_statx_stx_subvol" = xyes +then : + +printf "%s\n" "#define HAVE_STRUCT_STATX_STX_SUBVOL 1" >>confdefs.h + + +fi + + ac_fn_c_check_member "$LINENO" "struct statx" "stx_atomic_write_unit_min" "ac_cv_member_struct_statx_stx_atomic_write_unit_min" "$ac_includes_default" +if test "x$ac_cv_member_struct_statx_stx_atomic_write_unit_min" = xyes +then : + +printf "%s\n" "#define HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MIN 1" >>confdefs.h + + +fi + + # stx_atomic_write_unit_max and stx_atomic_write_segments_max were added + # together with stx_atomic_write_unit_min + ac_fn_c_check_member "$LINENO" "struct statx" "stx_dio_read_offset_align" "ac_cv_member_struct_statx_stx_dio_read_offset_align" "$ac_includes_default" +if test "x$ac_cv_member_struct_statx_stx_dio_read_offset_align" = xyes +then : + +printf "%s\n" "#define HAVE_STRUCT_STATX_STX_DIO_READ_OFFSET_ALIGN 1" >>confdefs.h + + +fi + # stx_atomic_write_unit_max_opt was added in Linux 6.16, but is controlled by # the STATX_WRITE_ATOMIC mask bit added in Linux 6.11, so having the mask bit # doesn't imply having the member. diff --git a/configure.ac b/configure.ac index 4d0e5a1ca1d..6f29362f467 100644 --- a/configure.ac +++ b/configure.ac @@ -5827,6 +5827,17 @@ AC_CHECK_MEMBERS([struct passwd.pw_gecos, struct passwd.pw_passwd], [], [], [[ AC_CHECK_MEMBERS([siginfo_t.si_band], [], [], [[@%:@include ]]) if test "$ac_cv_func_statx" = yes; then + # Some systems have the definitions of the mask bits without having the + # corresponding members in struct statx. Check for members added after Linux + # 4.11 (when statx itself was added). + AC_CHECK_MEMBERS([struct statx.stx_mnt_id]) + AC_CHECK_MEMBERS([struct statx.stx_dio_mem_align]) + # stx_dio_offset_align was added together with stx_dio_mem_align + AC_CHECK_MEMBERS([struct statx.stx_subvol]) + AC_CHECK_MEMBERS([struct statx.stx_atomic_write_unit_min]) + # stx_atomic_write_unit_max and stx_atomic_write_segments_max were added + # together with stx_atomic_write_unit_min + AC_CHECK_MEMBERS([struct statx.stx_dio_read_offset_align]) # stx_atomic_write_unit_max_opt was added in Linux 6.16, but is controlled by # the STATX_WRITE_ATOMIC mask bit added in Linux 6.11, so having the mask bit # doesn't imply having the member. diff --git a/pyconfig.h.in b/pyconfig.h.in index 092f96e7152..fb12079bafa 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1334,6 +1334,23 @@ statx'. */ #undef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT +/* Define to 1 if 'stx_atomic_write_unit_min' is a member of 'struct statx'. + */ +#undef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MIN + +/* Define to 1 if 'stx_dio_mem_align' is a member of 'struct statx'. */ +#undef HAVE_STRUCT_STATX_STX_DIO_MEM_ALIGN + +/* Define to 1 if 'stx_dio_read_offset_align' is a member of 'struct statx'. + */ +#undef HAVE_STRUCT_STATX_STX_DIO_READ_OFFSET_ALIGN + +/* Define to 1 if 'stx_mnt_id' is a member of 'struct statx'. */ +#undef HAVE_STRUCT_STATX_STX_MNT_ID + +/* Define to 1 if 'stx_subvol' is a member of 'struct statx'. */ +#undef HAVE_STRUCT_STATX_STX_SUBVOL + /* Define to 1 if 'st_birthtime' is a member of 'struct stat'. */ #undef HAVE_STRUCT_STAT_ST_BIRTHTIME From 02c1abfc54954ec89a0c728823efde5ce7918a0f Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Tue, 21 Oct 2025 18:33:30 +0100 Subject: [PATCH 245/373] gh-69528: Distinguish between file modes "wb+" and "rb+" (GH-137834) Co-authored-by: Xiang Zhang --- Lib/_pyio.py | 7 ++++++- Lib/test/test_gzip.py | 2 +- Lib/test/test_io/test_fileio.py | 4 ++-- Lib/test/test_io/test_general.py | 4 ++-- Lib/test/test_tempfile.py | 2 +- ...2025-08-15-20-35-30.gh-issue-69528.qc-Eh_.rst | 2 ++ Modules/_io/fileio.c | 16 +++++++++++++--- 7 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-08-15-20-35-30.gh-issue-69528.qc-Eh_.rst diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 5db8ce9244b..9ae72743919 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -1498,6 +1498,7 @@ class FileIO(RawIOBase): _writable = False _appending = False _seekable = None + _truncate = False _closefd = True def __init__(self, file, mode='r', closefd=True, opener=None): @@ -1553,6 +1554,7 @@ def __init__(self, file, mode='r', closefd=True, opener=None): flags = 0 elif 'w' in mode: self._writable = True + self._truncate = True flags = os.O_CREAT | os.O_TRUNC elif 'a' in mode: self._writable = True @@ -1877,7 +1879,10 @@ def mode(self): return 'ab' elif self._readable: if self._writable: - return 'rb+' + if self._truncate: + return 'wb+' + else: + return 'rb+' else: return 'rb' else: diff --git a/Lib/test/test_gzip.py b/Lib/test/test_gzip.py index 9a2e1dd248f..f14a882d386 100644 --- a/Lib/test/test_gzip.py +++ b/Lib/test/test_gzip.py @@ -639,7 +639,7 @@ def test_fileobj_mode(self): with open(self.filename, mode) as f: with gzip.GzipFile(fileobj=f) as g: self.assertEqual(g.mode, gzip.READ) - for mode in "wb", "ab", "xb": + for mode in "wb", "ab", "xb", "wb+", "ab+", "xb+": if "x" in mode: os_helper.unlink(self.filename) with open(self.filename, mode) as f: diff --git a/Lib/test/test_io/test_fileio.py b/Lib/test/test_io/test_fileio.py index e3d54f6315a..e53c4749f58 100644 --- a/Lib/test/test_io/test_fileio.py +++ b/Lib/test/test_io/test_fileio.py @@ -567,8 +567,8 @@ def testModeStrings(self): # test that the mode attribute is correct for various mode strings # given as init args try: - for modes in [('w', 'wb'), ('wb', 'wb'), ('wb+', 'rb+'), - ('w+b', 'rb+'), ('a', 'ab'), ('ab', 'ab'), + for modes in [('w', 'wb'), ('wb', 'wb'), ('wb+', 'wb+'), + ('w+b', 'wb+'), ('a', 'ab'), ('ab', 'ab'), ('ab+', 'ab+'), ('a+b', 'ab+'), ('r', 'rb'), ('rb', 'rb'), ('rb+', 'rb+'), ('r+b', 'rb+')]: # read modes are last so that TESTFN will exist first diff --git a/Lib/test/test_io/test_general.py b/Lib/test/test_io/test_general.py index 7cea0392d0d..ac9c5a425d7 100644 --- a/Lib/test/test_io/test_general.py +++ b/Lib/test/test_io/test_general.py @@ -960,8 +960,8 @@ def test_attributes(self): f = self.open(os_helper.TESTFN, "w+", encoding="utf-8") self.assertEqual(f.mode, "w+") - self.assertEqual(f.buffer.mode, "rb+") # Does it really matter? - self.assertEqual(f.buffer.raw.mode, "rb+") + self.assertEqual(f.buffer.mode, "wb+") + self.assertEqual(f.buffer.raw.mode, "wb+") g = self.open(f.fileno(), "wb", closefd=False) self.assertEqual(g.mode, "wb") diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index 52b13b98cbc..7eec34f2f29 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -1386,7 +1386,7 @@ def test_properties(self): f.write(b'x') self.assertTrue(f._rolled) - self.assertEqual(f.mode, 'rb+') + self.assertEqual(f.mode, 'wb+') self.assertIsNotNone(f.name) with self.assertRaises(AttributeError): f.newlines diff --git a/Misc/NEWS.d/next/Library/2025-08-15-20-35-30.gh-issue-69528.qc-Eh_.rst b/Misc/NEWS.d/next/Library/2025-08-15-20-35-30.gh-issue-69528.qc-Eh_.rst new file mode 100644 index 00000000000..b18781e0dce --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-08-15-20-35-30.gh-issue-69528.qc-Eh_.rst @@ -0,0 +1,2 @@ +The :attr:`~io.FileIO.mode` attribute of files opened in the ``'wb+'`` mode is +now ``'wb+'`` instead of ``'rb+'``. diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index 9992d48a1d8..b84c1bd3e22 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -70,6 +70,7 @@ typedef struct { unsigned int writable : 1; unsigned int appending : 1; signed int seekable : 2; /* -1 means unknown */ + unsigned int truncate : 1; unsigned int closefd : 1; char finalizing; /* Stat result which was grabbed at file open, useful for optimizing common @@ -209,6 +210,7 @@ fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds) self->writable = 0; self->appending = 0; self->seekable = -1; + self->truncate = 0; self->stat_atopen = NULL; self->closefd = 1; self->weakreflist = NULL; @@ -341,6 +343,7 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode, goto bad_mode; rwa = 1; self->writable = 1; + self->truncate = 1; flags |= O_CREAT | O_TRUNC; break; case 'a': @@ -1145,10 +1148,17 @@ mode_string(fileio *self) return "ab"; } else if (self->readable) { - if (self->writable) - return "rb+"; - else + if (self->writable) { + if (self->truncate) { + return "wb+"; + } + else { + return "rb+"; + } + } + else { return "rb"; + } } else return "wb"; From a8edca62fc6d44d16c7f86d49421be1a5ebea3e5 Mon Sep 17 00:00:00 2001 From: Emma Smith Date: Tue, 21 Oct 2025 14:48:29 -0700 Subject: [PATCH 246/373] gh-132835: Add defensive NULL checks to MRO resolution (GH-134763) Currently, there are a few places where tp_mro could theoretically become NULL, but do not in practice. This commit adds defensive checks for NULL values to ensure that any changes do not introduce a crash and that state invariants are upheld. The assertions added in this commit are all instances where a NULL value would get passed to something not expecting a NULL, so it is better to catch an assertion failure than crash later on. There are a few cases where it is OK for the return of lookup_tp_mro to be NULL, such as when passed to is_subtype_with_mro, which handles this explicitly. --- Objects/typeobject.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 721d4871244..6cc6d366a35 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1780,7 +1780,7 @@ static int recurse_down_subclasses(PyTypeObject *type, PyObject *name, // Compute tp_mro for this type and all of its subclasses. This // is called after __bases__ is assigned to an existing type. static int -mro_hierarchy(PyTypeObject *type, PyObject *temp) +mro_hierarchy_for_complete_type(PyTypeObject *type, PyObject *temp) { ASSERT_TYPE_LOCK_HELD(); @@ -1791,6 +1791,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp) return res; } PyObject *new_mro = lookup_tp_mro(type); + assert(new_mro != NULL); PyObject *tuple; if (old_mro != NULL) { @@ -1835,7 +1836,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp) Py_ssize_t n = PyList_GET_SIZE(subclasses); for (Py_ssize_t i = 0; i < n; i++) { PyTypeObject *subclass = _PyType_CAST(PyList_GET_ITEM(subclasses, i)); - res = mro_hierarchy(subclass, temp); + res = mro_hierarchy_for_complete_type(subclass, temp); if (res < 0) { break; } @@ -1926,7 +1927,7 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject *new_bases, PyTypeObject *b if (temp == NULL) { goto bail; } - if (mro_hierarchy(type, temp) < 0) { + if (mro_hierarchy_for_complete_type(type, temp) < 0) { goto undo; } Py_DECREF(temp); @@ -3432,6 +3433,7 @@ mro_implementation_unlocked(PyTypeObject *type) */ PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, 0)); PyObject *base_mro = lookup_tp_mro(base); + assert(base_mro != NULL); Py_ssize_t k = PyTuple_GET_SIZE(base_mro); PyObject *result = PyTuple_New(k + 1); if (result == NULL) { @@ -3466,9 +3468,12 @@ mro_implementation_unlocked(PyTypeObject *type) return NULL; } + PyObject *mro_to_merge; for (Py_ssize_t i = 0; i < n; i++) { PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, i)); - to_merge[i] = lookup_tp_mro(base); + mro_to_merge = lookup_tp_mro(base); + assert(mro_to_merge != NULL); + to_merge[i] = mro_to_merge; } to_merge[n] = bases; @@ -8998,6 +9003,7 @@ type_ready_inherit(PyTypeObject *type) // Inherit slots PyObject *mro = lookup_tp_mro(type); + assert(mro != NULL); Py_ssize_t n = PyTuple_GET_SIZE(mro); for (Py_ssize_t i = 1; i < n; i++) { PyObject *b = PyTuple_GET_ITEM(mro, i); From 29b38b7aae884c14085a918282ea7f0798ed7a2a Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 22 Oct 2025 07:12:26 +0800 Subject: [PATCH 247/373] gh-55258: Skip tests of stdout status on mobile platforms (#140401) Skip tests of stdout status on mobile platforms. --- Lib/test/test_support.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 92909519636..d69328a6a6a 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -96,6 +96,8 @@ def test_get_attribute(self): self.test_get_attribute) self.assertRaises(unittest.SkipTest, support.get_attribute, self, "foo") + @unittest.skipIf(support.is_android or support.is_apple_mobile, + 'Mobile platforms redirect stdout to system log') def test_get_original_stdout(self): if isinstance(sys.stdout, io.StringIO): # gh-55258: When --junit-xml is used, stdout is a StringIO: From b3a38438d83245e52dff80f348c7fde539333cea Mon Sep 17 00:00:00 2001 From: Alper Date: Tue, 21 Oct 2025 17:14:48 -0700 Subject: [PATCH 248/373] gh-116738: Make _suggestions module thread-safe (gh-140321) --- .../test_free_threading/test_suggestions.py | 24 +++++++++++++++++++ ...-10-18-19-52-20.gh-issue-116738.NLJW0L.rst | 2 ++ Modules/_suggestions.c | 5 ++-- Modules/clinic/_suggestions.c.h | 5 +++- 4 files changed, 33 insertions(+), 3 deletions(-) create mode 100755 Lib/test/test_free_threading/test_suggestions.py create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-18-19-52-20.gh-issue-116738.NLJW0L.rst diff --git a/Lib/test/test_free_threading/test_suggestions.py b/Lib/test/test_free_threading/test_suggestions.py new file mode 100755 index 00000000000..2c10a511b86 --- /dev/null +++ b/Lib/test/test_free_threading/test_suggestions.py @@ -0,0 +1,24 @@ +import unittest + +from test.support import import_helper, threading_helper +from test.support.threading_helper import run_concurrently + +suggestions = import_helper.import_module("_suggestions") + +NTHREADS = 10 + + +@threading_helper.requires_working_threading() +class SuggestionsTests(unittest.TestCase): + def test_generate_suggestions(self): + candidates = [str(i) for i in range(100)] + + def worker(): + _ = suggestions._generate_suggestions(candidates, "42") + candidates.clear() + + run_concurrently(worker_func=worker, nthreads=NTHREADS) + + +if __name__ == "__main__": + unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-18-19-52-20.gh-issue-116738.NLJW0L.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-18-19-52-20.gh-issue-116738.NLJW0L.rst new file mode 100644 index 00000000000..bf323b870bc --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-18-19-52-20.gh-issue-116738.NLJW0L.rst @@ -0,0 +1,2 @@ +Make _suggestions module thread-safe on the :term:`free threaded ` build. diff --git a/Modules/_suggestions.c b/Modules/_suggestions.c index b8bc6db2477..fb588de7808 100644 --- a/Modules/_suggestions.c +++ b/Modules/_suggestions.c @@ -8,6 +8,7 @@ module _suggestions /*[clinic end generated code: output=da39a3ee5e6b4b0d input=e58d81fafad5637b]*/ /*[clinic input] +@critical_section candidates _suggestions._generate_suggestions candidates: object item: unicode @@ -18,7 +19,7 @@ Returns the candidate in candidates that's closest to item static PyObject * _suggestions__generate_suggestions_impl(PyObject *module, PyObject *candidates, PyObject *item) -/*[clinic end generated code: output=79be7b653ae5e7ca input=ba2a8dddc654e33a]*/ +/*[clinic end generated code: output=79be7b653ae5e7ca input=92861a6c9bd8f667]*/ { // Check if dir is a list if (!PyList_CheckExact(candidates)) { @@ -29,7 +30,7 @@ _suggestions__generate_suggestions_impl(PyObject *module, // Check if all elements in the list are Unicode Py_ssize_t size = PyList_Size(candidates); for (Py_ssize_t i = 0; i < size; ++i) { - PyObject *elem = PyList_GetItem(candidates, i); + PyObject *elem = PyList_GET_ITEM(candidates, i); if (!PyUnicode_Check(elem)) { PyErr_SetString(PyExc_TypeError, "all elements in 'candidates' must be strings"); return NULL; diff --git a/Modules/clinic/_suggestions.c.h b/Modules/clinic/_suggestions.c.h index 51484b13d5a..3b3ed5056ac 100644 --- a/Modules/clinic/_suggestions.c.h +++ b/Modules/clinic/_suggestions.c.h @@ -2,6 +2,7 @@ preserve [clinic start generated code]*/ +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_CheckPositional() PyDoc_STRVAR(_suggestions__generate_suggestions__doc__, @@ -33,9 +34,11 @@ _suggestions__generate_suggestions(PyObject *module, PyObject *const *args, Py_s goto exit; } item = args[1]; + Py_BEGIN_CRITICAL_SECTION(candidates); return_value = _suggestions__generate_suggestions_impl(module, candidates, item); + Py_END_CRITICAL_SECTION(); exit: return return_value; } -/*[clinic end generated code: output=1d8e963cdae30b13 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1690dd15a464d19c input=a9049054013a1b77]*/ From 7339cf7899229920a8c3b634a026a94df40853b8 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 22 Oct 2025 11:48:37 +0200 Subject: [PATCH 249/373] gh-83714: Use "stx_" prefix for all os.statx_result members (#140432) Rename stx_birthtime to stx_btime, and rename stx_birthtime_ns to stx_btime_ns. --- Doc/library/os.rst | 255 +++++++++++++----- Doc/whatsnew/3.15.rst | 2 +- Lib/test/test_os/test_os.py | 55 ++-- Lib/test/test_os/test_posix.py | 15 +- ...5-09-18-21-25-41.gh-issue-83714.TQjDWZ.rst | 4 +- Modules/posixmodule.c | 95 ++++--- 6 files changed, 271 insertions(+), 155 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 50f6886c89d..8f7b9ac15a0 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3411,90 +3411,43 @@ features: Information about a file returned by :func:`os.statx`. - :class:`!statx_result` has all the attributes that :class:`~stat_result` has - on Linux, making it :term:`duck-typing` compatible, but - :class:`!statx_result` is not a subclass of :class:`~stat_result` and cannot - be used as a tuple. - - :class:`!statx_result` has the following additional attributes: + :class:`!statx_result` has the following attributes: .. attribute:: stx_mask Bitmask of :const:`STATX_* ` constants specifying the information retrieved, which may differ from what was requested. - .. attribute:: stx_attributes_mask + .. attribute:: stx_atime - Bitmask of :const:`STATX_ATTR_* ` constants - specifying the attributes bits supported for this file. + Time of most recent access expressed in seconds. - .. attribute:: stx_attributes + Equal to ``None`` if :data:`STATX_ATIME` is missing from + :attr:`~statx_result.stx_mask`. - Bitmask of :const:`STATX_ATTR_* ` constants - specifying the attributes of this file. + .. attribute:: stx_atime_ns - .. attribute:: stx_dev_major + Time of most recent access expressed in nanoseconds as an integer. - Major number of the device on which this file resides. + Equal to ``None`` if :data:`STATX_ATIME` is missing from + :attr:`~statx_result.stx_mask`. - .. attribute:: stx_dev_minor + .. attribute:: stx_atomic_write_segments_max - Minor number of the device on which this file resides. + Maximum iovecs for direct I/O with torn-write protection. - .. attribute:: stx_rdev_major - - Major number of the device this file represents. - - .. attribute:: stx_rdev_minor - - Minor number of the device this file represents. - - .. attribute:: stx_mnt_id - - Mount identifier. - - Equal to ``None`` if :data:`STATX_MNT_ID` is missing from - :attr:`~os.statx_result.stx_mask`. + Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from + :attr:`~statx_result.stx_mask`. .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel - userspace API headers >= 5.8. - - .. attribute:: stx_dio_mem_align - - Direct I/O memory buffer alignment requirement. - - Equal to ``None`` if :data:`STATX_DIOALIGN` is missing from - :attr:`~os.statx_result.stx_mask`. - - .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel - userspace API headers >= 6.1. - - .. attribute:: stx_dio_offset_align - - Direct I/O file offset alignment requirement. - - Equal to ``None`` if :data:`STATX_DIOALIGN` is missing from - :attr:`~os.statx_result.stx_mask`. - - .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel - userspace API headers >= 6.1. - - .. attribute:: stx_subvol - - Subvolume identifier. - - Equal to ``None`` if :data:`STATX_SUBVOL` is missing from - :attr:`~os.statx_result.stx_mask`. - - .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel - userspace API headers >= 6.10. + userspace API headers >= 6.11. .. attribute:: stx_atomic_write_unit_min Minimum size for direct I/O with torn-write protection. Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from - :attr:`~os.statx_result.stx_mask`. + :attr:`~statx_result.stx_mask`. .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel userspace API headers >= 6.11. @@ -3504,7 +3457,7 @@ features: Maximum size for direct I/O with torn-write protection. Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from - :attr:`~os.statx_result.stx_mask`. + :attr:`~statx_result.stx_mask`. .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel userspace API headers >= 6.11. @@ -3514,31 +3467,193 @@ features: Maximum optimized size for direct I/O with torn-write protection. Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from - :attr:`~os.statx_result.stx_mask`. + :attr:`~statx_result.stx_mask`. .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel userspace API headers >= 6.16. - .. attribute:: stx_atomic_write_segments_max + .. attribute:: stx_attributes - Maximum iovecs for direct I/O with torn-write protection. + Bitmask of :const:`STATX_ATTR_* ` constants + specifying the attributes of this file. - Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from - :attr:`~os.statx_result.stx_mask`. + .. attribute:: stx_attributes_mask + + A mask indicating which bits in :attr:`stx_attributes` are supported by + the VFS and the filesystem. + + .. attribute:: stx_blksize + + "Preferred" blocksize for efficient file system I/O. Writing to a file in + smaller chunks may cause an inefficient read-modify-rewrite. + + .. attribute:: stx_blocks + + Number of 512-byte blocks allocated for file. + This may be smaller than :attr:`stx_size`/512 when the file has holes. + + Equal to ``None`` if :data:`STATX_BLOCKS` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_btime + + Time of file creation expressed in seconds. + + Equal to ``None`` if :data:`STATX_BTIME` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_btime_ns + + Time of file creation expressed in nanoseconds as an integer. + + Equal to ``None`` if :data:`STATX_BTIME` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_ctime + + Time of most recent metadata change expressed in seconds. + + Equal to ``None`` if :data:`STATX_CTIME` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_ctime_ns + + Time of most recent metadata change expressed in nanoseconds as an + integer. + + Equal to ``None`` if :data:`STATX_CTIME` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_dev + + Identifier of the device on which this file resides. + + .. attribute:: stx_dev_major + + Major number of the device on which this file resides. + + .. attribute:: stx_dev_minor + + Minor number of the device on which this file resides. + + .. attribute:: stx_dio_offset_align + + Direct I/O file offset alignment requirement. + + Equal to ``None`` if :data:`STATX_DIOALIGN` is missing from + :attr:`~statx_result.stx_mask`. .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel - userspace API headers >= 6.11. + userspace API headers >= 6.1. + + .. attribute:: stx_dio_mem_align + + Direct I/O memory buffer alignment requirement. + + Equal to ``None`` if :data:`STATX_DIOALIGN` is missing from + :attr:`~statx_result.stx_mask`. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.1. .. attribute:: stx_dio_read_offset_align Direct I/O file offset alignment requirement for reads. Equal to ``None`` if :data:`STATX_DIO_READ_ALIGN` is missing from - :attr:`~os.statx_result.stx_mask`. + :attr:`~statx_result.stx_mask`. .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel userspace API headers >= 6.14. + .. attribute:: stx_gid + + Group identifier of the file owner. + + Equal to ``None`` if :data:`STATX_GID` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_ino + + Inode number. + + Equal to ``None`` if :data:`STATX_INO` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_mnt_id + + Mount identifier. + + Equal to ``None`` if :data:`STATX_MNT_ID` is missing from + :attr:`~statx_result.stx_mask`. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 5.8. + + .. attribute:: stx_mode + + File mode: file type and file mode bits (permissions). + + .. attribute:: stx_mtime + + Time of most recent content modification expressed in seconds. + + Equal to ``None`` if :data:`STATX_MTIME` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_mtime_ns + + Time of most recent content modification expressed in nanoseconds as an + integer. + + Equal to ``None`` if :data:`STATX_MTIME` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_nlink + + Number of hard links. + + Equal to ``None`` if :data:`STATX_NLINK` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_rdev + + Type of device if an inode device. + + .. attribute:: stx_rdev_major + + Major number of the device this file represents. + + .. attribute:: stx_rdev_minor + + Minor number of the device this file represents. + + .. attribute:: stx_size + + Size of the file in bytes, if it is a regular file or a symbolic link. + The size of a symbolic link is the length of the pathname it contains, + without a terminating null byte. + + Equal to ``None`` if :data:`STATX_SIZE` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_subvol + + Subvolume identifier. + + Equal to ``None`` if :data:`STATX_SUBVOL` is missing from + :attr:`~statx_result.stx_mask`. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.10. + + .. attribute:: stx_uid + + User identifier of the file owner. + + Equal to ``None`` if :data:`STATX_UID` is missing from + :attr:`~statx_result.stx_mask`. + .. seealso:: The :manpage:`statx(2)` man page. .. availability:: Linux >= 4.11 with glibc >= 2.28. diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 8503a4c7f97..22e9cfde06f 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -438,7 +438,7 @@ os * Add :func:`os.statx` on Linux kernel versions 4.11 and later with glibc versions 2.28 and later. - (Contributed by Jeffrey Bosboom in :gh:`83714`.) + (Contributed by Jeffrey Bosboom and Victor Stinner in :gh:`83714`.) os.path diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py index 67599537736..9a40c5c2a1f 100644 --- a/Lib/test/test_os/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -750,8 +750,8 @@ def check_statx_attributes(self, filename): result = os.statx(filename, maximal_mask) basic_result = os.stat(filename) - time_attributes = ('st_atime', 'st_mtime', 'st_ctime', 'st_birthtime') - # gh-83714: st_birthtime can be None on tmpfs even if STATX_BTIME mask + time_attributes = ('stx_atime', 'stx_btime', 'stx_ctime', 'stx_mtime') + # gh-83714: stx_btime can be None on tmpfs even if STATX_BTIME mask # is used time_attributes = [name for name in time_attributes if getattr(result, name) is not None] @@ -759,43 +759,44 @@ def check_statx_attributes(self, filename): # Check that valid attributes match os.stat. requirements = ( - ('st_mode', os.STATX_TYPE | os.STATX_MODE), - ('st_nlink', os.STATX_NLINK), - ('st_uid', os.STATX_UID), - ('st_gid', os.STATX_GID), - ('st_atime', os.STATX_ATIME), - ('st_atime_ns', os.STATX_ATIME), - ('st_mtime', os.STATX_MTIME), - ('st_mtime_ns', os.STATX_MTIME), - ('st_ctime', os.STATX_CTIME), - ('st_ctime_ns', os.STATX_CTIME), - ('st_ino', os.STATX_INO), - ('st_size', os.STATX_SIZE), - ('st_blocks', os.STATX_BLOCKS), - ('st_birthtime', os.STATX_BTIME), - ('st_birthtime_ns', os.STATX_BTIME), + ('stx_mode', os.STATX_TYPE | os.STATX_MODE), + ('stx_nlink', os.STATX_NLINK), + ('stx_uid', os.STATX_UID), + ('stx_gid', os.STATX_GID), + ('stx_atime', os.STATX_ATIME), + ('stx_atime_ns', os.STATX_ATIME), + ('stx_mtime', os.STATX_MTIME), + ('stx_mtime_ns', os.STATX_MTIME), + ('stx_ctime', os.STATX_CTIME), + ('stx_ctime_ns', os.STATX_CTIME), + ('stx_ino', os.STATX_INO), + ('stx_size', os.STATX_SIZE), + ('stx_blocks', os.STATX_BLOCKS), + ('stx_birthtime', os.STATX_BTIME), + ('stx_birthtime_ns', os.STATX_BTIME), # unconditionally valid members - ('st_blksize', 0), - ('st_rdev', 0), - ('st_dev', 0), + ('stx_blksize', 0), + ('stx_rdev', 0), + ('stx_dev', 0), ) for name, bits in requirements: - if result.stx_mask & bits == bits and hasattr(basic_result, name): + st_name = "st_" + name[4:] + if result.stx_mask & bits == bits and hasattr(basic_result, st_name): x = getattr(result, name) - b = getattr(basic_result, name) + b = getattr(basic_result, st_name) self.assertEqual(type(x), type(b)) if isinstance(x, float): self.assertAlmostEqual(x, b, msg=name) else: self.assertEqual(x, b, msg=name) - self.assertEqual(result.stx_rdev_major, os.major(result.st_rdev)) - self.assertEqual(result.stx_rdev_minor, os.minor(result.st_rdev)) - self.assertEqual(result.stx_dev_major, os.major(result.st_dev)) - self.assertEqual(result.stx_dev_minor, os.minor(result.st_dev)) + self.assertEqual(result.stx_rdev_major, os.major(result.stx_rdev)) + self.assertEqual(result.stx_rdev_minor, os.minor(result.stx_rdev)) + self.assertEqual(result.stx_dev_major, os.major(result.stx_dev)) + self.assertEqual(result.stx_dev_minor, os.minor(result.stx_dev)) members = [name for name in dir(result) - if name.startswith('st_') or name.startswith('stx_')] + if name.startswith('stx_')] for name in members: try: setattr(result, name, 1) diff --git a/Lib/test/test_os/test_posix.py b/Lib/test/test_os/test_posix.py index 905f0201253..de24719a1ca 100644 --- a/Lib/test/test_os/test_posix.py +++ b/Lib/test/test_os/test_posix.py @@ -1672,17 +1672,22 @@ def test_chown_dir_fd(self): with self.prepare_file() as (dir_fd, name, fullname): posix.chown(name, os.getuid(), os.getgid(), dir_fd=dir_fd) - def check_statlike_dir_fd(self, func): + def check_statlike_dir_fd(self, func, prefix): with self.prepare() as (dir_fd, name, fullname): with open(fullname, 'w') as outfile: outfile.write("testline\n") self.addCleanup(posix.unlink, fullname) + def get(result, attr): + return getattr(result, prefix + attr) + s1 = func(fullname) s2 = func(name, dir_fd=dir_fd) - self.assertEqual((s1.st_dev, s1.st_ino), (s2.st_dev, s2.st_ino)) + self.assertEqual((get(s1, "dev"), get(s1, "ino")), + (get(s2, "dev"), get(s2, "ino"))) s2 = func(fullname, dir_fd=None) - self.assertEqual((s1.st_dev, s1.st_ino), (s2.st_dev, s2.st_ino)) + self.assertEqual((get(s1, "dev"), get(s1, "ino")), + (get(s2, "dev"), get(s2, "ino"))) self.assertRaisesRegex(TypeError, 'should be integer or None, not', func, name, dir_fd=posix.getcwd()) @@ -1700,13 +1705,13 @@ def check_statlike_dir_fd(self, func): @unittest.skipUnless(os.stat in os.supports_dir_fd, "test needs dir_fd support in os.stat()") def test_stat_dir_fd(self): - self.check_statlike_dir_fd(posix.stat) + self.check_statlike_dir_fd(posix.stat, prefix="st_") @unittest.skipUnless(hasattr(posix, 'statx'), "test needs os.statx()") def test_statx_dir_fd(self): def func(path, **kwargs): return posix.statx(path, os.STATX_INO, **kwargs) - self.check_statlike_dir_fd(func) + self.check_statlike_dir_fd(func, prefix="stx_") @unittest.skipUnless(os.utime in os.supports_dir_fd, "test needs dir_fd support in os.utime()") def test_utime_dir_fd(self): diff --git a/Misc/NEWS.d/next/Library/2025-09-18-21-25-41.gh-issue-83714.TQjDWZ.rst b/Misc/NEWS.d/next/Library/2025-09-18-21-25-41.gh-issue-83714.TQjDWZ.rst index 7229a361147..3653eb9a114 100644 --- a/Misc/NEWS.d/next/Library/2025-09-18-21-25-41.gh-issue-83714.TQjDWZ.rst +++ b/Misc/NEWS.d/next/Library/2025-09-18-21-25-41.gh-issue-83714.TQjDWZ.rst @@ -1,2 +1,2 @@ -Implement :func:`os.statx` on Linux kernel versions 4.11 and later with -glibc versions 2.28 and later. Contributed by Jeffrey Bosboom. +Implement :func:`os.statx` on Linux kernel versions 4.11 and later with glibc +versions 2.28 and later. Contributed by Jeffrey Bosboom and Victor Stinner. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index d0b9eed8c82..433acdcb538 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -3330,17 +3330,17 @@ typedef struct { static PyMemberDef pystatx_result_members[] = { MM(stx_mask, Py_T_UINT, mask, "member validity mask"), - MM(st_blksize, Py_T_UINT, blksize, "blocksize for filesystem I/O"), + MM(stx_blksize, Py_T_UINT, blksize, "blocksize for filesystem I/O"), MM(stx_attributes, Py_T_ULONGLONG, attributes, "Linux inode attribute bits"), - MM(st_mode, Py_T_USHORT, mode, "protection bits"), + MM(stx_mode, Py_T_USHORT, mode, "protection bits"), MM(stx_attributes_mask, Py_T_ULONGLONG, attributes_mask, "Mask of supported bits in stx_attributes"), MM(stx_rdev_major, Py_T_UINT, rdev_major, "represented device major number"), MM(stx_rdev_minor, Py_T_UINT, rdev_minor, "represented device minor number"), - MX(st_rdev, Py_T_ULONGLONG, rdev, "device type (if inode device)"), + MX(stx_rdev, Py_T_ULONGLONG, rdev, "device type (if inode device)"), MM(stx_dev_major, Py_T_UINT, dev_major, "containing device major number"), MM(stx_dev_minor, Py_T_UINT, dev_minor, "containing device minor number"), - MX(st_dev, Py_T_ULONGLONG, dev, "device"), + MX(stx_dev, Py_T_ULONGLONG, dev, "device"), {NULL}, }; @@ -3349,7 +3349,7 @@ static PyMemberDef pystatx_result_members[] = { #undef M -#define STATX_GET_UINT(ATTR, MEMBER, MASK) \ +#define STATX_GET_UINT(ATTR, MASK) \ static PyObject* \ pystatx_result_get_##ATTR(PyObject *op, void *Py_UNUSED(context)) \ { \ @@ -3357,36 +3357,31 @@ static PyMemberDef pystatx_result_members[] = { if (!(self->stx.stx_mask & MASK)) { \ Py_RETURN_NONE; \ } \ - unsigned long value = self->stx.MEMBER; \ + unsigned long value = self->stx.ATTR; \ return PyLong_FromUnsignedLong(value); \ } -STATX_GET_UINT(st_uid, stx_uid, STATX_UID) -STATX_GET_UINT(st_gid, stx_gid, STATX_GID) -STATX_GET_UINT(st_nlink, stx_nlink, STATX_NLINK) +STATX_GET_UINT(stx_uid, STATX_UID) +STATX_GET_UINT(stx_gid, STATX_GID) +STATX_GET_UINT(stx_nlink, STATX_NLINK) #ifdef HAVE_STRUCT_STATX_STX_DIO_MEM_ALIGN -STATX_GET_UINT(stx_dio_mem_align, stx_dio_mem_align, STATX_DIOALIGN) -STATX_GET_UINT(stx_dio_offset_align, stx_dio_offset_align, STATX_DIOALIGN) +STATX_GET_UINT(stx_dio_mem_align, STATX_DIOALIGN) +STATX_GET_UINT(stx_dio_offset_align, STATX_DIOALIGN) #endif #ifdef HAVE_STRUCT_STATX_STX_DIO_READ_OFFSET_ALIGN -STATX_GET_UINT(stx_dio_read_offset_align, stx_dio_read_offset_align, - STATX_DIO_READ_ALIGN) +STATX_GET_UINT(stx_dio_read_offset_align, STATX_DIO_READ_ALIGN) #endif #ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MIN -STATX_GET_UINT(stx_atomic_write_unit_min, stx_atomic_write_unit_min, - STATX_WRITE_ATOMIC) -STATX_GET_UINT(stx_atomic_write_unit_max, stx_atomic_write_unit_max, - STATX_WRITE_ATOMIC) -STATX_GET_UINT(stx_atomic_write_segments_max, stx_atomic_write_segments_max, - STATX_WRITE_ATOMIC) +STATX_GET_UINT(stx_atomic_write_unit_min, STATX_WRITE_ATOMIC) +STATX_GET_UINT(stx_atomic_write_unit_max, STATX_WRITE_ATOMIC) +STATX_GET_UINT(stx_atomic_write_segments_max, STATX_WRITE_ATOMIC) #endif #ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT -STATX_GET_UINT(stx_atomic_write_unit_max_opt, stx_atomic_write_unit_max_opt, - STATX_WRITE_ATOMIC) +STATX_GET_UINT(stx_atomic_write_unit_max_opt, STATX_WRITE_ATOMIC) #endif -#define STATX_GET_ULONGLONG(ATTR, MEMBER, MASK) \ +#define STATX_GET_ULONGLONG(ATTR, MASK) \ static PyObject* \ pystatx_result_get_##ATTR(PyObject *op, void *Py_UNUSED(context)) \ { \ @@ -3394,18 +3389,18 @@ STATX_GET_UINT(stx_atomic_write_unit_max_opt, stx_atomic_write_unit_max_opt, if (!(self->stx.stx_mask & MASK)) { \ Py_RETURN_NONE; \ } \ - unsigned long long value = self->stx.MEMBER; \ + unsigned long long value = self->stx.ATTR; \ return PyLong_FromUnsignedLongLong(value); \ } -STATX_GET_ULONGLONG(st_blocks, stx_blocks, STATX_BLOCKS) -STATX_GET_ULONGLONG(st_ino, stx_ino, STATX_INO) -STATX_GET_ULONGLONG(st_size, stx_size, STATX_SIZE) +STATX_GET_ULONGLONG(stx_blocks, STATX_BLOCKS) +STATX_GET_ULONGLONG(stx_ino, STATX_INO) +STATX_GET_ULONGLONG(stx_size, STATX_SIZE) #ifdef HAVE_STRUCT_STATX_STX_MNT_ID -STATX_GET_ULONGLONG(stx_mnt_id, stx_mnt_id, STATX_MNT_ID) +STATX_GET_ULONGLONG(stx_mnt_id, STATX_MNT_ID) #endif #ifdef HAVE_STRUCT_STATX_STX_SUBVOL -STATX_GET_ULONGLONG(stx_subvol, stx_subvol, STATX_SUBVOL) +STATX_GET_ULONGLONG(stx_subvol, STATX_SUBVOL) #endif @@ -3421,10 +3416,10 @@ STATX_GET_ULONGLONG(stx_subvol, stx_subvol, STATX_SUBVOL) return PyFloat_FromDouble(sec); \ } -STATX_GET_DOUBLE(st_atime, atime_sec, STATX_ATIME) -STATX_GET_DOUBLE(st_birthtime, btime_sec, STATX_BTIME) -STATX_GET_DOUBLE(st_ctime, ctime_sec, STATX_CTIME) -STATX_GET_DOUBLE(st_mtime, mtime_sec, STATX_MTIME) +STATX_GET_DOUBLE(stx_atime, atime_sec, STATX_ATIME) +STATX_GET_DOUBLE(stx_btime, btime_sec, STATX_BTIME) +STATX_GET_DOUBLE(stx_ctime, ctime_sec, STATX_CTIME) +STATX_GET_DOUBLE(stx_mtime, mtime_sec, STATX_MTIME) #define STATX_GET_NSEC(ATTR, MEMBER, MASK) \ static PyObject* \ @@ -3440,29 +3435,29 @@ STATX_GET_DOUBLE(st_mtime, mtime_sec, STATX_MTIME) return stat_nanosecond_timestamp(state, ts->tv_sec, ts->tv_nsec); \ } -STATX_GET_NSEC(st_atime_ns, stx_atime, STATX_ATIME) -STATX_GET_NSEC(st_birthtime_ns, stx_btime, STATX_BTIME) -STATX_GET_NSEC(st_ctime_ns, stx_ctime, STATX_CTIME) -STATX_GET_NSEC(st_mtime_ns, stx_mtime, STATX_MTIME) +STATX_GET_NSEC(stx_atime_ns, stx_atime, STATX_ATIME) +STATX_GET_NSEC(stx_btime_ns, stx_btime, STATX_BTIME) +STATX_GET_NSEC(stx_ctime_ns, stx_ctime, STATX_CTIME) +STATX_GET_NSEC(stx_mtime_ns, stx_mtime, STATX_MTIME) #define G(attr, doc) \ {#attr, pystatx_result_get_##attr, NULL, PyDoc_STR(doc), NULL} static PyGetSetDef pystatx_result_getset[] = { - G(st_nlink, "number of hard links"), - G(st_uid, "user ID of owner"), - G(st_gid, "group ID of owner"), - G(st_ino, "inode"), - G(st_size, "total size, in bytes"), - G(st_blocks, "number of blocks allocated"), - G(st_atime, "time of last access"), - G(st_atime_ns, "time of last access in nanoseconds"), - G(st_birthtime, "time of creation"), - G(st_birthtime_ns, "time of creation in nanoseconds"), - G(st_ctime, "time of last change"), - G(st_ctime_ns, "time of last change in nanoseconds"), - G(st_mtime, "time of last modification"), - G(st_mtime_ns, "time of last modification in nanoseconds"), + G(stx_nlink, "number of hard links"), + G(stx_uid, "user ID of owner"), + G(stx_gid, "group ID of owner"), + G(stx_ino, "inode"), + G(stx_size, "total size, in bytes"), + G(stx_blocks, "number of blocks allocated"), + G(stx_atime, "time of last access"), + G(stx_atime_ns, "time of last access in nanoseconds"), + G(stx_btime, "time of creation"), + G(stx_btime_ns, "time of creation in nanoseconds"), + G(stx_ctime, "time of last change"), + G(stx_ctime_ns, "time of last change in nanoseconds"), + G(stx_mtime, "time of last modification"), + G(stx_mtime_ns, "time of last modification in nanoseconds"), #ifdef HAVE_STRUCT_STATX_STX_MNT_ID G(stx_mnt_id, "mount ID"), #endif From e53e9eb2f1b148219d1a269e979bc7aef4fc8302 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 22 Oct 2025 12:42:51 +0200 Subject: [PATCH 250/373] gh-83714: Only use STATX_MNT_ID & STATX_SUBVOL if they're defined (GH-140446) --- Modules/posixmodule.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 433acdcb538..465af26b1c5 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -3396,10 +3396,10 @@ STATX_GET_UINT(stx_atomic_write_unit_max_opt, STATX_WRITE_ATOMIC) STATX_GET_ULONGLONG(stx_blocks, STATX_BLOCKS) STATX_GET_ULONGLONG(stx_ino, STATX_INO) STATX_GET_ULONGLONG(stx_size, STATX_SIZE) -#ifdef HAVE_STRUCT_STATX_STX_MNT_ID +#if defined(STATX_MNT_ID) && defined(HAVE_STRUCT_STATX_STX_MNT_ID) STATX_GET_ULONGLONG(stx_mnt_id, STATX_MNT_ID) #endif -#ifdef HAVE_STRUCT_STATX_STX_SUBVOL +#if defined(STATX_SUBVOL) && defined(HAVE_STRUCT_STATX_STX_SUBVOL) STATX_GET_ULONGLONG(stx_subvol, STATX_SUBVOL) #endif @@ -3458,14 +3458,14 @@ static PyGetSetDef pystatx_result_getset[] = { G(stx_ctime_ns, "time of last change in nanoseconds"), G(stx_mtime, "time of last modification"), G(stx_mtime_ns, "time of last modification in nanoseconds"), -#ifdef HAVE_STRUCT_STATX_STX_MNT_ID +#if defined(STATX_MNT_ID) && defined(HAVE_STRUCT_STATX_STX_MNT_ID) G(stx_mnt_id, "mount ID"), #endif #ifdef HAVE_STRUCT_STATX_STX_DIO_MEM_ALIGN G(stx_dio_mem_align, "direct I/O memory buffer alignment"), G(stx_dio_offset_align, "direct I/O file offset alignment"), #endif -#ifdef HAVE_STRUCT_STATX_STX_SUBVOL +#if defined(STATX_SUBVOL) && defined(HAVE_STRUCT_STATX_STX_SUBVOL) G(stx_subvol, "subvolume ID"), #endif #ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MIN From a4709e525f5bc3a1719b33f52377cccb2af70278 Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Wed, 22 Oct 2025 18:14:25 +0500 Subject: [PATCH 251/373] GH-139193: Fix dump_stack when PYTHON_LLTRACE=4 (GH-139384) --- Include/internal/pycore_stackref.h | 6 ++++++ Python/ceval.c | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/Include/internal/pycore_stackref.h b/Include/internal/pycore_stackref.h index 062834368bc..efe9fb3b6c7 100644 --- a/Include/internal/pycore_stackref.h +++ b/Include/internal/pycore_stackref.h @@ -296,6 +296,12 @@ PyStackRef_IsError(_PyStackRef ref) return ref.bits == Py_TAG_INVALID; } +static inline bool +PyStackRef_IsMalformed(_PyStackRef ref) +{ + return (ref.bits & Py_TAG_BITS) == Py_TAG_INVALID; +} + static inline bool PyStackRef_IsValid(_PyStackRef ref) { diff --git a/Python/ceval.c b/Python/ceval.c index f48f412fab8..defd084db9a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -160,6 +160,10 @@ dump_item(_PyStackRef item) printf(""); return; } + if (PyStackRef_IsMalformed(item)) { + printf(""); + return; + } if (PyStackRef_IsTaggedInt(item)) { printf("%" PRId64, (int64_t)PyStackRef_UntagInt(item)); return; From d51be28876ac0715b6fc674ef41618d214021348 Mon Sep 17 00:00:00 2001 From: Alper Date: Wed, 22 Oct 2025 08:16:28 -0700 Subject: [PATCH 252/373] gh-116738: Add critical section to dbm/gdbm context manager (gh-140391) --- Modules/_dbmmodule.c | 6 +++++- Modules/_gdbmmodule.c | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index 06712015418..f88861fa244 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -515,8 +515,12 @@ dbm__enter__(PyObject *self, PyObject *Py_UNUSED(dummy)) static PyObject * dbm__exit__(PyObject *self, PyObject *Py_UNUSED(args)) { + PyObject *result; dbmobject *dp = dbmobject_CAST(self); - return _dbm_dbm_close_impl(dp); + Py_BEGIN_CRITICAL_SECTION(self); + result = _dbm_dbm_close_impl(dp); + Py_END_CRITICAL_SECTION(); + return result; } static PyMethodDef dbm_methods[] = { diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index a6e0662ae74..72f568ceb06 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -690,7 +690,11 @@ gdbm__enter__(PyObject *self, PyObject *args) static PyObject * gdbm__exit__(PyObject *self, PyObject *args) { - return _gdbm_gdbm_close_impl((gdbmobject *)self); + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(self); + result = _gdbm_gdbm_close_impl((gdbmobject *)self); + Py_END_CRITICAL_SECTION(); + return result; } static PyMethodDef gdbm_methods[] = { From d2f3cfd38457204a2c47162de6f89ee3dd22fa99 Mon Sep 17 00:00:00 2001 From: Jakob Date: Wed, 22 Oct 2025 18:15:26 +0200 Subject: [PATCH 253/373] gh-140448: Default `suggest_on_error` to `True` in `argparse.ArgumentParser` (#140450) --- Doc/library/argparse.rst | 31 +++++++++---------- Doc/whatsnew/3.15.rst | 7 +++++ Lib/argparse.py | 4 +-- Lib/test/test_argparse.py | 17 +++++----- Misc/ACKS | 1 + ...-10-22-12-56-57.gh-issue-140448.GsEkXD.rst | 2 ++ 6 files changed, 36 insertions(+), 26 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-22-12-56-57.gh-issue-140448.GsEkXD.rst diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index a7603ac2726..418f514995d 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -74,7 +74,7 @@ ArgumentParser objects prefix_chars='-', fromfile_prefix_chars=None, \ argument_default=None, conflict_handler='error', \ add_help=True, allow_abbrev=True, exit_on_error=True, \ - *, suggest_on_error=False, color=True) + *, suggest_on_error=True, color=True) Create a new :class:`ArgumentParser` object. All parameters should be passed as keyword arguments. Each parameter has its own more detailed description @@ -117,7 +117,7 @@ ArgumentParser objects error info when an error occurs. (default: ``True``) * suggest_on_error_ - Enables suggestions for mistyped argument choices - and subparser names (default: ``False``) + and subparser names (default: ``True``) * color_ - Allow color output (default: ``True``) @@ -134,6 +134,9 @@ ArgumentParser objects .. versionchanged:: 3.14 *suggest_on_error* and *color* parameters were added. + .. versionchanged:: 3.15 + *suggest_on_error* default changed to ``True``. + The following sections describe how each of these are used. @@ -596,13 +599,11 @@ suggest_on_error ^^^^^^^^^^^^^^^^ By default, when a user passes an invalid argument choice or subparser name, -:class:`ArgumentParser` will exit with error info and list the permissible -argument choices (if specified) or subparser names as part of the error message. - -If the user would like to enable suggestions for mistyped argument choices and -subparser names, the feature can be enabled by setting ``suggest_on_error`` to -``True``. Note that this only applies for arguments when the choices specified -are strings:: +:class:`ArgumentParser` will exit with error info and provide suggestions for +mistyped arguments. The error message will list the permissible argument +choices (if specified) or subparser names, along with a "maybe you meant" +suggestion if a close match is found. Note that this only applies for arguments +when the choices specified are strings:: >>> parser = argparse.ArgumentParser(description='Process some integers.', suggest_on_error=True) @@ -612,16 +613,14 @@ are strings:: >>> parser.parse_args(['--action', 'sumn', 1, 2, 3]) tester.py: error: argument --action: invalid choice: 'sumn', maybe you meant 'sum'? (choose from 'sum', 'max') -If you're writing code that needs to be compatible with older Python versions -and want to opportunistically use ``suggest_on_error`` when it's available, you -can set it as an attribute after initializing the parser instead of using the -keyword argument:: +You can disable suggestions by setting ``suggest_on_error`` to ``False``:: - >>> parser = argparse.ArgumentParser(description='Process some integers.') - >>> parser.suggest_on_error = True + >>> parser = argparse.ArgumentParser(description='Process some integers.', + suggest_on_error=False) .. versionadded:: 3.14 - +.. versionchanged:: 3.15 + Changed default value of ``suggest_on_error`` from ``False`` to ``True``. color ^^^^^ diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 22e9cfde06f..a9543bdd13e 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -317,6 +317,13 @@ New modules Improved modules ================ +argparse +-------- + +* Changed the *suggest_on_error* parameter of :class:`argparse.ArgumentParser` to + default to ``True``. This enables suggestions for mistyped arguments by default. + (Contributed by Jakob Schluse in :gh:`140450`.) + calendar -------- diff --git a/Lib/argparse.py b/Lib/argparse.py index 1ddb7abbb35..1f4413a9897 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -1857,7 +1857,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): - exit_on_error -- Determines whether or not ArgumentParser exits with error info when an error occurs - suggest_on_error - Enables suggestions for mistyped argument choices - and subparser names (default: ``False``) + and subparser names (default: ``True``) - color - Allow color output in help messages (default: ``False``) """ @@ -1876,7 +1876,7 @@ def __init__(self, allow_abbrev=True, exit_on_error=True, *, - suggest_on_error=False, + suggest_on_error=True, color=True, ): superinit = super(ArgumentParser, self).__init__ diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index e5d642cde66..d6c9c1ef2c8 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -2287,7 +2287,7 @@ class TestArgumentAndSubparserSuggestions(TestCase): """Test error handling and suggestion when a user makes a typo""" def test_wrong_argument_error_with_suggestions(self): - parser = ErrorRaisingArgumentParser(suggest_on_error=True) + parser = ErrorRaisingArgumentParser() parser.add_argument('foo', choices=['bar', 'baz']) with self.assertRaises(ArgumentParserError) as excinfo: parser.parse_args(('bazz',)) @@ -2307,7 +2307,7 @@ def test_wrong_argument_error_no_suggestions(self): ) def test_wrong_argument_subparsers_with_suggestions(self): - parser = ErrorRaisingArgumentParser(suggest_on_error=True) + parser = ErrorRaisingArgumentParser() subparsers = parser.add_subparsers(required=True) subparsers.add_parser('foo') subparsers.add_parser('bar') @@ -2331,18 +2331,19 @@ def test_wrong_argument_subparsers_no_suggestions(self): excinfo.exception.stderr, ) - def test_wrong_argument_no_suggestion_implicit(self): - parser = ErrorRaisingArgumentParser() + def test_wrong_argument_with_suggestion_explicit(self): + parser = ErrorRaisingArgumentParser(suggest_on_error=True) parser.add_argument('foo', choices=['bar', 'baz']) with self.assertRaises(ArgumentParserError) as excinfo: parser.parse_args(('bazz',)) self.assertIn( - "error: argument foo: invalid choice: 'bazz' (choose from bar, baz)", + "error: argument foo: invalid choice: 'bazz', maybe you meant" + " 'baz'? (choose from bar, baz)", excinfo.exception.stderr, ) def test_suggestions_choices_empty(self): - parser = ErrorRaisingArgumentParser(suggest_on_error=True) + parser = ErrorRaisingArgumentParser() parser.add_argument('foo', choices=[]) with self.assertRaises(ArgumentParserError) as excinfo: parser.parse_args(('bazz',)) @@ -2352,7 +2353,7 @@ def test_suggestions_choices_empty(self): ) def test_suggestions_choices_int(self): - parser = ErrorRaisingArgumentParser(suggest_on_error=True) + parser = ErrorRaisingArgumentParser() parser.add_argument('foo', choices=[1, 2]) with self.assertRaises(ArgumentParserError) as excinfo: parser.parse_args(('3',)) @@ -2362,7 +2363,7 @@ def test_suggestions_choices_int(self): ) def test_suggestions_choices_mixed_types(self): - parser = ErrorRaisingArgumentParser(suggest_on_error=True) + parser = ErrorRaisingArgumentParser() parser.add_argument('foo', choices=[1, '2']) with self.assertRaises(ArgumentParserError) as excinfo: parser.parse_args(('3',)) diff --git a/Misc/ACKS b/Misc/ACKS index 2dc513829a2..6876380e0ba 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1681,6 +1681,7 @@ David Scherer Wolfgang Scherer Felix Scherz Hynek Schlawack +Jakob Schluse Bob Schmertz Gregor Schmid Ralf Schmitt diff --git a/Misc/NEWS.d/next/Library/2025-10-22-12-56-57.gh-issue-140448.GsEkXD.rst b/Misc/NEWS.d/next/Library/2025-10-22-12-56-57.gh-issue-140448.GsEkXD.rst new file mode 100644 index 00000000000..db7f92e136d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-22-12-56-57.gh-issue-140448.GsEkXD.rst @@ -0,0 +1,2 @@ +Change the default of ``suggest_on_error`` to ``True`` in +``argparse.ArgumentParser``. From 76fea5596c235a7853cda8df87c3998d506e950c Mon Sep 17 00:00:00 2001 From: Stefano Rivera Date: Wed, 22 Oct 2025 09:36:12 -0700 Subject: [PATCH 254/373] gh-130317: Fix SNaN broken tests on HP PA RISC (#140452) While looking at #140028, I found some unrelated test regressions in the 3.14 cycle. These seem to all come from #130317. From what I can tell, that made Python more correct than it was before. According to [0], HP PA RISC uses 1 for SNaN and thus a 0 for QNaN. [0]: https://grouper.ieee.org/groups/1788/email/msg03272.html --- Lib/test/test_capi/test_float.py | 5 +++++ Lib/test/test_struct.py | 12 ++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_capi/test_float.py b/Lib/test/test_capi/test_float.py index f7efe0d0254..983b991b4f1 100644 --- a/Lib/test/test_capi/test_float.py +++ b/Lib/test/test_capi/test_float.py @@ -1,5 +1,6 @@ import math import random +import platform import sys import unittest import warnings @@ -197,6 +198,10 @@ def test_pack_unpack_roundtrip_for_nans(self): # PyFloat_Pack/Unpack*() API. See also gh-130317 and # e.g. https://developercommunity.visualstudio.com/t/155064 signaling = 0 + if platform.machine().startswith('parisc'): + # HP PA RISC uses 0 for quiet, see: + # https://en.wikipedia.org/wiki/NaN#Encoding + signaling = 1 quiet = int(not signaling) if size == 8: payload = random.randint(signaling, 0x7ffffffffffff) diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index 7df01f28f09..75c76a36ee9 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -5,6 +5,7 @@ import math import operator import unittest +import platform import struct import sys import weakref @@ -917,10 +918,17 @@ def test_half_float(self): # Check that packing produces a bit pattern representing a quiet NaN: # all exponent bits and the msb of the fraction should all be 1. + if platform.machine().startswith('parisc'): + # HP PA RISC uses 0 for quiet, see: + # https://en.wikipedia.org/wiki/NaN#Encoding + expected = 0x7c + else: + expected = 0x7e + packed = struct.pack(' Date: Wed, 22 Oct 2025 20:29:14 +0200 Subject: [PATCH 255/373] gh-140253: Improve the syntax error from an ill-positioned double-star subpattern (#140254) --- Grammar/python.gram | 5 + Lib/test/test_exceptions.py | 11 +- Lib/test/test_syntax.py | 26 +- ...-10-17-14-38-10.gh-issue-140253.gCqFaL.rst | 2 + Parser/parser.c | 1643 +++++++++-------- 5 files changed, 922 insertions(+), 765 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-14-38-10.gh-issue-140253.gCqFaL.rst diff --git a/Grammar/python.gram b/Grammar/python.gram index b9ecd2273a5..1aa544d9ef4 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -626,6 +626,7 @@ mapping_pattern[pattern_ty]: CHECK(asdl_pattern_seq*, _PyPegen_get_patterns(p, items)), NULL, EXTRA) } + | invalid_mapping_pattern items_pattern[asdl_seq*]: | ','.key_value_pattern+ @@ -1490,6 +1491,10 @@ invalid_class_pattern: PyPegen_first_item(a, pattern_ty), PyPegen_last_item(a, pattern_ty), "positional patterns follow keyword patterns") } +invalid_mapping_pattern: + | '{' (items_pattern ',')? rest=double_star_pattern ',' items_pattern ','? '}' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION( + rest, + "double star pattern must be the last (right-most) subpattern in the mapping pattern") } invalid_class_argument_pattern[asdl_pattern_seq*]: | [positional_patterns ','] keyword_patterns ',' a=positional_patterns { a } invalid_if_stmt: diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 323a8c401bd..5262b58908a 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -252,7 +252,16 @@ def testSyntaxErrorOffset(self): check('[\nfile\nfor str(file)\nin\n[]\n]', 3, 5) check('[file for\n str(file) in []]', 2, 2) check("ages = {'Alice'=22, 'Bob'=23}", 1, 9) - check('match ...:\n case {**rest, "key": value}:\n ...', 2, 19) + check(dedent("""\ + match ...: + case {**rest1, "after": after}: + ... + """), 2, 11) + check(dedent("""\ + match ...: + case {"before": before, **rest2, "after": after}: + ... + """), 2, 29) check("[a b c d e f]", 1, 2) check("for x yfff:", 1, 7) check("f(a for a in b, c)", 1, 3, 1, 15) diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index e334f48179e..ecb9e077137 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -370,12 +370,6 @@ Traceback (most recent call last): SyntaxError: invalid syntax ->>> match ...: -... case {**rest, "key": value}: -... ... -Traceback (most recent call last): -SyntaxError: invalid syntax - >>> match ...: ... case {**_}: ... ... @@ -2240,7 +2234,7 @@ Traceback (most recent call last): SyntaxError: invalid character '£' (U+00A3) - Invalid pattern matching constructs: +Invalid pattern matching constructs: >>> match ...: ... case 42 as _: @@ -2302,6 +2296,24 @@ Traceback (most recent call last): SyntaxError: positional patterns follow keyword patterns + >>> match ...: + ... case {**double_star, "spam": "eggs"}: + ... ... + Traceback (most recent call last): + SyntaxError: double star pattern must be the last (right-most) subpattern in the mapping pattern + + >>> match ...: + ... case {"foo": 1, **double_star, "spam": "eggs"}: + ... ... + Traceback (most recent call last): + SyntaxError: double star pattern must be the last (right-most) subpattern in the mapping pattern + + >>> match ...: + ... case {"spam": "eggs", "b": {**d, "ham": "bacon"}}: + ... ... + Traceback (most recent call last): + SyntaxError: double star pattern must be the last (right-most) subpattern in the mapping pattern + Uses of the star operator which should fail: A[:*b] diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-14-38-10.gh-issue-140253.gCqFaL.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-14-38-10.gh-issue-140253.gCqFaL.rst new file mode 100644 index 00000000000..955dcac2e01 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-14-38-10.gh-issue-140253.gCqFaL.rst @@ -0,0 +1,2 @@ +Wrong placement of a double-star pattern inside a mapping pattern now throws a specialized syntax error. +Contributed by Bartosz Sławecki in :gh:`140253`. diff --git a/Parser/parser.c b/Parser/parser.c index 8242c4dfabb..7a3102bb141 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -334,202 +334,204 @@ static char *soft_keywords[] = { #define invalid_case_block_type 1247 #define invalid_as_pattern_type 1248 #define invalid_class_pattern_type 1249 -#define invalid_class_argument_pattern_type 1250 -#define invalid_if_stmt_type 1251 -#define invalid_elif_stmt_type 1252 -#define invalid_else_stmt_type 1253 -#define invalid_while_stmt_type 1254 -#define invalid_for_stmt_type 1255 -#define invalid_def_raw_type 1256 -#define invalid_class_def_raw_type 1257 -#define invalid_double_starred_kvpairs_type 1258 -#define invalid_kvpair_type 1259 -#define invalid_starred_expression_unpacking_type 1260 -#define invalid_starred_expression_type 1261 -#define invalid_fstring_replacement_field_type 1262 -#define invalid_fstring_conversion_character_type 1263 -#define invalid_tstring_replacement_field_type 1264 -#define invalid_tstring_conversion_character_type 1265 -#define invalid_string_tstring_concat_type 1266 -#define invalid_arithmetic_type 1267 -#define invalid_factor_type 1268 -#define invalid_type_params_type 1269 -#define _loop0_1_type 1270 -#define _loop1_2_type 1271 -#define _loop0_3_type 1272 -#define _gather_4_type 1273 -#define _tmp_5_type 1274 -#define _tmp_6_type 1275 -#define _tmp_7_type 1276 -#define _tmp_8_type 1277 -#define _tmp_9_type 1278 -#define _tmp_10_type 1279 -#define _tmp_11_type 1280 -#define _loop1_12_type 1281 -#define _loop0_13_type 1282 -#define _gather_14_type 1283 -#define _tmp_15_type 1284 -#define _tmp_16_type 1285 -#define _loop0_17_type 1286 -#define _loop1_18_type 1287 -#define _loop0_19_type 1288 -#define _gather_20_type 1289 -#define _tmp_21_type 1290 -#define _loop0_22_type 1291 -#define _gather_23_type 1292 -#define _loop1_24_type 1293 -#define _tmp_25_type 1294 -#define _tmp_26_type 1295 -#define _loop0_27_type 1296 -#define _loop0_28_type 1297 -#define _loop1_29_type 1298 -#define _loop1_30_type 1299 -#define _loop0_31_type 1300 -#define _loop1_32_type 1301 -#define _loop0_33_type 1302 -#define _gather_34_type 1303 -#define _tmp_35_type 1304 -#define _loop1_36_type 1305 -#define _loop1_37_type 1306 -#define _loop1_38_type 1307 -#define _loop0_39_type 1308 -#define _gather_40_type 1309 -#define _tmp_41_type 1310 -#define _tmp_42_type 1311 -#define _tmp_43_type 1312 -#define _loop0_44_type 1313 -#define _gather_45_type 1314 -#define _loop0_46_type 1315 -#define _gather_47_type 1316 -#define _tmp_48_type 1317 -#define _loop0_49_type 1318 -#define _gather_50_type 1319 -#define _loop0_51_type 1320 -#define _gather_52_type 1321 -#define _loop0_53_type 1322 -#define _gather_54_type 1323 -#define _loop1_55_type 1324 -#define _loop1_56_type 1325 -#define _loop0_57_type 1326 -#define _gather_58_type 1327 -#define _loop1_59_type 1328 -#define _loop1_60_type 1329 -#define _loop1_61_type 1330 -#define _tmp_62_type 1331 -#define _loop0_63_type 1332 -#define _gather_64_type 1333 -#define _tmp_65_type 1334 -#define _tmp_66_type 1335 -#define _tmp_67_type 1336 -#define _tmp_68_type 1337 -#define _tmp_69_type 1338 -#define _loop0_70_type 1339 -#define _loop0_71_type 1340 -#define _loop1_72_type 1341 -#define _loop1_73_type 1342 -#define _loop0_74_type 1343 -#define _loop1_75_type 1344 -#define _loop0_76_type 1345 -#define _loop0_77_type 1346 -#define _loop0_78_type 1347 -#define _loop0_79_type 1348 -#define _loop1_80_type 1349 -#define _loop1_81_type 1350 -#define _tmp_82_type 1351 -#define _loop0_83_type 1352 -#define _gather_84_type 1353 -#define _loop1_85_type 1354 -#define _loop0_86_type 1355 -#define _tmp_87_type 1356 -#define _loop0_88_type 1357 -#define _gather_89_type 1358 -#define _tmp_90_type 1359 -#define _loop0_91_type 1360 -#define _gather_92_type 1361 -#define _loop0_93_type 1362 -#define _gather_94_type 1363 -#define _loop0_95_type 1364 -#define _loop0_96_type 1365 -#define _gather_97_type 1366 -#define _loop1_98_type 1367 -#define _tmp_99_type 1368 -#define _loop0_100_type 1369 -#define _gather_101_type 1370 -#define _loop0_102_type 1371 -#define _gather_103_type 1372 -#define _tmp_104_type 1373 -#define _tmp_105_type 1374 -#define _loop0_106_type 1375 -#define _gather_107_type 1376 -#define _tmp_108_type 1377 -#define _tmp_109_type 1378 -#define _tmp_110_type 1379 -#define _tmp_111_type 1380 -#define _tmp_112_type 1381 -#define _loop1_113_type 1382 -#define _tmp_114_type 1383 -#define _tmp_115_type 1384 -#define _tmp_116_type 1385 -#define _tmp_117_type 1386 -#define _tmp_118_type 1387 -#define _loop0_119_type 1388 -#define _loop0_120_type 1389 -#define _tmp_121_type 1390 -#define _tmp_122_type 1391 -#define _tmp_123_type 1392 -#define _tmp_124_type 1393 -#define _tmp_125_type 1394 -#define _tmp_126_type 1395 -#define _tmp_127_type 1396 -#define _tmp_128_type 1397 -#define _tmp_129_type 1398 -#define _loop0_130_type 1399 -#define _gather_131_type 1400 -#define _tmp_132_type 1401 -#define _tmp_133_type 1402 -#define _tmp_134_type 1403 -#define _tmp_135_type 1404 -#define _loop0_136_type 1405 -#define _gather_137_type 1406 -#define _tmp_138_type 1407 -#define _loop0_139_type 1408 -#define _gather_140_type 1409 -#define _loop0_141_type 1410 -#define _gather_142_type 1411 -#define _tmp_143_type 1412 -#define _loop0_144_type 1413 -#define _tmp_145_type 1414 -#define _tmp_146_type 1415 -#define _tmp_147_type 1416 -#define _tmp_148_type 1417 -#define _tmp_149_type 1418 -#define _tmp_150_type 1419 -#define _tmp_151_type 1420 -#define _tmp_152_type 1421 -#define _tmp_153_type 1422 -#define _tmp_154_type 1423 -#define _tmp_155_type 1424 -#define _tmp_156_type 1425 -#define _tmp_157_type 1426 -#define _tmp_158_type 1427 -#define _tmp_159_type 1428 -#define _tmp_160_type 1429 -#define _tmp_161_type 1430 -#define _tmp_162_type 1431 -#define _tmp_163_type 1432 -#define _tmp_164_type 1433 -#define _tmp_165_type 1434 -#define _tmp_166_type 1435 -#define _tmp_167_type 1436 -#define _tmp_168_type 1437 -#define _tmp_169_type 1438 -#define _tmp_170_type 1439 -#define _loop0_171_type 1440 -#define _tmp_172_type 1441 -#define _tmp_173_type 1442 -#define _tmp_174_type 1443 -#define _tmp_175_type 1444 -#define _tmp_176_type 1445 +#define invalid_mapping_pattern_type 1250 +#define invalid_class_argument_pattern_type 1251 +#define invalid_if_stmt_type 1252 +#define invalid_elif_stmt_type 1253 +#define invalid_else_stmt_type 1254 +#define invalid_while_stmt_type 1255 +#define invalid_for_stmt_type 1256 +#define invalid_def_raw_type 1257 +#define invalid_class_def_raw_type 1258 +#define invalid_double_starred_kvpairs_type 1259 +#define invalid_kvpair_type 1260 +#define invalid_starred_expression_unpacking_type 1261 +#define invalid_starred_expression_type 1262 +#define invalid_fstring_replacement_field_type 1263 +#define invalid_fstring_conversion_character_type 1264 +#define invalid_tstring_replacement_field_type 1265 +#define invalid_tstring_conversion_character_type 1266 +#define invalid_string_tstring_concat_type 1267 +#define invalid_arithmetic_type 1268 +#define invalid_factor_type 1269 +#define invalid_type_params_type 1270 +#define _loop0_1_type 1271 +#define _loop1_2_type 1272 +#define _loop0_3_type 1273 +#define _gather_4_type 1274 +#define _tmp_5_type 1275 +#define _tmp_6_type 1276 +#define _tmp_7_type 1277 +#define _tmp_8_type 1278 +#define _tmp_9_type 1279 +#define _tmp_10_type 1280 +#define _tmp_11_type 1281 +#define _loop1_12_type 1282 +#define _loop0_13_type 1283 +#define _gather_14_type 1284 +#define _tmp_15_type 1285 +#define _tmp_16_type 1286 +#define _loop0_17_type 1287 +#define _loop1_18_type 1288 +#define _loop0_19_type 1289 +#define _gather_20_type 1290 +#define _tmp_21_type 1291 +#define _loop0_22_type 1292 +#define _gather_23_type 1293 +#define _loop1_24_type 1294 +#define _tmp_25_type 1295 +#define _tmp_26_type 1296 +#define _loop0_27_type 1297 +#define _loop0_28_type 1298 +#define _loop1_29_type 1299 +#define _loop1_30_type 1300 +#define _loop0_31_type 1301 +#define _loop1_32_type 1302 +#define _loop0_33_type 1303 +#define _gather_34_type 1304 +#define _tmp_35_type 1305 +#define _loop1_36_type 1306 +#define _loop1_37_type 1307 +#define _loop1_38_type 1308 +#define _loop0_39_type 1309 +#define _gather_40_type 1310 +#define _tmp_41_type 1311 +#define _tmp_42_type 1312 +#define _tmp_43_type 1313 +#define _loop0_44_type 1314 +#define _gather_45_type 1315 +#define _loop0_46_type 1316 +#define _gather_47_type 1317 +#define _tmp_48_type 1318 +#define _loop0_49_type 1319 +#define _gather_50_type 1320 +#define _loop0_51_type 1321 +#define _gather_52_type 1322 +#define _loop0_53_type 1323 +#define _gather_54_type 1324 +#define _loop1_55_type 1325 +#define _loop1_56_type 1326 +#define _loop0_57_type 1327 +#define _gather_58_type 1328 +#define _loop1_59_type 1329 +#define _loop1_60_type 1330 +#define _loop1_61_type 1331 +#define _tmp_62_type 1332 +#define _loop0_63_type 1333 +#define _gather_64_type 1334 +#define _tmp_65_type 1335 +#define _tmp_66_type 1336 +#define _tmp_67_type 1337 +#define _tmp_68_type 1338 +#define _tmp_69_type 1339 +#define _loop0_70_type 1340 +#define _loop0_71_type 1341 +#define _loop1_72_type 1342 +#define _loop1_73_type 1343 +#define _loop0_74_type 1344 +#define _loop1_75_type 1345 +#define _loop0_76_type 1346 +#define _loop0_77_type 1347 +#define _loop0_78_type 1348 +#define _loop0_79_type 1349 +#define _loop1_80_type 1350 +#define _loop1_81_type 1351 +#define _tmp_82_type 1352 +#define _loop0_83_type 1353 +#define _gather_84_type 1354 +#define _loop1_85_type 1355 +#define _loop0_86_type 1356 +#define _tmp_87_type 1357 +#define _loop0_88_type 1358 +#define _gather_89_type 1359 +#define _tmp_90_type 1360 +#define _loop0_91_type 1361 +#define _gather_92_type 1362 +#define _loop0_93_type 1363 +#define _gather_94_type 1364 +#define _loop0_95_type 1365 +#define _loop0_96_type 1366 +#define _gather_97_type 1367 +#define _loop1_98_type 1368 +#define _tmp_99_type 1369 +#define _loop0_100_type 1370 +#define _gather_101_type 1371 +#define _loop0_102_type 1372 +#define _gather_103_type 1373 +#define _tmp_104_type 1374 +#define _tmp_105_type 1375 +#define _loop0_106_type 1376 +#define _gather_107_type 1377 +#define _tmp_108_type 1378 +#define _tmp_109_type 1379 +#define _tmp_110_type 1380 +#define _tmp_111_type 1381 +#define _tmp_112_type 1382 +#define _loop1_113_type 1383 +#define _tmp_114_type 1384 +#define _tmp_115_type 1385 +#define _tmp_116_type 1386 +#define _tmp_117_type 1387 +#define _tmp_118_type 1388 +#define _loop0_119_type 1389 +#define _loop0_120_type 1390 +#define _tmp_121_type 1391 +#define _tmp_122_type 1392 +#define _tmp_123_type 1393 +#define _tmp_124_type 1394 +#define _tmp_125_type 1395 +#define _tmp_126_type 1396 +#define _tmp_127_type 1397 +#define _tmp_128_type 1398 +#define _tmp_129_type 1399 +#define _loop0_130_type 1400 +#define _gather_131_type 1401 +#define _tmp_132_type 1402 +#define _tmp_133_type 1403 +#define _tmp_134_type 1404 +#define _tmp_135_type 1405 +#define _loop0_136_type 1406 +#define _gather_137_type 1407 +#define _tmp_138_type 1408 +#define _loop0_139_type 1409 +#define _gather_140_type 1410 +#define _loop0_141_type 1411 +#define _gather_142_type 1412 +#define _tmp_143_type 1413 +#define _loop0_144_type 1414 +#define _tmp_145_type 1415 +#define _tmp_146_type 1416 +#define _tmp_147_type 1417 +#define _tmp_148_type 1418 +#define _tmp_149_type 1419 +#define _tmp_150_type 1420 +#define _tmp_151_type 1421 +#define _tmp_152_type 1422 +#define _tmp_153_type 1423 +#define _tmp_154_type 1424 +#define _tmp_155_type 1425 +#define _tmp_156_type 1426 +#define _tmp_157_type 1427 +#define _tmp_158_type 1428 +#define _tmp_159_type 1429 +#define _tmp_160_type 1430 +#define _tmp_161_type 1431 +#define _tmp_162_type 1432 +#define _tmp_163_type 1433 +#define _tmp_164_type 1434 +#define _tmp_165_type 1435 +#define _tmp_166_type 1436 +#define _tmp_167_type 1437 +#define _tmp_168_type 1438 +#define _tmp_169_type 1439 +#define _tmp_170_type 1440 +#define _tmp_171_type 1441 +#define _loop0_172_type 1442 +#define _tmp_173_type 1443 +#define _tmp_174_type 1444 +#define _tmp_175_type 1445 +#define _tmp_176_type 1446 +#define _tmp_177_type 1447 static mod_ty file_rule(Parser *p); static mod_ty interactive_rule(Parser *p); @@ -781,6 +783,7 @@ static void *invalid_match_stmt_rule(Parser *p); static void *invalid_case_block_rule(Parser *p); static void *invalid_as_pattern_rule(Parser *p); static void *invalid_class_pattern_rule(Parser *p); +static void *invalid_mapping_pattern_rule(Parser *p); static asdl_pattern_seq* invalid_class_argument_pattern_rule(Parser *p); static void *invalid_if_stmt_rule(Parser *p); static void *invalid_elif_stmt_rule(Parser *p); @@ -971,12 +974,13 @@ static void *_tmp_167_rule(Parser *p); static void *_tmp_168_rule(Parser *p); static void *_tmp_169_rule(Parser *p); static void *_tmp_170_rule(Parser *p); -static asdl_seq *_loop0_171_rule(Parser *p); -static void *_tmp_172_rule(Parser *p); +static void *_tmp_171_rule(Parser *p); +static asdl_seq *_loop0_172_rule(Parser *p); static void *_tmp_173_rule(Parser *p); static void *_tmp_174_rule(Parser *p); static void *_tmp_175_rule(Parser *p); static void *_tmp_176_rule(Parser *p); +static void *_tmp_177_rule(Parser *p); // file: statements? $ @@ -10093,6 +10097,7 @@ star_pattern_rule(Parser *p) // | '{' double_star_pattern ','? '}' // | '{' items_pattern ',' double_star_pattern ','? '}' // | '{' items_pattern ','? '}' +// | invalid_mapping_pattern static pattern_ty mapping_pattern_rule(Parser *p) { @@ -10285,6 +10290,25 @@ mapping_pattern_rule(Parser *p) D(fprintf(stderr, "%*c%s mapping_pattern[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' items_pattern ','? '}'")); } + if (p->call_invalid_rules) { // invalid_mapping_pattern + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> mapping_pattern[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_mapping_pattern")); + void *invalid_mapping_pattern_var; + if ( + (invalid_mapping_pattern_var = invalid_mapping_pattern_rule(p)) // invalid_mapping_pattern + ) + { + D(fprintf(stderr, "%*c+ mapping_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_mapping_pattern")); + _res = invalid_mapping_pattern_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s mapping_pattern[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_mapping_pattern")); + } _res = NULL; done: p->level--; @@ -25435,6 +25459,70 @@ invalid_class_pattern_rule(Parser *p) return _res; } +// invalid_mapping_pattern: +// | '{' [(items_pattern ',')] double_star_pattern ',' items_pattern ','? '}' +static void * +invalid_mapping_pattern_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // '{' [(items_pattern ',')] double_star_pattern ',' items_pattern ','? '}' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_mapping_pattern[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' [(items_pattern ',')] double_star_pattern ',' items_pattern ','? '}'")); + Token * _literal; + Token * _literal_1; + Token * _literal_2; + void *_opt_var; + UNUSED(_opt_var); // Silence compiler warnings + void *_opt_var_1; + UNUSED(_opt_var_1); // Silence compiler warnings + asdl_seq* items_pattern_var; + expr_ty rest; + if ( + (_literal = _PyPegen_expect_token(p, 25)) // token='{' + && + (_opt_var = _tmp_147_rule(p), !p->error_indicator) // [(items_pattern ',')] + && + (rest = double_star_pattern_rule(p)) // double_star_pattern + && + (_literal_1 = _PyPegen_expect_token(p, 12)) // token=',' + && + (items_pattern_var = items_pattern_rule(p)) // items_pattern + && + (_opt_var_1 = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? + && + (_literal_2 = _PyPegen_expect_token(p, 26)) // token='}' + ) + { + D(fprintf(stderr, "%*c+ invalid_mapping_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' [(items_pattern ',')] double_star_pattern ',' items_pattern ','? '}'")); + _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( rest , "double star pattern must be the last (right-most) subpattern in the mapping pattern" ); + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_mapping_pattern[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' [(items_pattern ',')] double_star_pattern ',' items_pattern ','? '}'")); + } + _res = NULL; + done: + p->level--; + return _res; +} + // invalid_class_argument_pattern: // | [positional_patterns ','] keyword_patterns ',' positional_patterns static asdl_pattern_seq* @@ -25461,7 +25549,7 @@ invalid_class_argument_pattern_rule(Parser *p) asdl_pattern_seq* a; asdl_seq* keyword_patterns_var; if ( - (_opt_var = _tmp_147_rule(p), !p->error_indicator) // [positional_patterns ','] + (_opt_var = _tmp_148_rule(p), !p->error_indicator) // [positional_patterns ','] && (keyword_patterns_var = keyword_patterns_rule(p)) // keyword_patterns && @@ -26258,7 +26346,7 @@ invalid_double_starred_kvpairs_rule(Parser *p) && (a = _PyPegen_expect_token(p, 11)) // token=':' && - _PyPegen_lookahead(1, _tmp_148_rule, p) + _PyPegen_lookahead(1, _tmp_149_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_double_starred_kvpairs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' &('}' | ',')")); @@ -26368,7 +26456,7 @@ invalid_kvpair_rule(Parser *p) && (a = _PyPegen_expect_token(p, 11)) // token=':' && - _PyPegen_lookahead(1, _tmp_148_rule, p) + _PyPegen_lookahead(1, _tmp_149_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_kvpair[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' &('}' | ',')")); @@ -26656,7 +26744,7 @@ invalid_fstring_replacement_field_rule(Parser *p) && (annotated_rhs_var = annotated_rhs_rule(p)) // annotated_rhs && - _PyPegen_lookahead(0, _tmp_149_rule, p) + _PyPegen_lookahead(0, _tmp_150_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs !('=' | '!' | ':' | '}')")); @@ -26688,7 +26776,7 @@ invalid_fstring_replacement_field_rule(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 22)) // token='=' && - _PyPegen_lookahead(0, _tmp_150_rule, p) + _PyPegen_lookahead(0, _tmp_151_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '=' !('!' | ':' | '}')")); @@ -26752,9 +26840,9 @@ invalid_fstring_replacement_field_rule(Parser *p) && (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator) // '='? && - (_opt_var_1 = _tmp_151_rule(p), !p->error_indicator) // ['!' NAME] + (_opt_var_1 = _tmp_152_rule(p), !p->error_indicator) // ['!' NAME] && - _PyPegen_lookahead(0, _tmp_152_rule, p) + _PyPegen_lookahead(0, _tmp_153_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !(':' | '}')")); @@ -26791,7 +26879,7 @@ invalid_fstring_replacement_field_rule(Parser *p) && (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator) // '='? && - (_opt_var_1 = _tmp_151_rule(p), !p->error_indicator) // ['!' NAME] + (_opt_var_1 = _tmp_152_rule(p), !p->error_indicator) // ['!' NAME] && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' && @@ -26832,7 +26920,7 @@ invalid_fstring_replacement_field_rule(Parser *p) && (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator) // '='? && - (_opt_var_1 = _tmp_151_rule(p), !p->error_indicator) // ['!' NAME] + (_opt_var_1 = _tmp_152_rule(p), !p->error_indicator) // ['!' NAME] && _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 26) // token='}' ) @@ -26879,7 +26967,7 @@ invalid_fstring_conversion_character_rule(Parser *p) if ( (_literal = _PyPegen_expect_token(p, 54)) // token='!' && - _PyPegen_lookahead(1, _tmp_152_rule, p) + _PyPegen_lookahead(1, _tmp_153_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_fstring_conversion_character[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' &(':' | '}')")); @@ -27098,7 +27186,7 @@ invalid_tstring_replacement_field_rule(Parser *p) && (annotated_rhs_var = annotated_rhs_rule(p)) // annotated_rhs && - _PyPegen_lookahead(0, _tmp_149_rule, p) + _PyPegen_lookahead(0, _tmp_150_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs !('=' | '!' | ':' | '}')")); @@ -27130,7 +27218,7 @@ invalid_tstring_replacement_field_rule(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 22)) // token='=' && - _PyPegen_lookahead(0, _tmp_150_rule, p) + _PyPegen_lookahead(0, _tmp_151_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '=' !('!' | ':' | '}')")); @@ -27194,9 +27282,9 @@ invalid_tstring_replacement_field_rule(Parser *p) && (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator) // '='? && - (_opt_var_1 = _tmp_151_rule(p), !p->error_indicator) // ['!' NAME] + (_opt_var_1 = _tmp_152_rule(p), !p->error_indicator) // ['!' NAME] && - _PyPegen_lookahead(0, _tmp_152_rule, p) + _PyPegen_lookahead(0, _tmp_153_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !(':' | '}')")); @@ -27233,7 +27321,7 @@ invalid_tstring_replacement_field_rule(Parser *p) && (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator) // '='? && - (_opt_var_1 = _tmp_151_rule(p), !p->error_indicator) // ['!' NAME] + (_opt_var_1 = _tmp_152_rule(p), !p->error_indicator) // ['!' NAME] && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' && @@ -27274,7 +27362,7 @@ invalid_tstring_replacement_field_rule(Parser *p) && (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator) // '='? && - (_opt_var_1 = _tmp_151_rule(p), !p->error_indicator) // ['!' NAME] + (_opt_var_1 = _tmp_152_rule(p), !p->error_indicator) // ['!' NAME] && _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 26) // token='}' ) @@ -27321,7 +27409,7 @@ invalid_tstring_conversion_character_rule(Parser *p) if ( (_literal = _PyPegen_expect_token(p, 54)) // token='!' && - _PyPegen_lookahead(1, _tmp_152_rule, p) + _PyPegen_lookahead(1, _tmp_153_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_tstring_conversion_character[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' &(':' | '}')")); @@ -27422,7 +27510,7 @@ invalid_string_tstring_concat_rule(Parser *p) if ( (a = _loop1_81_rule(p)) // tstring+ && - (b = (expr_ty)_tmp_153_rule(p)) // fstring | string + (b = (expr_ty)_tmp_154_rule(p)) // fstring | string ) { D(fprintf(stderr, "%*c+ invalid_string_tstring_concat[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tstring+ (fstring | string)")); @@ -27463,14 +27551,14 @@ invalid_arithmetic_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_arithmetic[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "sum ('+' | '-' | '*' | '/' | '%' | '//' | '@') 'not' inversion")); - void *_tmp_154_var; + void *_tmp_155_var; Token * a; expr_ty b; expr_ty sum_var; if ( (sum_var = sum_rule(p)) // sum && - (_tmp_154_var = _tmp_154_rule(p)) // '+' | '-' | '*' | '/' | '%' | '//' | '@' + (_tmp_155_var = _tmp_155_rule(p)) // '+' | '-' | '*' | '/' | '%' | '//' | '@' && (a = _PyPegen_expect_token(p, 712)) // token='not' && @@ -27515,11 +27603,11 @@ invalid_factor_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_factor[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('+' | '-' | '~') 'not' factor")); - void *_tmp_155_var; + void *_tmp_156_var; Token * a; expr_ty b; if ( - (_tmp_155_var = _tmp_155_rule(p)) // '+' | '-' | '~' + (_tmp_156_var = _tmp_156_rule(p)) // '+' | '-' | '~' && (a = _PyPegen_expect_token(p, 712)) // token='not' && @@ -28293,12 +28381,12 @@ _loop1_12_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_12[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); - void *_tmp_156_var; + void *_tmp_157_var; while ( - (_tmp_156_var = _tmp_156_rule(p)) // star_targets '=' + (_tmp_157_var = _tmp_157_rule(p)) // star_targets '=' ) { - _res = _tmp_156_var; + _res = _tmp_157_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -28585,12 +28673,12 @@ _loop0_17_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop0_17[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('.' | '...')")); - void *_tmp_157_var; + void *_tmp_158_var; while ( - (_tmp_157_var = _tmp_157_rule(p)) // '.' | '...' + (_tmp_158_var = _tmp_158_rule(p)) // '.' | '...' ) { - _res = _tmp_157_var; + _res = _tmp_158_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -28652,12 +28740,12 @@ _loop1_18_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_18[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('.' | '...')")); - void *_tmp_157_var; + void *_tmp_158_var; while ( - (_tmp_157_var = _tmp_157_rule(p)) // '.' | '...' + (_tmp_158_var = _tmp_158_rule(p)) // '.' | '...' ) { - _res = _tmp_157_var; + _res = _tmp_158_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -29004,12 +29092,12 @@ _loop1_24_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_24[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('@' named_expression NEWLINE)")); - void *_tmp_158_var; + void *_tmp_159_var; while ( - (_tmp_158_var = _tmp_158_rule(p)) // '@' named_expression NEWLINE + (_tmp_159_var = _tmp_159_rule(p)) // '@' named_expression NEWLINE ) { - _res = _tmp_158_var; + _res = _tmp_159_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -31037,12 +31125,12 @@ _loop1_56_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_56[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_expression)")); - void *_tmp_159_var; + void *_tmp_160_var; while ( - (_tmp_159_var = _tmp_159_rule(p)) // ',' star_expression + (_tmp_160_var = _tmp_160_rule(p)) // ',' star_expression ) { - _res = _tmp_159_var; + _res = _tmp_160_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -31226,12 +31314,12 @@ _loop1_59_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_59[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('or' conjunction)")); - void *_tmp_160_var; + void *_tmp_161_var; while ( - (_tmp_160_var = _tmp_160_rule(p)) // 'or' conjunction + (_tmp_161_var = _tmp_161_rule(p)) // 'or' conjunction ) { - _res = _tmp_160_var; + _res = _tmp_161_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -31298,12 +31386,12 @@ _loop1_60_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_60[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('and' inversion)")); - void *_tmp_161_var; + void *_tmp_162_var; while ( - (_tmp_161_var = _tmp_161_rule(p)) // 'and' inversion + (_tmp_162_var = _tmp_162_rule(p)) // 'and' inversion ) { - _res = _tmp_161_var; + _res = _tmp_162_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -31490,7 +31578,7 @@ _loop0_63_rule(Parser *p) while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_162_rule(p)) // slice | starred_expression + (elem = _tmp_163_rule(p)) // slice | starred_expression ) { _res = elem; @@ -31555,7 +31643,7 @@ _gather_64_rule(Parser *p) void *elem; asdl_seq * seq; if ( - (elem = _tmp_162_rule(p)) // slice | starred_expression + (elem = _tmp_163_rule(p)) // slice | starred_expression && (seq = _loop0_63_rule(p)) // _loop0_63 ) @@ -32618,12 +32706,12 @@ _loop1_80_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_80[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(fstring | string)")); - void *_tmp_153_var; + void *_tmp_154_var; while ( - (_tmp_153_var = _tmp_153_rule(p)) // fstring | string + (_tmp_154_var = _tmp_154_rule(p)) // fstring | string ) { - _res = _tmp_153_var; + _res = _tmp_154_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -33000,12 +33088,12 @@ _loop0_86_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop0_86[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)")); - void *_tmp_163_var; + void *_tmp_164_var; while ( - (_tmp_163_var = _tmp_163_rule(p)) // 'if' disjunction + (_tmp_164_var = _tmp_164_rule(p)) // 'if' disjunction ) { - _res = _tmp_163_var; + _res = _tmp_164_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -33131,7 +33219,7 @@ _loop0_88_rule(Parser *p) while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_164_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' + (elem = _tmp_165_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' ) { _res = elem; @@ -33197,7 +33285,7 @@ _gather_89_rule(Parser *p) void *elem; asdl_seq * seq; if ( - (elem = _tmp_164_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' + (elem = _tmp_165_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' && (seq = _loop0_88_rule(p)) // _loop0_88 ) @@ -33524,12 +33612,12 @@ _loop0_95_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop0_95[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)")); - void *_tmp_165_var; + void *_tmp_166_var; while ( - (_tmp_165_var = _tmp_165_rule(p)) // ',' star_target + (_tmp_166_var = _tmp_166_rule(p)) // ',' star_target ) { - _res = _tmp_165_var; + _res = _tmp_166_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -33708,12 +33796,12 @@ _loop1_98_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_98[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)")); - void *_tmp_165_var; + void *_tmp_166_var; while ( - (_tmp_165_var = _tmp_165_rule(p)) // ',' star_target + (_tmp_166_var = _tmp_166_rule(p)) // ',' star_target ) { - _res = _tmp_165_var; + _res = _tmp_166_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -34088,13 +34176,13 @@ _tmp_105_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _tmp_105[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)")); - void *_tmp_166_var; + void *_tmp_167_var; if ( - (_tmp_166_var = _tmp_166_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs + (_tmp_167_var = _tmp_167_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs ) { D(fprintf(stderr, "%*c+ _tmp_105[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)")); - _res = _tmp_166_var; + _res = _tmp_167_var; goto done; } p->mark = _mark; @@ -34159,7 +34247,7 @@ _loop0_106_rule(Parser *p) while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_167_rule(p)) // starred_expression !'=' + (elem = _tmp_168_rule(p)) // starred_expression !'=' ) { _res = elem; @@ -34224,7 +34312,7 @@ _gather_107_rule(Parser *p) void *elem; asdl_seq * seq; if ( - (elem = _tmp_167_rule(p)) // starred_expression !'=' + (elem = _tmp_168_rule(p)) // starred_expression !'=' && (seq = _loop0_106_rule(p)) // _loop0_106 ) @@ -34546,12 +34634,12 @@ _loop1_113_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_113[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(!STRING expression_without_invalid)")); - void *_tmp_168_var; + void *_tmp_169_var; while ( - (_tmp_168_var = _tmp_168_rule(p)) // !STRING expression_without_invalid + (_tmp_169_var = _tmp_169_rule(p)) // !STRING expression_without_invalid ) { - _res = _tmp_168_var; + _res = _tmp_169_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -35068,12 +35156,12 @@ _loop0_120_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop0_120[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); - void *_tmp_156_var; + void *_tmp_157_var; while ( - (_tmp_156_var = _tmp_156_rule(p)) // star_targets '=' + (_tmp_157_var = _tmp_157_rule(p)) // star_targets '=' ) { - _res = _tmp_156_var; + _res = _tmp_157_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -35450,15 +35538,15 @@ _tmp_126_rule(Parser *p) } D(fprintf(stderr, "%*c> _tmp_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); Token * _literal; - void *_tmp_169_var; + void *_tmp_170_var; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_tmp_169_var = _tmp_169_rule(p)) // ')' | '**' + (_tmp_170_var = _tmp_170_rule(p)) // ')' | '**' ) { D(fprintf(stderr, "%*c+ _tmp_126[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); - _res = _PyPegen_dummy_name(p, _literal, _tmp_169_var); + _res = _PyPegen_dummy_name(p, _literal, _tmp_170_var); goto done; } p->mark = _mark; @@ -35874,15 +35962,15 @@ _tmp_133_rule(Parser *p) } D(fprintf(stderr, "%*c> _tmp_133[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); Token * _literal; - void *_tmp_170_var; + void *_tmp_171_var; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_tmp_170_var = _tmp_170_rule(p)) // ':' | '**' + (_tmp_171_var = _tmp_171_rule(p)) // ':' | '**' ) { D(fprintf(stderr, "%*c+ _tmp_133[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); - _res = _PyPegen_dummy_name(p, _literal, _tmp_170_var); + _res = _PyPegen_dummy_name(p, _literal, _tmp_171_var); goto done; } p->mark = _mark; @@ -35971,20 +36059,20 @@ _tmp_135_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _tmp_135[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?")); - asdl_seq * _loop0_171_var; + asdl_seq * _loop0_172_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty bitwise_or_var; if ( (bitwise_or_var = bitwise_or_rule(p)) // bitwise_or && - (_loop0_171_var = _loop0_171_rule(p)) // ((',' bitwise_or))* + (_loop0_172_var = _loop0_172_rule(p)) // ((',' bitwise_or))* && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? ) { D(fprintf(stderr, "%*c+ _tmp_135[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?")); - _res = _PyPegen_dummy_name(p, bitwise_or_var, _loop0_171_var, _opt_var); + _res = _PyPegen_dummy_name(p, bitwise_or_var, _loop0_172_var, _opt_var); goto done; } p->mark = _mark; @@ -36133,16 +36221,16 @@ _tmp_138_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _tmp_138[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME (',' | ')' | NEWLINE)")); - void *_tmp_172_var; + void *_tmp_173_var; expr_ty name_var; if ( (name_var = _PyPegen_name_token(p)) // NAME && - (_tmp_172_var = _tmp_172_rule(p)) // ',' | ')' | NEWLINE + (_tmp_173_var = _tmp_173_rule(p)) // ',' | ')' | NEWLINE ) { D(fprintf(stderr, "%*c+ _tmp_138[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME (',' | ')' | NEWLINE)")); - _res = _PyPegen_dummy_name(p, name_var, _tmp_172_var); + _res = _PyPegen_dummy_name(p, name_var, _tmp_173_var); goto done; } p->mark = _mark; @@ -36188,7 +36276,7 @@ _loop0_139_rule(Parser *p) while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_173_rule(p)) // expression ['as' star_target] + (elem = _tmp_174_rule(p)) // expression ['as' star_target] ) { _res = elem; @@ -36253,7 +36341,7 @@ _gather_140_rule(Parser *p) void *elem; asdl_seq * seq; if ( - (elem = _tmp_173_rule(p)) // expression ['as' star_target] + (elem = _tmp_174_rule(p)) // expression ['as' star_target] && (seq = _loop0_139_rule(p)) // _loop0_139 ) @@ -36305,7 +36393,7 @@ _loop0_141_rule(Parser *p) while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_174_rule(p)) // expressions ['as' star_target] + (elem = _tmp_175_rule(p)) // expressions ['as' star_target] ) { _res = elem; @@ -36370,7 +36458,7 @@ _gather_142_rule(Parser *p) void *elem; asdl_seq * seq; if ( - (elem = _tmp_174_rule(p)) // expressions ['as' star_target] + (elem = _tmp_175_rule(p)) // expressions ['as' star_target] && (seq = _loop0_141_rule(p)) // _loop0_141 ) @@ -36612,9 +36700,50 @@ _tmp_146_rule(Parser *p) return _res; } -// _tmp_147: positional_patterns ',' +// _tmp_147: items_pattern ',' static void * _tmp_147_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // items_pattern ',' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "items_pattern ','")); + Token * _literal; + asdl_seq* items_pattern_var; + if ( + (items_pattern_var = items_pattern_rule(p)) // items_pattern + && + (_literal = _PyPegen_expect_token(p, 12)) // token=',' + ) + { + D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "items_pattern ','")); + _res = _PyPegen_dummy_name(p, items_pattern_var, _literal); + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "items_pattern ','")); + } + _res = NULL; + done: + p->level--; + return _res; +} + +// _tmp_148: positional_patterns ',' +static void * +_tmp_148_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36630,7 +36759,7 @@ _tmp_147_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "positional_patterns ','")); + D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "positional_patterns ','")); Token * _literal; asdl_pattern_seq* positional_patterns_var; if ( @@ -36639,12 +36768,12 @@ _tmp_147_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "positional_patterns ','")); + D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "positional_patterns ','")); _res = _PyPegen_dummy_name(p, positional_patterns_var, _literal); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "positional_patterns ','")); } _res = NULL; @@ -36653,64 +36782,7 @@ _tmp_147_rule(Parser *p) return _res; } -// _tmp_148: '}' | ',' -static void * -_tmp_148_rule(Parser *p) -{ - if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { - _Pypegen_stack_overflow(p); - } - if (p->error_indicator) { - p->level--; - return NULL; - } - void * _res = NULL; - int _mark = p->mark; - { // '}' - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); - Token * _literal; - if ( - (_literal = _PyPegen_expect_token(p, 26)) // token='}' - ) - { - D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); - _res = _literal; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'")); - } - { // ',' - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); - Token * _literal; - if ( - (_literal = _PyPegen_expect_token(p, 12)) // token=',' - ) - { - D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); - _res = _literal; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); - } - _res = NULL; - done: - p->level--; - return _res; -} - -// _tmp_149: '=' | '!' | ':' | '}' +// _tmp_149: '}' | ',' static void * _tmp_149_rule(Parser *p) { @@ -36723,63 +36795,6 @@ _tmp_149_rule(Parser *p) } void * _res = NULL; int _mark = p->mark; - { // '=' - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='")); - Token * _literal; - if ( - (_literal = _PyPegen_expect_token(p, 22)) // token='=' - ) - { - D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='")); - _res = _literal; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'='")); - } - { // '!' - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!'")); - Token * _literal; - if ( - (_literal = _PyPegen_expect_token(p, 54)) // token='!' - ) - { - D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!'")); - _res = _literal; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!'")); - } - { // ':' - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); - Token * _literal; - if ( - (_literal = _PyPegen_expect_token(p, 11)) // token=':' - ) - { - D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); - _res = _literal; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); - } { // '}' if (p->error_indicator) { p->level--; @@ -36799,13 +36814,32 @@ _tmp_149_rule(Parser *p) D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'")); } + { // ',' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + Token * _literal; + if ( + (_literal = _PyPegen_expect_token(p, 12)) // token=',' + ) + { + D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + _res = _literal; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); + } _res = NULL; done: p->level--; return _res; } -// _tmp_150: '!' | ':' | '}' +// _tmp_150: '=' | '!' | ':' | '}' static void * _tmp_150_rule(Parser *p) { @@ -36818,6 +36852,25 @@ _tmp_150_rule(Parser *p) } void * _res = NULL; int _mark = p->mark; + { // '=' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='")); + Token * _literal; + if ( + (_literal = _PyPegen_expect_token(p, 22)) // token='=' + ) + { + D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='")); + _res = _literal; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'='")); + } { // '!' if (p->error_indicator) { p->level--; @@ -36881,9 +36934,85 @@ _tmp_150_rule(Parser *p) return _res; } -// _tmp_151: '!' NAME +// _tmp_151: '!' | ':' | '}' static void * _tmp_151_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // '!' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!'")); + Token * _literal; + if ( + (_literal = _PyPegen_expect_token(p, 54)) // token='!' + ) + { + D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!'")); + _res = _literal; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!'")); + } + { // ':' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + Token * _literal; + if ( + (_literal = _PyPegen_expect_token(p, 11)) // token=':' + ) + { + D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + _res = _literal; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); + } + { // '}' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); + Token * _literal; + if ( + (_literal = _PyPegen_expect_token(p, 26)) // token='}' + ) + { + D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); + _res = _literal; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'")); + } + _res = NULL; + done: + p->level--; + return _res; +} + +// _tmp_152: '!' NAME +static void * +_tmp_152_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36899,7 +37028,7 @@ _tmp_151_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' NAME")); + D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' NAME")); Token * _literal; expr_ty name_var; if ( @@ -36908,12 +37037,12 @@ _tmp_151_rule(Parser *p) (name_var = _PyPegen_name_token(p)) // NAME ) { - D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' NAME")); + D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' NAME")); _res = _PyPegen_dummy_name(p, _literal, name_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!' NAME")); } _res = NULL; @@ -36922,9 +37051,9 @@ _tmp_151_rule(Parser *p) return _res; } -// _tmp_152: ':' | '}' +// _tmp_153: ':' | '}' static void * -_tmp_152_rule(Parser *p) +_tmp_153_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36940,18 +37069,18 @@ _tmp_152_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } { // '}' @@ -36959,18 +37088,18 @@ _tmp_152_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'")); } _res = NULL; @@ -36979,9 +37108,9 @@ _tmp_152_rule(Parser *p) return _res; } -// _tmp_153: fstring | string +// _tmp_154: fstring | string static void * -_tmp_153_rule(Parser *p) +_tmp_154_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36997,18 +37126,18 @@ _tmp_153_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring")); + D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring")); expr_ty fstring_var; if ( (fstring_var = fstring_rule(p)) // fstring ) { - D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "fstring")); + D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "fstring")); _res = fstring_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "fstring")); } { // string @@ -37016,18 +37145,18 @@ _tmp_153_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "string")); + D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "string")); expr_ty string_var; if ( (string_var = string_rule(p)) // string ) { - D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "string")); + D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "string")); _res = string_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "string")); } _res = NULL; @@ -37036,159 +37165,7 @@ _tmp_153_rule(Parser *p) return _res; } -// _tmp_154: '+' | '-' | '*' | '/' | '%' | '//' | '@' -static void * -_tmp_154_rule(Parser *p) -{ - if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { - _Pypegen_stack_overflow(p); - } - if (p->error_indicator) { - p->level--; - return NULL; - } - void * _res = NULL; - int _mark = p->mark; - { // '+' - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+'")); - Token * _literal; - if ( - (_literal = _PyPegen_expect_token(p, 14)) // token='+' - ) - { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+'")); - _res = _literal; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'+'")); - } - { // '-' - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'-'")); - Token * _literal; - if ( - (_literal = _PyPegen_expect_token(p, 15)) // token='-' - ) - { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'-'")); - _res = _literal; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'-'")); - } - { // '*' - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'")); - Token * _literal; - if ( - (_literal = _PyPegen_expect_token(p, 16)) // token='*' - ) - { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); - _res = _literal; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*'")); - } - { // '/' - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'")); - Token * _literal; - if ( - (_literal = _PyPegen_expect_token(p, 17)) // token='/' - ) - { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'")); - _res = _literal; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'/'")); - } - { // '%' - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'%'")); - Token * _literal; - if ( - (_literal = _PyPegen_expect_token(p, 24)) // token='%' - ) - { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'%'")); - _res = _literal; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'%'")); - } - { // '//' - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'//'")); - Token * _literal; - if ( - (_literal = _PyPegen_expect_token(p, 47)) // token='//' - ) - { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'//'")); - _res = _literal; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'//'")); - } - { // '@' - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@'")); - Token * _literal; - if ( - (_literal = _PyPegen_expect_token(p, 49)) // token='@' - ) - { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@'")); - _res = _literal; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'@'")); - } - _res = NULL; - done: - p->level--; - return _res; -} - -// _tmp_155: '+' | '-' | '~' +// _tmp_155: '+' | '-' | '*' | '/' | '%' | '//' | '@' static void * _tmp_155_rule(Parser *p) { @@ -37239,23 +37216,175 @@ _tmp_155_rule(Parser *p) D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'-'")); } - { // '~' + { // '*' if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'~'")); + D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'")); Token * _literal; if ( - (_literal = _PyPegen_expect_token(p, 31)) // token='~' + (_literal = _PyPegen_expect_token(p, 16)) // token='*' ) { - D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'~'")); + D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); _res = _literal; goto done; } p->mark = _mark; D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*'")); + } + { // '/' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'")); + Token * _literal; + if ( + (_literal = _PyPegen_expect_token(p, 17)) // token='/' + ) + { + D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'")); + _res = _literal; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'/'")); + } + { // '%' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'%'")); + Token * _literal; + if ( + (_literal = _PyPegen_expect_token(p, 24)) // token='%' + ) + { + D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'%'")); + _res = _literal; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'%'")); + } + { // '//' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'//'")); + Token * _literal; + if ( + (_literal = _PyPegen_expect_token(p, 47)) // token='//' + ) + { + D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'//'")); + _res = _literal; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'//'")); + } + { // '@' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@'")); + Token * _literal; + if ( + (_literal = _PyPegen_expect_token(p, 49)) // token='@' + ) + { + D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@'")); + _res = _literal; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'@'")); + } + _res = NULL; + done: + p->level--; + return _res; +} + +// _tmp_156: '+' | '-' | '~' +static void * +_tmp_156_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // '+' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+'")); + Token * _literal; + if ( + (_literal = _PyPegen_expect_token(p, 14)) // token='+' + ) + { + D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+'")); + _res = _literal; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'+'")); + } + { // '-' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'-'")); + Token * _literal; + if ( + (_literal = _PyPegen_expect_token(p, 15)) // token='-' + ) + { + D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'-'")); + _res = _literal; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'-'")); + } + { // '~' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'~'")); + Token * _literal; + if ( + (_literal = _PyPegen_expect_token(p, 31)) // token='~' + ) + { + D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'~'")); + _res = _literal; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'~'")); } _res = NULL; @@ -37264,9 +37393,9 @@ _tmp_155_rule(Parser *p) return _res; } -// _tmp_156: star_targets '=' +// _tmp_157: star_targets '=' static void * -_tmp_156_rule(Parser *p) +_tmp_157_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37282,7 +37411,7 @@ _tmp_156_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); Token * _literal; expr_ty z; if ( @@ -37291,7 +37420,7 @@ _tmp_156_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); _res = z; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -37301,7 +37430,7 @@ _tmp_156_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_targets '='")); } _res = NULL; @@ -37310,9 +37439,9 @@ _tmp_156_rule(Parser *p) return _res; } -// _tmp_157: '.' | '...' +// _tmp_158: '.' | '...' static void * -_tmp_157_rule(Parser *p) +_tmp_158_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37328,18 +37457,18 @@ _tmp_157_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'")); + D(fprintf(stderr, "%*c> _tmp_158[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 23)) // token='.' ) { - D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'")); + D(fprintf(stderr, "%*c+ _tmp_158[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_158[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'.'")); } { // '...' @@ -37347,18 +37476,18 @@ _tmp_157_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'")); + D(fprintf(stderr, "%*c> _tmp_158[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 52)) // token='...' ) { - D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'")); + D(fprintf(stderr, "%*c+ _tmp_158[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_158[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'...'")); } _res = NULL; @@ -37367,9 +37496,9 @@ _tmp_157_rule(Parser *p) return _res; } -// _tmp_158: '@' named_expression NEWLINE +// _tmp_159: '@' named_expression NEWLINE static void * -_tmp_158_rule(Parser *p) +_tmp_159_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37385,7 +37514,7 @@ _tmp_158_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_158[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); + D(fprintf(stderr, "%*c> _tmp_159[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); Token * _literal; expr_ty f; Token * newline_var; @@ -37397,7 +37526,7 @@ _tmp_158_rule(Parser *p) (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) { - D(fprintf(stderr, "%*c+ _tmp_158[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); + D(fprintf(stderr, "%*c+ _tmp_159[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); _res = f; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -37407,7 +37536,7 @@ _tmp_158_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_158[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_159[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'@' named_expression NEWLINE")); } _res = NULL; @@ -37416,9 +37545,9 @@ _tmp_158_rule(Parser *p) return _res; } -// _tmp_159: ',' star_expression +// _tmp_160: ',' star_expression static void * -_tmp_159_rule(Parser *p) +_tmp_160_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37434,7 +37563,7 @@ _tmp_159_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_159[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_expression")); + D(fprintf(stderr, "%*c> _tmp_160[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_expression")); Token * _literal; expr_ty c; if ( @@ -37443,7 +37572,7 @@ _tmp_159_rule(Parser *p) (c = star_expression_rule(p)) // star_expression ) { - D(fprintf(stderr, "%*c+ _tmp_159[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_expression")); + D(fprintf(stderr, "%*c+ _tmp_160[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_expression")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -37453,7 +37582,7 @@ _tmp_159_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_159[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_160[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_expression")); } _res = NULL; @@ -37462,9 +37591,9 @@ _tmp_159_rule(Parser *p) return _res; } -// _tmp_160: 'or' conjunction +// _tmp_161: 'or' conjunction static void * -_tmp_160_rule(Parser *p) +_tmp_161_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37480,7 +37609,7 @@ _tmp_160_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_160[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); + D(fprintf(stderr, "%*c> _tmp_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); Token * _keyword; expr_ty c; if ( @@ -37489,7 +37618,7 @@ _tmp_160_rule(Parser *p) (c = conjunction_rule(p)) // conjunction ) { - D(fprintf(stderr, "%*c+ _tmp_160[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); + D(fprintf(stderr, "%*c+ _tmp_161[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -37499,7 +37628,7 @@ _tmp_160_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_160[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_161[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'or' conjunction")); } _res = NULL; @@ -37508,9 +37637,9 @@ _tmp_160_rule(Parser *p) return _res; } -// _tmp_161: 'and' inversion +// _tmp_162: 'and' inversion static void * -_tmp_161_rule(Parser *p) +_tmp_162_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37526,7 +37655,7 @@ _tmp_161_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'and' inversion")); + D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'and' inversion")); Token * _keyword; expr_ty c; if ( @@ -37535,7 +37664,7 @@ _tmp_161_rule(Parser *p) (c = inversion_rule(p)) // inversion ) { - D(fprintf(stderr, "%*c+ _tmp_161[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'and' inversion")); + D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'and' inversion")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -37545,7 +37674,7 @@ _tmp_161_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_161[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'and' inversion")); } _res = NULL; @@ -37554,9 +37683,9 @@ _tmp_161_rule(Parser *p) return _res; } -// _tmp_162: slice | starred_expression +// _tmp_163: slice | starred_expression static void * -_tmp_162_rule(Parser *p) +_tmp_163_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37572,18 +37701,18 @@ _tmp_162_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slice")); + D(fprintf(stderr, "%*c> _tmp_163[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slice")); expr_ty slice_var; if ( (slice_var = slice_rule(p)) // slice ) { - D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slice")); + D(fprintf(stderr, "%*c+ _tmp_163[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slice")); _res = slice_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_163[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slice")); } { // starred_expression @@ -37591,18 +37720,18 @@ _tmp_162_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c> _tmp_163[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); expr_ty starred_expression_var; if ( (starred_expression_var = starred_expression_rule(p)) // starred_expression ) { - D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c+ _tmp_163[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); _res = starred_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_163[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression")); } _res = NULL; @@ -37611,9 +37740,9 @@ _tmp_162_rule(Parser *p) return _res; } -// _tmp_163: 'if' disjunction +// _tmp_164: 'if' disjunction static void * -_tmp_163_rule(Parser *p) +_tmp_164_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37629,7 +37758,7 @@ _tmp_163_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_163[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c> _tmp_164[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); Token * _keyword; expr_ty z; if ( @@ -37638,7 +37767,7 @@ _tmp_163_rule(Parser *p) (z = disjunction_rule(p)) // disjunction ) { - D(fprintf(stderr, "%*c+ _tmp_163[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c+ _tmp_164[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); _res = z; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -37648,7 +37777,7 @@ _tmp_163_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_163[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_164[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'if' disjunction")); } _res = NULL; @@ -37657,9 +37786,9 @@ _tmp_163_rule(Parser *p) return _res; } -// _tmp_164: starred_expression | (assignment_expression | expression !':=') !'=' +// _tmp_165: starred_expression | (assignment_expression | expression !':=') !'=' static void * -_tmp_164_rule(Parser *p) +_tmp_165_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37675,18 +37804,18 @@ _tmp_164_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_164[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c> _tmp_165[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); expr_ty starred_expression_var; if ( (starred_expression_var = starred_expression_rule(p)) // starred_expression ) { - D(fprintf(stderr, "%*c+ _tmp_164[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c+ _tmp_165[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); _res = starred_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_164[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_165[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression")); } { // (assignment_expression | expression !':=') !'=' @@ -37694,7 +37823,7 @@ _tmp_164_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_164[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); + D(fprintf(stderr, "%*c> _tmp_165[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); void *_tmp_87_var; if ( (_tmp_87_var = _tmp_87_rule(p)) // assignment_expression | expression !':=' @@ -37702,12 +37831,12 @@ _tmp_164_rule(Parser *p) _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 22) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_164[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); + D(fprintf(stderr, "%*c+ _tmp_165[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); _res = _tmp_87_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_164[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_165[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(assignment_expression | expression !':=') !'='")); } _res = NULL; @@ -37716,9 +37845,9 @@ _tmp_164_rule(Parser *p) return _res; } -// _tmp_165: ',' star_target +// _tmp_166: ',' star_target static void * -_tmp_165_rule(Parser *p) +_tmp_166_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37734,7 +37863,7 @@ _tmp_165_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_165[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); + D(fprintf(stderr, "%*c> _tmp_166[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); Token * _literal; expr_ty c; if ( @@ -37743,7 +37872,7 @@ _tmp_165_rule(Parser *p) (c = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_165[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target")); + D(fprintf(stderr, "%*c+ _tmp_166[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -37753,7 +37882,7 @@ _tmp_165_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_165[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_166[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_target")); } _res = NULL; @@ -37762,10 +37891,10 @@ _tmp_165_rule(Parser *p) return _res; } -// _tmp_166: +// _tmp_167: // | ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs static void * -_tmp_166_rule(Parser *p) +_tmp_167_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37781,7 +37910,7 @@ _tmp_166_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_166[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); + D(fprintf(stderr, "%*c> _tmp_167[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); asdl_seq * _gather_89_var; Token * _literal; asdl_seq* kwargs_var; @@ -37793,12 +37922,12 @@ _tmp_166_rule(Parser *p) (kwargs_var = kwargs_rule(p)) // kwargs ) { - D(fprintf(stderr, "%*c+ _tmp_166[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); + D(fprintf(stderr, "%*c+ _tmp_167[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); _res = _PyPegen_dummy_name(p, _gather_89_var, _literal, kwargs_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_166[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_167[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); } _res = NULL; @@ -37807,9 +37936,9 @@ _tmp_166_rule(Parser *p) return _res; } -// _tmp_167: starred_expression !'=' +// _tmp_168: starred_expression !'=' static void * -_tmp_167_rule(Parser *p) +_tmp_168_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37825,7 +37954,7 @@ _tmp_167_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_167[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression !'='")); + D(fprintf(stderr, "%*c> _tmp_168[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression !'='")); expr_ty starred_expression_var; if ( (starred_expression_var = starred_expression_rule(p)) // starred_expression @@ -37833,12 +37962,12 @@ _tmp_167_rule(Parser *p) _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 22) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_167[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression !'='")); + D(fprintf(stderr, "%*c+ _tmp_168[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression !'='")); _res = starred_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_167[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_168[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression !'='")); } _res = NULL; @@ -37847,9 +37976,9 @@ _tmp_167_rule(Parser *p) return _res; } -// _tmp_168: !STRING expression_without_invalid +// _tmp_169: !STRING expression_without_invalid static void * -_tmp_168_rule(Parser *p) +_tmp_169_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37865,7 +37994,7 @@ _tmp_168_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_168[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!STRING expression_without_invalid")); + D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!STRING expression_without_invalid")); expr_ty expression_without_invalid_var; if ( _PyPegen_lookahead(0, _PyPegen_string_token, p) @@ -37873,12 +38002,12 @@ _tmp_168_rule(Parser *p) (expression_without_invalid_var = expression_without_invalid_rule(p)) // expression_without_invalid ) { - D(fprintf(stderr, "%*c+ _tmp_168[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!STRING expression_without_invalid")); + D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!STRING expression_without_invalid")); _res = expression_without_invalid_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_168[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "!STRING expression_without_invalid")); } _res = NULL; @@ -37887,9 +38016,9 @@ _tmp_168_rule(Parser *p) return _res; } -// _tmp_169: ')' | '**' +// _tmp_170: ')' | '**' static void * -_tmp_169_rule(Parser *p) +_tmp_170_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37905,76 +38034,19 @@ _tmp_169_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_170[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); - _res = _literal; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); - } - { // '**' - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); - Token * _literal; - if ( - (_literal = _PyPegen_expect_token(p, 35)) // token='**' - ) - { - D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); - _res = _literal; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'")); - } - _res = NULL; - done: - p->level--; - return _res; -} - -// _tmp_170: ':' | '**' -static void * -_tmp_170_rule(Parser *p) -{ - if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { - _Pypegen_stack_overflow(p); - } - if (p->error_indicator) { - p->level--; - return NULL; - } - void * _res = NULL; - int _mark = p->mark; - { // ':' - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_170[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); - Token * _literal; - if ( - (_literal = _PyPegen_expect_token(p, 11)) // token=':' - ) - { - D(fprintf(stderr, "%*c+ _tmp_170[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_170[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; D(fprintf(stderr, "%*c%s _tmp_170[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } { // '**' if (p->error_indicator) { @@ -38001,9 +38073,66 @@ _tmp_170_rule(Parser *p) return _res; } -// _loop0_171: (',' bitwise_or) +// _tmp_171: ':' | '**' +static void * +_tmp_171_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // ':' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + Token * _literal; + if ( + (_literal = _PyPegen_expect_token(p, 11)) // token=':' + ) + { + D(fprintf(stderr, "%*c+ _tmp_171[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + _res = _literal; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_171[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); + } + { // '**' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); + Token * _literal; + if ( + (_literal = _PyPegen_expect_token(p, 35)) // token='**' + ) + { + D(fprintf(stderr, "%*c+ _tmp_171[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); + _res = _literal; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_171[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'")); + } + _res = NULL; + done: + p->level--; + return _res; +} + +// _loop0_172: (',' bitwise_or) static asdl_seq * -_loop0_171_rule(Parser *p) +_loop0_172_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -38028,13 +38157,13 @@ _loop0_171_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' bitwise_or)")); - void *_tmp_175_var; + D(fprintf(stderr, "%*c> _loop0_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' bitwise_or)")); + void *_tmp_176_var; while ( - (_tmp_175_var = _tmp_175_rule(p)) // ',' bitwise_or + (_tmp_176_var = _tmp_176_rule(p)) // ',' bitwise_or ) { - _res = _tmp_175_var; + _res = _tmp_176_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -38051,7 +38180,7 @@ _loop0_171_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_171[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_172[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(',' bitwise_or)")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -38068,9 +38197,9 @@ _loop0_171_rule(Parser *p) return _seq; } -// _tmp_172: ',' | ')' | NEWLINE +// _tmp_173: ',' | ')' | NEWLINE static void * -_tmp_172_rule(Parser *p) +_tmp_173_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -38086,18 +38215,18 @@ _tmp_172_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_173[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_172[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_173[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_172[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_173[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } { // ')' @@ -38105,18 +38234,18 @@ _tmp_172_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_173[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_172[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_173[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_172[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_173[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } { // NEWLINE @@ -38124,18 +38253,18 @@ _tmp_172_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE")); + D(fprintf(stderr, "%*c> _tmp_173[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE")); Token * newline_var; if ( (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) { - D(fprintf(stderr, "%*c+ _tmp_172[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE")); + D(fprintf(stderr, "%*c+ _tmp_173[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE")); _res = newline_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_172[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_173[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NEWLINE")); } _res = NULL; @@ -38144,9 +38273,9 @@ _tmp_172_rule(Parser *p) return _res; } -// _tmp_173: expression ['as' star_target] +// _tmp_174: expression ['as' star_target] static void * -_tmp_173_rule(Parser *p) +_tmp_174_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -38162,22 +38291,22 @@ _tmp_173_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_173[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); + D(fprintf(stderr, "%*c> _tmp_174[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty expression_var; if ( (expression_var = expression_rule(p)) // expression && - (_opt_var = _tmp_176_rule(p), !p->error_indicator) // ['as' star_target] + (_opt_var = _tmp_177_rule(p), !p->error_indicator) // ['as' star_target] ) { - D(fprintf(stderr, "%*c+ _tmp_173[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); + D(fprintf(stderr, "%*c+ _tmp_174[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); _res = _PyPegen_dummy_name(p, expression_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_173[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_174[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ['as' star_target]")); } _res = NULL; @@ -38186,9 +38315,9 @@ _tmp_173_rule(Parser *p) return _res; } -// _tmp_174: expressions ['as' star_target] +// _tmp_175: expressions ['as' star_target] static void * -_tmp_174_rule(Parser *p) +_tmp_175_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -38204,22 +38333,22 @@ _tmp_174_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_174[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); + D(fprintf(stderr, "%*c> _tmp_175[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty expressions_var; if ( (expressions_var = expressions_rule(p)) // expressions && - (_opt_var = _tmp_176_rule(p), !p->error_indicator) // ['as' star_target] + (_opt_var = _tmp_177_rule(p), !p->error_indicator) // ['as' star_target] ) { - D(fprintf(stderr, "%*c+ _tmp_174[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); + D(fprintf(stderr, "%*c+ _tmp_175[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); _res = _PyPegen_dummy_name(p, expressions_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_174[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_175[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expressions ['as' star_target]")); } _res = NULL; @@ -38228,9 +38357,9 @@ _tmp_174_rule(Parser *p) return _res; } -// _tmp_175: ',' bitwise_or +// _tmp_176: ',' bitwise_or static void * -_tmp_175_rule(Parser *p) +_tmp_176_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -38246,7 +38375,7 @@ _tmp_175_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_175[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' bitwise_or")); + D(fprintf(stderr, "%*c> _tmp_176[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' bitwise_or")); Token * _literal; expr_ty bitwise_or_var; if ( @@ -38255,12 +38384,12 @@ _tmp_175_rule(Parser *p) (bitwise_or_var = bitwise_or_rule(p)) // bitwise_or ) { - D(fprintf(stderr, "%*c+ _tmp_175[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' bitwise_or")); + D(fprintf(stderr, "%*c+ _tmp_176[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' bitwise_or")); _res = _PyPegen_dummy_name(p, _literal, bitwise_or_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_175[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_176[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' bitwise_or")); } _res = NULL; @@ -38269,9 +38398,9 @@ _tmp_175_rule(Parser *p) return _res; } -// _tmp_176: 'as' star_target +// _tmp_177: 'as' star_target static void * -_tmp_176_rule(Parser *p) +_tmp_177_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -38287,7 +38416,7 @@ _tmp_176_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_176[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c> _tmp_177[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); Token * _keyword; expr_ty star_target_var; if ( @@ -38296,12 +38425,12 @@ _tmp_176_rule(Parser *p) (star_target_var = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_176[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c+ _tmp_177[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); _res = _PyPegen_dummy_name(p, _keyword, star_target_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_176[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_177[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' star_target")); } _res = NULL; From e5f4299f138ef46378dc5766b33de7eb8937392b Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Wed, 22 Oct 2025 14:34:37 -0700 Subject: [PATCH 256/373] GH-140475: Support WASI SDK 25 (#140477) As well, bump the version of Wasmtime used in CI. --- .github/workflows/reusable-wasi.yml | 4 ++-- .../next/Build/2025-10-22-12-44-07.gh-issue-140475.OhzQbR.rst | 1 + Tools/wasm/wasi/__main__.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2025-10-22-12-44-07.gh-issue-140475.OhzQbR.rst diff --git a/.github/workflows/reusable-wasi.yml b/.github/workflows/reusable-wasi.yml index 6beb91e66d4..a4673d74cbb 100644 --- a/.github/workflows/reusable-wasi.yml +++ b/.github/workflows/reusable-wasi.yml @@ -16,8 +16,8 @@ jobs: runs-on: ubuntu-24.04 timeout-minutes: 60 env: - WASMTIME_VERSION: 22.0.0 - WASI_SDK_VERSION: 24 + WASMTIME_VERSION: 38.0.2 + WASI_SDK_VERSION: 25 WASI_SDK_PATH: /opt/wasi-sdk CROSS_BUILD_PYTHON: cross-build/build CROSS_BUILD_WASI: cross-build/wasm32-wasip1 diff --git a/Misc/NEWS.d/next/Build/2025-10-22-12-44-07.gh-issue-140475.OhzQbR.rst b/Misc/NEWS.d/next/Build/2025-10-22-12-44-07.gh-issue-140475.OhzQbR.rst new file mode 100644 index 00000000000..b4139024761 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2025-10-22-12-44-07.gh-issue-140475.OhzQbR.rst @@ -0,0 +1 @@ +Support WASI SDK 25. diff --git a/Tools/wasm/wasi/__main__.py b/Tools/wasm/wasi/__main__.py index b2f643ddbfc..3aedb5e97b7 100644 --- a/Tools/wasm/wasi/__main__.py +++ b/Tools/wasm/wasi/__main__.py @@ -31,7 +31,7 @@ b"# Required to statically build extension modules." ) -WASI_SDK_VERSION = 24 +WASI_SDK_VERSION = 25 WASMTIME_VAR_NAME = "WASMTIME" WASMTIME_HOST_RUNNER_VAR = f"{{{WASMTIME_VAR_NAME}}}" From bd2c7e8c8b10f4d31eab971781de13844bcd07fe Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Wed, 22 Oct 2025 16:11:48 -0700 Subject: [PATCH 257/373] GH-140472: Create a WASI devcontainer configuration (GH-140473) --- .devcontainer/wasi/devcontainer.json | 73 ++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 .devcontainer/wasi/devcontainer.json diff --git a/.devcontainer/wasi/devcontainer.json b/.devcontainer/wasi/devcontainer.json new file mode 100644 index 00000000000..4266144ce47 --- /dev/null +++ b/.devcontainer/wasi/devcontainer.json @@ -0,0 +1,73 @@ +{ + "image": "ghcr.io/python/wasicontainer:latest", + "onCreateCommand": [ + // Install common tooling. + "dnf", + "install", + "-y", + // For umask fix below. + "/usr/bin/setfacl" + ], + "updateContentCommand": { + // Using the shell for `nproc` usage. + "python": "python3 Tools/wasm/wasi build --quiet -- --with-pydebug -C" + }, + "postCreateCommand": { + // https://github.com/orgs/community/discussions/26026 + "umask fix: workspace": ["sudo", "setfacl", "-bnR", "."], + "umask fix: /tmp": ["sudo", "setfacl", "-bnR", "/tmp"] + }, + "customizations": { + "vscode": { + "extensions": [ + // Highlighting for Parser/Python.asdl. + "brettcannon.zephyr-asdl", + // Highlighting for configure.ac. + "maelvalais.autoconf", + // C auto-complete. + "ms-vscode.cpptools", + // Python auto-complete. + "ms-python.python" + ], + "settings": { + "C_Cpp.default.compilerPath": "/usr/bin/clang", + "C_Cpp.default.cStandard": "c11", + "C_Cpp.default.defines": [ + "CONFIG_64", + "Py_BUILD_CORE" + ], + "C_Cpp.default.includePath": [ + "${workspaceFolder}/*", + "${workspaceFolder}/Include/**" + ], + // https://github.com/microsoft/vscode-cpptools/issues/10732 + "C_Cpp.errorSquiggles": "disabled", + "editor.insertSpaces": true, + "editor.rulers": [ + 80 + ], + "editor.tabSize": 4, + "editor.trimAutoWhitespace": true, + "files.associations": { + "*.h": "c" + }, + "files.encoding": "utf8", + "files.eol": "\n", + "files.insertFinalNewline": true, + "files.trimTrailingWhitespace": true, + "python.analysis.diagnosticSeverityOverrides": { + // Complains about shadowing the stdlib w/ the stdlib. + "reportShadowedImports": "none", + // Doesn't like _frozen_importlib. + "reportMissingImports": "none" + }, + "python.analysis.extraPaths": [ + "Lib" + ], + "[restructuredtext]": { + "editor.tabSize": 3 + } + } + } + } +} From aa9d0a61d5c48717454f36351f0aabe4cc532de5 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Thu, 23 Oct 2025 10:49:27 +0100 Subject: [PATCH 258/373] gh-140474: Fix memory leak in `array.array` (GH-140478) --- Lib/test/test_array.py | 8 ++++++++ .../2025-10-22-20-52-13.gh-issue-140474.xIWlip.rst | 2 ++ Modules/arraymodule.c | 3 +++ 3 files changed, 13 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-10-22-20-52-13.gh-issue-140474.xIWlip.rst diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 58ea89c4fac..83b3c978da3 100755 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -1255,6 +1255,14 @@ def test_typecode_u_deprecation(self): with self.assertWarns(DeprecationWarning): array.array("u") + def test_empty_string_mem_leak_gh140474(self): + with warnings.catch_warnings(): + warnings.simplefilter('ignore', DeprecationWarning) + for _ in range(1000): + a = array.array('u', '') + self.assertEqual(len(a), 0) + self.assertEqual(a.typecode, 'u') + class UCS4Test(UnicodeTest): typecode = 'w' diff --git a/Misc/NEWS.d/next/Library/2025-10-22-20-52-13.gh-issue-140474.xIWlip.rst b/Misc/NEWS.d/next/Library/2025-10-22-20-52-13.gh-issue-140474.xIWlip.rst new file mode 100644 index 00000000000..aca4e68b1e5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-22-20-52-13.gh-issue-140474.xIWlip.rst @@ -0,0 +1,2 @@ +Fix memory leak in :class:`array.array` when creating arrays from an empty +:class:`str` and the ``u`` type code. diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index d97cf7af767..729e085c19f 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -2833,6 +2833,9 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) Py_SET_SIZE(self, n); self->allocated = n; } + else { + PyMem_Free(ustr); + } } else { // c == 'w' Py_ssize_t n = PyUnicode_GET_LENGTH(initial); From 6be6f8ff590712fc9fe7db75defcc5975493070d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurycy=20Paw=C5=82owski-Wiero=C5=84ski?= <5383+maurycy@users.noreply.github.com> Date: Thu, 23 Oct 2025 14:28:29 +0200 Subject: [PATCH 259/373] gh-137627: Make `csv.Sniffer.sniff()` delimiter detection 1.6x faster (#137628) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/whatsnew/3.15.rst | 8 +-- Lib/csv.py | 26 +++++----- Lib/test/test_csv.py | 50 +++++++++++++++++++ ...-08-11-04-52-18.gh-issue-137627.Ku5Yi2.rst | 1 + 4 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-08-11-04-52-18.gh-issue-137627.Ku5Yi2.rst diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index a9543bdd13e..cbca20cba5c 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -652,11 +652,11 @@ zlib Optimizations ============= -module_name ------------ - -* TODO +csv +--- +* :meth:`csv.Sniffer.sniff` delimiter detection is now up to 1.6x faster. + (Contributed by Maurycy Pawłowski-Wieroński in :gh:`137628`.) Removed diff --git a/Lib/csv.py b/Lib/csv.py index 98eab01429a..b2aaf5fd9fa 100644 --- a/Lib/csv.py +++ b/Lib/csv.py @@ -362,31 +362,33 @@ def _guess_delimiter(self, data, delimiters): try and evaluate the smallest portion of the data possible, evaluating additional chunks as necessary. """ + from collections import Counter, defaultdict data = list(filter(None, data.split('\n'))) - ascii = [chr(c) for c in range(127)] # 7-bit ASCII - # build frequency tables chunkLength = min(10, len(data)) iteration = 0 - charFrequency = {} + num_lines = 0 + # {char -> {count_per_line -> num_lines_with_that_count}} + char_frequency = defaultdict(Counter) modes = {} delims = {} start, end = 0, chunkLength while start < len(data): iteration += 1 for line in data[start:end]: - for char in ascii: - metaFrequency = charFrequency.get(char, {}) - # must count even if frequency is 0 - freq = line.count(char) - # value is the mode - metaFrequency[freq] = metaFrequency.get(freq, 0) + 1 - charFrequency[char] = metaFrequency + num_lines += 1 + for char, count in Counter(line).items(): + if char.isascii(): + char_frequency[char][count] += 1 - for char in charFrequency.keys(): - items = list(charFrequency[char].items()) + for char, counts in char_frequency.items(): + items = list(counts.items()) + missed_lines = num_lines - sum(counts.values()) + if missed_lines: + # Store the number of lines 'char' was missing from. + items.append((0, missed_lines)) if len(items) == 1 and items[0][0] == 0: continue # get the mode of the frequencies diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index 50431b562f9..6be6a7ae222 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -1437,6 +1437,56 @@ def test_doublequote(self): dialect = sniffer.sniff(self.sample9) self.assertTrue(dialect.doublequote) + def test_guess_delimiter_crlf_not_chosen(self): + # Ensure that we pick the real delimiter ("|") over "\r" in a tie. + sniffer = csv.Sniffer() + sample = "a|b\r\nc|d\r\ne|f\r\n" + self.assertEqual(sniffer.sniff(sample).delimiter, "|") + self.assertNotEqual(sniffer.sniff(sample).delimiter, "\r") + + def test_zero_mode_tie_order_independence(self): + sniffer = csv.Sniffer() + # ":" appears in half the rows (1, 0, 1, 0) - a tie between + # 0 and 1 per line. + # "," appears once every row (true delimiter). + # + # Even if the zero-frequency bucket is appended vs. inserted, the tie + # yields an adjusted score of 0, so ":" should not be promoted and + # "," must be selected. + sample = ( + "a,b:c\n" + "d,e\n" + "f,g:c\n" + "h,i\n" + ) + dialect = sniffer.sniff(sample) + self.assertEqual(dialect.delimiter, ",") + + def test_zero_mode_tie_order_comma_first(self): + sniffer = csv.Sniffer() + pattern = ( + "a,b\n" + "c:d\n" + "e,f\n" + "g:h\n" + ) + sample = pattern * 10 + with self.assertRaisesRegex(csv.Error, "Could not determine delimiter"): + sniffer.sniff(sample) + + def test_zero_mode_tie_order_colon_first(self): + sniffer = csv.Sniffer() + pattern = ( + "a:b\n" + "c,d\n" + "e:f\n" + "g,h\n" + ) + sample = pattern * 10 + with self.assertRaisesRegex(csv.Error, "Could not determine delimiter"): + sniffer.sniff(sample) + + class NUL: def write(s, *args): pass diff --git a/Misc/NEWS.d/next/Library/2025-08-11-04-52-18.gh-issue-137627.Ku5Yi2.rst b/Misc/NEWS.d/next/Library/2025-08-11-04-52-18.gh-issue-137627.Ku5Yi2.rst new file mode 100644 index 00000000000..855070ed6f4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-08-11-04-52-18.gh-issue-137627.Ku5Yi2.rst @@ -0,0 +1 @@ +Speed up :meth:`csv.Sniffer.sniff` delimiter detection by up to 1.6x. From 4d0849426f4c6862e50658c4e35341ffb5ab288b Mon Sep 17 00:00:00 2001 From: George Ogden <38294960+George-Ogden@users.noreply.github.com> Date: Thu, 23 Oct 2025 14:56:05 +0100 Subject: [PATCH 260/373] gh-138774: use `value` to `ast.unparse` code when `str` is `None` in `ast.Interpolation` (#139415) --- Doc/library/ast.rst | 5 + Lib/_ast_unparse.py | 7 +- Lib/test/test_unparse.py | 100 ++++++++++++++++-- ...-10-23-12-12-22.gh-issue-138774.mnh2gU.rst | 2 + 4 files changed, 102 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-23-12-12-22.gh-issue-138774.mnh2gU.rst diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index ea3ec7d95dc..49462167217 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -363,6 +363,11 @@ Literals function call). This has the same meaning as ``FormattedValue.value``. * ``str`` is a constant containing the text of the interpolation expression. + + If ``str`` is set to ``None``, then ``value`` is used to generate code + when calling :func:`ast.unparse`. This no longer guarantees that the + generated code is identical to the original and is intended for code + generation. * ``conversion`` is an integer: * -1: no conversion diff --git a/Lib/_ast_unparse.py b/Lib/_ast_unparse.py index 16cf56f62cc..1c8741b5a55 100644 --- a/Lib/_ast_unparse.py +++ b/Lib/_ast_unparse.py @@ -658,9 +658,9 @@ def _unparse_interpolation_value(self, inner): unparser.set_precedence(_Precedence.TEST.next(), inner) return unparser.visit(inner) - def _write_interpolation(self, node, is_interpolation=False): + def _write_interpolation(self, node, use_str_attr=False): with self.delimit("{", "}"): - if is_interpolation: + if use_str_attr: expr = node.str else: expr = self._unparse_interpolation_value(node.value) @@ -678,7 +678,8 @@ def visit_FormattedValue(self, node): self._write_interpolation(node) def visit_Interpolation(self, node): - self._write_interpolation(node, is_interpolation=True) + # If `str` is set to `None`, use the `value` to generate the source code. + self._write_interpolation(node, use_str_attr=node.str is not None) def visit_Name(self, node): self.write(node.id) diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py index 0d6b05bc660..35e4652a87b 100644 --- a/Lib/test/test_unparse.py +++ b/Lib/test/test_unparse.py @@ -206,6 +206,97 @@ def test_tstrings(self): self.check_ast_roundtrip("t'foo'") self.check_ast_roundtrip("t'foo {bar}'") self.check_ast_roundtrip("t'foo {bar!s:.2f}'") + self.check_ast_roundtrip("t'{a + b}'") + self.check_ast_roundtrip("t'{a + b:x}'") + self.check_ast_roundtrip("t'{a + b!s}'") + self.check_ast_roundtrip("t'{ {a}}'") + self.check_ast_roundtrip("t'{ {a}=}'") + self.check_ast_roundtrip("t'{{a}}'") + self.check_ast_roundtrip("t''") + self.check_ast_roundtrip('t""') + self.check_ast_roundtrip("t'{(lambda x: x)}'") + self.check_ast_roundtrip("t'{t'{x}'}'") + + def test_tstring_with_nonsensical_str_field(self): + # `value` suggests that the original code is `t'{test1}`, but `str` suggests otherwise + self.assertEqual( + ast.unparse( + ast.TemplateStr( + values=[ + ast.Interpolation( + value=ast.Name(id="test1", ctx=ast.Load()), str="test2", conversion=-1 + ) + ] + ) + ), + "t'{test2}'", + ) + + def test_tstring_with_none_str_field(self): + self.assertEqual( + ast.unparse( + ast.TemplateStr( + [ast.Interpolation(value=ast.Name(id="test1"), str=None, conversion=-1)] + ) + ), + "t'{test1}'", + ) + self.assertEqual( + ast.unparse( + ast.TemplateStr( + [ + ast.Interpolation( + value=ast.Lambda( + args=ast.arguments(args=[ast.arg(arg="x")]), + body=ast.Name(id="x"), + ), + str=None, + conversion=-1, + ) + ] + ) + ), + "t'{(lambda x: x)}'", + ) + self.assertEqual( + ast.unparse( + ast.TemplateStr( + values=[ + ast.Interpolation( + value=ast.TemplateStr( + # `str` field kept here + [ast.Interpolation(value=ast.Name(id="x"), str="y", conversion=-1)] + ), + str=None, + conversion=-1, + ) + ] + ) + ), + '''t"{t'{y}'}"''', + ) + self.assertEqual( + ast.unparse( + ast.TemplateStr( + values=[ + ast.Interpolation( + value=ast.TemplateStr( + [ast.Interpolation(value=ast.Name(id="x"), str=None, conversion=-1)] + ), + str=None, + conversion=-1, + ) + ] + ) + ), + '''t"{t'{x}'}"''', + ) + self.assertEqual( + ast.unparse(ast.TemplateStr( + [ast.Interpolation(value=ast.Constant(value="foo"), str=None, conversion=114)] + )), + '''t"{'foo'!r}"''', + ) def test_strings(self): self.check_ast_roundtrip("u'foo'") @@ -813,15 +904,6 @@ def test_type_params(self): self.check_ast_roundtrip("def f[T: int = int, **P = int, *Ts = *int]():\n pass") self.check_ast_roundtrip("class C[T: int = int, **P = int, *Ts = *int]():\n pass") - def test_tstr(self): - self.check_ast_roundtrip("t'{a + b}'") - self.check_ast_roundtrip("t'{a + b:x}'") - self.check_ast_roundtrip("t'{a + b!s}'") - self.check_ast_roundtrip("t'{ {a}}'") - self.check_ast_roundtrip("t'{ {a}=}'") - self.check_ast_roundtrip("t'{{a}}'") - self.check_ast_roundtrip("t''") - class ManualASTCreationTestCase(unittest.TestCase): """Test that AST nodes created without a type_params field unparse correctly.""" diff --git a/Misc/NEWS.d/next/Library/2025-10-23-12-12-22.gh-issue-138774.mnh2gU.rst b/Misc/NEWS.d/next/Library/2025-10-23-12-12-22.gh-issue-138774.mnh2gU.rst new file mode 100644 index 00000000000..e12f789e674 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-23-12-12-22.gh-issue-138774.mnh2gU.rst @@ -0,0 +1,2 @@ +:func:`ast.unparse` now generates full source code when handling +:class:`ast.Interpolation` nodes that do not have a specified source. From 574405c19e9b5de0504be46a3925027ded4495ae Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Thu, 23 Oct 2025 10:18:13 -0400 Subject: [PATCH 261/373] gh-140431: Fix GC crash due to partially initialized coroutines (gh-140470) The `make_gen()` function creates and tracks generator/coro objects, but doesn't fully initialize all the fields. At a minimum, we need to initialize all the fields that may be accessed by gen_traverse because the call to `compute_cr_origin()` can trigger a GC. --- .../2025-10-22-17-22-22.gh-issue-140431.m8D_A-.rst | 3 +++ Objects/genobject.c | 1 + 2 files changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-22-17-22-22.gh-issue-140431.m8D_A-.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-22-17-22-22.gh-issue-140431.m8D_A-.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-22-17-22-22.gh-issue-140431.m8D_A-.rst new file mode 100644 index 00000000000..3d62d210f1f --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-22-17-22-22.gh-issue-140431.m8D_A-.rst @@ -0,0 +1,3 @@ +Fix a crash in Python's :term:`garbage collector ` due to +partially initialized :term:`coroutine` objects when coroutine origin tracking +depth is enabled (:func:`sys.set_coroutine_origin_tracking_depth`). diff --git a/Objects/genobject.c b/Objects/genobject.c index c9ca2f1de51..2371ad16d5c 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -932,6 +932,7 @@ make_gen(PyTypeObject *type, PyFunctionObject *func) gen->gi_weakreflist = NULL; gen->gi_exc_state.exc_value = NULL; gen->gi_exc_state.previous_item = NULL; + gen->gi_iframe.f_executable = PyStackRef_None; assert(func->func_name != NULL); gen->gi_name = Py_NewRef(func->func_name); assert(func->func_qualname != NULL); From 1a3da2c0700839b6e334e368ff2b600c2389763f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20S=C5=82awecki?= Date: Thu, 23 Oct 2025 17:23:23 +0200 Subject: [PATCH 262/373] gh-140438: properly run the asyncio REPL tests (#140298) --- Lib/test/test_repl.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_repl.py b/Lib/test/test_repl.py index 54e69277282..042aa84b35d 100644 --- a/Lib/test/test_repl.py +++ b/Lib/test/test_repl.py @@ -5,6 +5,7 @@ import subprocess import sys import unittest +from functools import partial from textwrap import dedent from test import support from test.support import ( @@ -27,7 +28,7 @@ raise unittest.SkipTest("test module requires subprocess") -def spawn_repl(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw): +def spawn_repl(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, custom=False, **kw): """Run the Python REPL with the given arguments. kw is extra keyword args to pass to subprocess.Popen. Returns a Popen @@ -41,7 +42,11 @@ def spawn_repl(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw): # path may be used by PyConfig_Get("module_search_paths") to build the # default module search path. stdin_fname = os.path.join(os.path.dirname(sys.executable), "") - cmd_line = [stdin_fname, '-I', '-i'] + cmd_line = [stdin_fname, '-I'] + # Don't re-run the built-in REPL from interactive mode + # if we're testing a custom REPL (such as the asyncio REPL). + if not custom: + cmd_line.append('-i') cmd_line.extend(args) # Set TERM=vt100, for the rationale see the comments in spawn_python() of @@ -55,6 +60,10 @@ def spawn_repl(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw): stdout=stdout, stderr=stderr, **kw) + +spawn_asyncio_repl = partial(spawn_repl, "-m", "asyncio", custom=True) + + def run_on_interactive_mode(source): """Spawn a new Python interpreter, pass the given input source code from the stdin and return the @@ -359,7 +368,7 @@ def f(): class TestAsyncioREPL(unittest.TestCase): def test_multiple_statements_fail_early(self): user_input = "1 / 0; print(f'afterwards: {1+1}')" - p = spawn_repl("-m", "asyncio") + p = spawn_asyncio_repl() p.stdin.write(user_input) output = kill_python(p) self.assertIn("ZeroDivisionError", output) @@ -371,7 +380,7 @@ def test_toplevel_contextvars_sync(self): var = ContextVar("var", default="failed") var.set("ok") """) - p = spawn_repl("-m", "asyncio") + p = spawn_asyncio_repl() p.stdin.write(user_input) user_input2 = dedent(""" print(f"toplevel contextvar test: {var.get()}") @@ -387,7 +396,7 @@ def test_toplevel_contextvars_async(self): from contextvars import ContextVar var = ContextVar('var', default='failed') """) - p = spawn_repl("-m", "asyncio") + p = spawn_asyncio_repl() p.stdin.write(user_input) user_input2 = "async def set_var(): var.set('ok')\n" p.stdin.write(user_input2) From 95953b692db6cbd88139de12d81fb123293ec2d5 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Thu, 23 Oct 2025 16:35:21 +0100 Subject: [PATCH 263/373] gh-140471: Fix buffer overflow in AST node initialization with malformed `_fields` (#140506) --- Lib/test/test_ast/test_ast.py | 9 +++++++++ .../2025-10-23-16-05-50.gh-issue-140471.Ax_aXn.rst | 2 ++ Parser/asdl_c.py | 4 ++-- Python/Python-ast.c | 4 ++-- 4 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-23-16-05-50.gh-issue-140471.Ax_aXn.rst diff --git a/Lib/test/test_ast/test_ast.py b/Lib/test/test_ast/test_ast.py index 1e6f6007430..5fdb3a458ae 100644 --- a/Lib/test/test_ast/test_ast.py +++ b/Lib/test/test_ast/test_ast.py @@ -3308,6 +3308,15 @@ class MoreFieldsThanTypes(ast.AST): self.assertEqual(obj.a, 1) self.assertEqual(obj.b, 2) + def test_malformed_fields_with_bytes(self): + class BadFields(ast.AST): + _fields = (b'\xff'*64,) + _field_types = {'a': int} + + # This should not crash + with self.assertWarnsRegex(DeprecationWarning, r"Field b'\\xff\\xff.*' .*"): + obj = BadFields() + def test_complete_field_types(self): class _AllFieldTypes(ast.AST): _fields = ('a', 'b') diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-23-16-05-50.gh-issue-140471.Ax_aXn.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-23-16-05-50.gh-issue-140471.Ax_aXn.rst new file mode 100644 index 00000000000..afa9326fff3 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-23-16-05-50.gh-issue-140471.Ax_aXn.rst @@ -0,0 +1,2 @@ +Fix potential buffer overflow in :class:`ast.AST` node initialization when +encountering malformed :attr:`~ast.AST._fields` containing non-:class:`str`. diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index dba20226c32..3e252cbc488 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -1009,7 +1009,7 @@ def visitModule(self, mod): else { if (PyErr_WarnFormat( PyExc_DeprecationWarning, 1, - "Field '%U' is missing from %.400s._field_types. " + "Field %R is missing from %.400s._field_types. " "This will become an error in Python 3.15.", name, Py_TYPE(self)->tp_name ) < 0) { @@ -1044,7 +1044,7 @@ def visitModule(self, mod): // simple field (e.g., identifier) if (PyErr_WarnFormat( PyExc_DeprecationWarning, 1, - "%.400s.__init__ missing 1 required positional argument: '%U'. " + "%.400s.__init__ missing 1 required positional argument: %R. " "This will become an error in Python 3.15.", Py_TYPE(self)->tp_name, name ) < 0) { diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 660bc598a48..aac24ed7d3c 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -5293,7 +5293,7 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) else { if (PyErr_WarnFormat( PyExc_DeprecationWarning, 1, - "Field '%U' is missing from %.400s._field_types. " + "Field %R is missing from %.400s._field_types. " "This will become an error in Python 3.15.", name, Py_TYPE(self)->tp_name ) < 0) { @@ -5328,7 +5328,7 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) // simple field (e.g., identifier) if (PyErr_WarnFormat( PyExc_DeprecationWarning, 1, - "%.400s.__init__ missing 1 required positional argument: '%U'. " + "%.400s.__init__ missing 1 required positional argument: %R. " "This will become an error in Python 3.15.", Py_TYPE(self)->tp_name, name ) < 0) { From 61e759c2ee521ccf817293d6150094b618fbeee5 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 23 Oct 2025 16:45:57 +0100 Subject: [PATCH 264/373] GH-135904: JIT compiler: Support 19 bit branch instructions on AArch64 for Mach-O. (GH-140453) * Insert labels into assembly for custom relocation during stencil creation. --- ...-10-22-11-30-16.gh-issue-135904.3WE5oW.rst | 3 + Tools/jit/_optimizers.py | 61 ++++++++++++++----- Tools/jit/_stencils.py | 12 ++++ Tools/jit/_targets.py | 3 +- 4 files changed, 62 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-22-11-30-16.gh-issue-135904.3WE5oW.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-22-11-30-16.gh-issue-135904.3WE5oW.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-22-11-30-16.gh-issue-135904.3WE5oW.rst new file mode 100644 index 00000000000..b52a57dba4a --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-22-11-30-16.gh-issue-135904.3WE5oW.rst @@ -0,0 +1,3 @@ +Add special labels to the assembly created during stencil creation to +support relocations that the native object file format does not support. +Specifically, 19 bit branches for AArch64 in Mach-O object files. diff --git a/Tools/jit/_optimizers.py b/Tools/jit/_optimizers.py index 866417398b0..0adc550ba5e 100644 --- a/Tools/jit/_optimizers.py +++ b/Tools/jit/_optimizers.py @@ -9,7 +9,7 @@ _RE_NEVER_MATCH = re.compile(r"(?!)") # Dictionary mapping branch instructions to their inverted branch instructions. # If a branch cannot be inverted, the value is None: -_X86_BRANCHES = { +_X86_BRANCH_NAMES = { # https://www.felixcloutier.com/x86/jcc "ja": "jna", "jae": "jnae", @@ -37,7 +37,11 @@ "loopz": None, } # Update with all of the inverted branches, too: -_X86_BRANCHES |= {v: k for k, v in _X86_BRANCHES.items() if v} +_X86_BRANCH_NAMES |= {v: k for k, v in _X86_BRANCH_NAMES.items() if v} +# No custom relocations needed +_X86_BRANCHES: dict[str, tuple[str | None, str | None]] = { + k: (v, None) for k, v in _X86_BRANCH_NAMES.items() +} _AARCH64_COND_CODES = { # https://developer.arm.com/documentation/dui0801/b/CJAJIHAD?lang=en @@ -58,12 +62,15 @@ "hi": "ls", "ls": "hi", } +# MyPy doesn't understand that a invariant variable can be initialized by a covariant value +CUSTOM_AARCH64_BRANCH19: str | None = "CUSTOM_AARCH64_BRANCH19" + # Branches are either b.{cond} or bc.{cond} -_AARCH64_BRANCHES = { - "b." + cond: ("b." + inverse if inverse else None) +_AARCH64_BRANCHES: dict[str, tuple[str | None, str | None]] = { + "b." + cond: (("b." + inverse if inverse else None), CUSTOM_AARCH64_BRANCH19) for (cond, inverse) in _AARCH64_COND_CODES.items() } | { - "bc." + cond: ("bc." + inverse if inverse else None) + "bc." + cond: (("bc." + inverse if inverse else None), CUSTOM_AARCH64_BRANCH19) for (cond, inverse) in _AARCH64_COND_CODES.items() } @@ -113,7 +120,8 @@ class Optimizer: r'\s*(?P
') return ''.join(v) - def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None): + def _format_html_page(self, theyear, content, css, encoding): """ - Return a formatted year as a complete HTML page. + Return a complete HTML page with the given content. """ if encoding is None: encoding = 'utf-8' @@ -597,11 +597,25 @@ def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None): a(f'\n') a('\n') a('\n') - a(self.formatyear(theyear, width)) + a(content) a('\n') a('\n') return ''.join(v).encode(encoding, "xmlcharrefreplace") + def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None): + """ + Return a formatted year as a complete HTML page. + """ + content = self.formatyear(theyear, width) + return self._format_html_page(theyear, content, css, encoding) + + def formatmonthpage(self, theyear, themonth, width=3, css='calendar.css', encoding=None): + """ + Return a formatted month as a complete HTML page. + """ + content = self.formatmonth(theyear, themonth, width) + return self._format_html_page(theyear, content, css, encoding) + class different_locale: def __init__(self, locale): @@ -886,7 +900,7 @@ def main(args=None): parser.add_argument( "month", nargs='?', type=int, - help="month number (1-12, text only)" + help="month number (1-12)" ) options = parser.parse_args(args) @@ -899,9 +913,6 @@ def main(args=None): today = datetime.date.today() if options.type == "html": - if options.month: - parser.error("incorrect number of arguments") - sys.exit(1) if options.locale: cal = LocaleHTMLCalendar(locale=locale) else: @@ -912,10 +923,14 @@ def main(args=None): encoding = 'utf-8' optdict = dict(encoding=encoding, css=options.css) write = sys.stdout.buffer.write + if options.year is None: write(cal.formatyearpage(today.year, **optdict)) else: - write(cal.formatyearpage(options.year, **optdict)) + if options.month: + write(cal.formatmonthpage(options.year, options.month, **optdict)) + else: + write(cal.formatyearpage(options.year, **optdict)) else: if options.locale: cal = _CLIDemoLocaleCalendar(highlight_day=today, locale=locale) diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py index 020f9d61cae..fe9a59d335b 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -245,6 +245,34 @@ """ +result_2009_6_html = """\ + + + + + +Calendar for 2009 + + + + + + + + + + + + +
June 2009
MonTueWedThuFriSatSun
1234567
891011121314
15161718192021
22232425262728
2930     
+ + +""" + result_2004_days = [ [[[0, 0, 0, 1, 2, 3, 4], [5, 6, 7, 8, 9, 10, 11], @@ -506,6 +534,13 @@ def test_format(self): calendar.format(["1", "2", "3"], colwidth=3, spacing=1) self.assertEqual(out.getvalue().strip(), "1 2 3") + def test_format_html_year_with_month(self): + self.assertEqual( + calendar.HTMLCalendar().formatmonthpage(2009, 6).decode("ascii"), + result_2009_6_html + ) + + class CalendarTestCase(unittest.TestCase): def test_deprecation_warning(self): @@ -1102,7 +1137,6 @@ def test_illegal_arguments(self): self.assertFailure('2004', '1', 'spam') self.assertFailure('2004', '1', '1') self.assertFailure('2004', '1', '1', 'spam') - self.assertFailure('-t', 'html', '2004', '1') def test_output_current_year(self): for run in self.runners: diff --git a/Misc/NEWS.d/next/Library/2025-10-16-22-49-16.gh-issue-140212.llBNd0.rst b/Misc/NEWS.d/next/Library/2025-10-16-22-49-16.gh-issue-140212.llBNd0.rst new file mode 100644 index 00000000000..5563d077171 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-16-22-49-16.gh-issue-140212.llBNd0.rst @@ -0,0 +1,5 @@ +Calendar's HTML formatting now accepts year and month as options. +Previously, running ``python -m calendar -t html 2025 10`` would result in an +error message. It now generates an HTML document displaying the calendar for +the specified month. +Contributed by Pål Grønås Drange. From a17c57eee5b5cc81390750d07e4800b19c0c3084 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 31 Oct 2025 17:44:02 +0200 Subject: [PATCH 351/373] gh-137836: Support more RAWTEXT and PLAINTEXT elements in HTMLParser (GH-137837) * the "plaintext" element * the RAWTEXT elements "xmp", "iframe", "noembed" and "noframes" * optionally RAWTEXT (if scripting=True) element "noscript" --- Doc/library/html.parser.rst | 33 +-- Lib/html/parser.py | 24 +- Lib/test/test_htmlparser.py | 217 ++++++++++-------- ...-08-15-23-08-44.gh-issue-137836.b55rhh.rst | 3 + 4 files changed, 163 insertions(+), 114 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2025-08-15-23-08-44.gh-issue-137836.b55rhh.rst diff --git a/Doc/library/html.parser.rst b/Doc/library/html.parser.rst index dd67fc34e85..341a8337ba2 100644 --- a/Doc/library/html.parser.rst +++ b/Doc/library/html.parser.rst @@ -15,14 +15,18 @@ This module defines a class :class:`HTMLParser` which serves as the basis for parsing text files formatted in HTML (HyperText Mark-up Language) and XHTML. -.. class:: HTMLParser(*, convert_charrefs=True) +.. class:: HTMLParser(*, convert_charrefs=True, scripting=False) Create a parser instance able to parse invalid markup. - If *convert_charrefs* is ``True`` (the default), all character - references (except the ones in ``script``/``style`` elements) are + If *convert_charrefs* is true (the default), all character + references (except the ones in elements like ``script`` and ``style``) are automatically converted to the corresponding Unicode characters. + If *scripting* is false (the default), the content of the ``noscript`` + element is parsed normally; if it's true, it's returned as is without + being parsed. + An :class:`.HTMLParser` instance is fed HTML data and calls handler methods when start tags, end tags, text, comments, and other markup elements are encountered. The user should subclass :class:`.HTMLParser` and override its @@ -37,6 +41,9 @@ parsing text files formatted in HTML (HyperText Mark-up Language) and XHTML. .. versionchanged:: 3.5 The default value for argument *convert_charrefs* is now ``True``. + .. versionchanged:: 3.14.1 + Added the *scripting* parameter. + Example HTML Parser Application ------------------------------- @@ -161,15 +168,15 @@ implementations do nothing (except for :meth:`~HTMLParser.handle_startendtag`): .. method:: HTMLParser.handle_data(data) This method is called to process arbitrary data (e.g. text nodes and the - content of ```` and ````). + content of elements like ``script`` and ``style``). .. method:: HTMLParser.handle_entityref(name) This method is called to process a named character reference of the form ``&name;`` (e.g. ``>``), where *name* is a general entity reference - (e.g. ``'gt'``). This method is never called if *convert_charrefs* is - ``True``. + (e.g. ``'gt'``). + This method is only called if *convert_charrefs* is false. .. method:: HTMLParser.handle_charref(name) @@ -177,8 +184,8 @@ implementations do nothing (except for :meth:`~HTMLParser.handle_startendtag`): This method is called to process decimal and hexadecimal numeric character references of the form :samp:`&#{NNN};` and :samp:`&#x{NNN};`. For example, the decimal equivalent for ``>`` is ``>``, whereas the hexadecimal is ``>``; - in this case the method will receive ``'62'`` or ``'x3E'``. This method - is never called if *convert_charrefs* is ``True``. + in this case the method will receive ``'62'`` or ``'x3E'``. + This method is only called if *convert_charrefs* is false. .. method:: HTMLParser.handle_comment(data) @@ -292,8 +299,8 @@ Parsing an element with a few attributes and a title: Data : Python End tag : h1 -The content of ``script`` and ``style`` elements is returned as is, without -further parsing: +The content of elements like ``script`` and ``style`` is returned as is, +without further parsing: .. doctest:: @@ -304,10 +311,10 @@ further parsing: End tag : style >>> parser.feed('') + ... 'alert("hello! ☺");') Start tag: script attr: ('type', 'text/javascript') - Data : alert("hello!"); + Data : alert("hello! ☺"); End tag : script Parsing comments: @@ -336,7 +343,7 @@ correct char (note: these 3 references are all equivalent to ``'>'``): Feeding incomplete chunks to :meth:`~HTMLParser.feed` works, but :meth:`~HTMLParser.handle_data` might be called more than once -(unless *convert_charrefs* is set to ``True``): +if *convert_charrefs* is false: .. doctest:: diff --git a/Lib/html/parser.py b/Lib/html/parser.py index 5d7050dad23..e50620de800 100644 --- a/Lib/html/parser.py +++ b/Lib/html/parser.py @@ -127,17 +127,25 @@ class HTMLParser(_markupbase.ParserBase): argument. """ - CDATA_CONTENT_ELEMENTS = ("script", "style") + # See the HTML5 specs section "13.4 Parsing HTML fragments". + # https://html.spec.whatwg.org/multipage/parsing.html#parsing-html-fragments + # CDATA_CONTENT_ELEMENTS are parsed in RAWTEXT mode + CDATA_CONTENT_ELEMENTS = ("script", "style", "xmp", "iframe", "noembed", "noframes") RCDATA_CONTENT_ELEMENTS = ("textarea", "title") - def __init__(self, *, convert_charrefs=True): + def __init__(self, *, convert_charrefs=True, scripting=False): """Initialize and reset this instance. - If convert_charrefs is True (the default), all character references + If convert_charrefs is true (the default), all character references are automatically converted to the corresponding Unicode characters. + + If *scripting* is false (the default), the content of the + ``noscript`` element is parsed normally; if it's true, + it's returned as is without being parsed. """ super().__init__() self.convert_charrefs = convert_charrefs + self.scripting = scripting self.reset() def reset(self): @@ -172,7 +180,9 @@ def get_starttag_text(self): def set_cdata_mode(self, elem, *, escapable=False): self.cdata_elem = elem.lower() self._escapable = escapable - if escapable and not self.convert_charrefs: + if self.cdata_elem == 'plaintext': + self.interesting = re.compile(r'\z') + elif escapable and not self.convert_charrefs: self.interesting = re.compile(r'&|])' % self.cdata_elem, re.IGNORECASE|re.ASCII) else: @@ -444,8 +454,10 @@ def parse_starttag(self, i): self.handle_startendtag(tag, attrs) else: self.handle_starttag(tag, attrs) - if tag in self.CDATA_CONTENT_ELEMENTS: - self.set_cdata_mode(tag) + if (tag in self.CDATA_CONTENT_ELEMENTS or + (self.scripting and tag == "noscript") or + tag == "plaintext"): + self.set_cdata_mode(tag, escapable=False) elif tag in self.RCDATA_CONTENT_ELEMENTS: self.set_cdata_mode(tag, escapable=True) return endpos diff --git a/Lib/test/test_htmlparser.py b/Lib/test/test_htmlparser.py index 6a1d69335a0..19dde9362a4 100644 --- a/Lib/test/test_htmlparser.py +++ b/Lib/test/test_htmlparser.py @@ -8,6 +8,18 @@ from test import support +SAMPLE_RCDATA = ( + '' + "" + '' + '' + '' + '\u2603' +) + +SAMPLE_RAWTEXT = SAMPLE_RCDATA + '&☺' + + class EventCollector(html.parser.HTMLParser): def __init__(self, *args, autocdata=False, **kw): @@ -293,30 +305,20 @@ def test_get_starttag_text(self): 'Date().getTime()+\'"><\\/s\'+\'cript>\');\n//]]>'), '\n\n', '', - 'foo = ""', - 'foo = ""', - 'foo = ""', - 'foo = ""', - 'foo = ""', - 'foo = ""', ]) def test_script_content(self, content): s = f'' - self._run_check(s, [("starttag", "script", []), - ("data", content), - ("endtag", "script")]) + self._run_check(s, [ + ("starttag", "script", []), + ("data", content), + ("endtag", "script"), + ]) @support.subTests('content', [ 'a::before { content: ""; }', 'a::before { content: "¬-an-entity-ref;"; }', 'a::before { content: ""; }', 'a::before { content: "\u2603"; }', - 'a::before { content: "< /style>"; }', - 'a::before { content: ""; }', - 'a::before { content: ""; }', - 'a::before { content: ""; }', - 'a::before { content: ""; }', - 'a::before { content: ""; }', ]) def test_style_content(self, content): s = f'' @@ -324,47 +326,59 @@ def test_style_content(self, content): ("data", content), ("endtag", "style")]) - @support.subTests('content', [ - '', - "", - '', - '', - '', - '\u2603', - '< /title>', - '', - '', - '', - '', - '', - ]) - def test_title_content(self, content): - source = f"{content}" + @support.subTests('tag', ['title', 'textarea']) + def test_rcdata_content(self, tag): + source = f"<{tag}>{SAMPLE_RCDATA}" self._run_check(source, [ - ("starttag", "title", []), - ("data", content), - ("endtag", "title"), + ("starttag", tag, []), + ("data", SAMPLE_RCDATA), + ("endtag", tag), + ]) + source = f"<{tag}>&" + self._run_check(source, [ + ("starttag", tag, []), + ('entityref', 'amp'), + ("endtag", tag), ]) - @support.subTests('content', [ - '', - "", - '', - '', - '', - '\u2603', - '< /textarea>', - '', - '', - '', - '', - ]) - def test_textarea_content(self, content): - source = f"" + @support.subTests('tag', + ['style', 'xmp', 'iframe', 'noembed', 'noframes', 'script']) + def test_rawtext_content(self, tag): + source = f"<{tag}>{SAMPLE_RAWTEXT}" self._run_check(source, [ - ("starttag", "textarea", []), + ("starttag", tag, []), + ("data", SAMPLE_RAWTEXT), + ("endtag", tag), + ]) + + def test_noscript_content(self): + source = f"" + # scripting=False -- normal mode + self._run_check(source, [ + ('starttag', 'noscript', []), + ('comment', ' not a comment '), + ('starttag', 'not', [('a', 'start tag')]), + ('unknown decl', 'CDATA[not a cdata'), + ('comment', 'not a bogus comment'), + ('endtag', 'not'), + ('data', '☃'), + ('entityref', 'amp'), + ('charref', '9786'), + ('endtag', 'noscript'), + ]) + # scripting=True -- RAWTEXT mode + self._run_check(source, [ + ("starttag", "noscript", []), + ("data", SAMPLE_RAWTEXT), + ("endtag", "noscript"), + ], collector=EventCollector(scripting=True)) + + def test_plaintext_content(self): + content = SAMPLE_RAWTEXT + '' # not closing + source = f"{content}" + self._run_check(source, [ + ("starttag", "plaintext", []), ("data", content), - ("endtag", "textarea"), ]) @support.subTests('endtag', ['script', 'SCRIPT', 'script ', 'script\n', @@ -381,52 +395,65 @@ def test_script_closing_tag(self, endtag): ("endtag", "script")], collector=EventCollectorNoNormalize(convert_charrefs=False)) - @support.subTests('endtag', ['style', 'STYLE', 'style ', 'style\n', - 'style/', 'style foo=bar', 'style foo=">"']) - def test_style_closing_tag(self, endtag): - content = """ - b::before { content: "<!-- not a comment -->"; } - p::before { content: "&not-an-entity-ref;"; } - a::before { content: "<i>"; } - a::after { content: "</i>"; } - """ - s = f'<StyLE>{content}</{endtag}>' - self._run_check(s, [("starttag", "style", []), - ("data", content), - ("endtag", "style")], - collector=EventCollectorNoNormalize(convert_charrefs=False)) + @support.subTests('tag', [ + 'script', 'style', 'xmp', 'iframe', 'noembed', 'noframes', + 'textarea', 'title', 'noscript', + ]) + def test_closing_tag(self, tag): + for endtag in [tag, tag.upper(), f'{tag} ', f'{tag}\n', + f'{tag}/', f'{tag} foo=bar', f'{tag} foo=">"']: + content = "<!-- not a comment --><i>Spam</i>" + s = f'<{tag.upper()}>{content}</{endtag}>' + self._run_check(s, [ + ("starttag", tag, []), + ('data', content), + ("endtag", tag), + ], collector=EventCollectorNoNormalize(convert_charrefs=False, scripting=True)) - @support.subTests('endtag', ['title', 'TITLE', 'title ', 'title\n', - 'title/', 'title foo=bar', 'title foo=">"']) - def test_title_closing_tag(self, endtag): - content = "<!-- not a comment --><i>Egg &amp; Spam</i>" - s = f'<TitLe>{content}</{endtag}>' - self._run_check(s, [("starttag", "title", []), - ('data', '<!-- not a comment --><i>Egg & Spam</i>'), - ("endtag", "title")], - collector=EventCollectorNoNormalize(convert_charrefs=True)) - self._run_check(s, [("starttag", "title", []), - ('data', '<!-- not a comment --><i>Egg '), - ('entityref', 'amp'), - ('data', ' Spam</i>'), - ("endtag", "title")], - collector=EventCollectorNoNormalize(convert_charrefs=False)) + @support.subTests('tag', [ + 'script', 'style', 'xmp', 'iframe', 'noembed', 'noframes', + 'textarea', 'title', 'noscript', + ]) + def test_invalid_closing_tag(self, tag): + content = ( + f'< /{tag}>' + f'</ {tag}>' + f'</{tag}x>' + f'</{tag}\v>' + f'</{tag}\xa0>' + ) + source = f"<{tag}>{content}</{tag}>" + self._run_check(source, [ + ("starttag", tag, []), + ("data", content), + ("endtag", tag), + ], collector=EventCollector(convert_charrefs=False, scripting=True)) - @support.subTests('endtag', ['textarea', 'TEXTAREA', 'textarea ', 'textarea\n', - 'textarea/', 'textarea foo=bar', 'textarea foo=">"']) - def test_textarea_closing_tag(self, endtag): - content = "<!-- not a comment --><i>Egg &amp; Spam</i>" - s = f'<TexTarEa>{content}</{endtag}>' - self._run_check(s, [("starttag", "textarea", []), - ('data', '<!-- not a comment --><i>Egg & Spam</i>'), - ("endtag", "textarea")], - collector=EventCollectorNoNormalize(convert_charrefs=True)) - self._run_check(s, [("starttag", "textarea", []), - ('data', '<!-- not a comment --><i>Egg '), - ('entityref', 'amp'), - ('data', ' Spam</i>'), - ("endtag", "textarea")], - collector=EventCollectorNoNormalize(convert_charrefs=False)) + @support.subTests('tag,endtag', [ + ('title', 'tıtle'), + ('style', 'ſtyle'), + ('style', 'ſtyle'), + ('style', 'style'), + ('iframe', 'ıframe'), + ('noframes', 'noframeſ'), + ('noscript', 'noſcript'), + ('noscript', 'noscrıpt'), + ('script', 'ſcript'), + ('script', 'scrıpt'), + ]) + def test_invalid_nonascii_closing_tag(self, tag, endtag): + content = f"<br></{endtag}>" + source = f"<{tag}>{content}" + self._run_check(source, [ + ("starttag", tag, []), + ("data", content), + ], collector=EventCollector(convert_charrefs=False, scripting=True)) + source = f"<{tag}>{content}</{tag}>" + self._run_check(source, [ + ("starttag", tag, []), + ("data", content), + ("endtag", tag), + ], collector=EventCollector(convert_charrefs=False, scripting=True)) @support.subTests('tail,end', [ ('', False), diff --git a/Misc/NEWS.d/next/Security/2025-08-15-23-08-44.gh-issue-137836.b55rhh.rst b/Misc/NEWS.d/next/Security/2025-08-15-23-08-44.gh-issue-137836.b55rhh.rst new file mode 100644 index 00000000000..c30c9439a76 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2025-08-15-23-08-44.gh-issue-137836.b55rhh.rst @@ -0,0 +1,3 @@ +Add support of the "plaintext" element, RAWTEXT elements "xmp", "iframe", +"noembed" and "noframes", and optionally RAWTEXT element "noscript" in +:class:`html.parser.HTMLParser`. From ede5693be1cefb859522b246897b6835c87ed6d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= <lains@riseup.net> Date: Sat, 1 Nov 2025 00:39:48 +0000 Subject: [PATCH 352/373] GH-119668: expose importlib.machinery.NamespacePath (#119669) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * GH-119668: expose importlib.NamespacePath Signed-off-by: Filipe Laíns <lains@riseup.net> * add news Signed-off-by: Filipe Laíns <lains@riseup.net> * add to docs Signed-off-by: Filipe Laíns <lains@riseup.net> * Apply suggestions from code review Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> * Fix news (importlib.NamespacePath > importlib.machinery.NamespacePath) Signed-off-by: Filipe Laíns <lains@riseup.net> * Link to module.__path__ in NamespacePath docs Signed-off-by: Filipe Laíns <lains@riseup.net> * Mention the path argument in the documentation Signed-off-by: Filipe Laíns <lains@riseup.net> * Simplify docs text Signed-off-by: Filipe Laíns <lains@riseup.net> * Highlight argument names in docs text Signed-off-by: Filipe Laíns <lains@riseup.net> * Update Lib/importlib/_bootstrap_external.py Co-authored-by: Brett Cannon <brett@python.org> * Rewrite NamespacePath's doc Signed-off-by: Filipe Laíns <lains@riseup.net> * Specify path_finder's type in the NamespacePath docstring Signed-off-by: Filipe Laíns <lains@riseup.net> * Fix doc tests Signed-off-by: Filipe Laíns <lains@riseup.net> * Apply suggestions from code review Co-authored-by: Barry Warsaw <barry@python.org> * Fix doc lint Signed-off-by: Filipe Laíns <lains@riseup.net> * Update Doc/library/importlib.rst Co-authored-by: Brett Cannon <brett@python.org> --------- Signed-off-by: Filipe Laíns <lains@riseup.net> Co-authored-by: Brett Cannon <brett@python.org> Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> Co-authored-by: Barry Warsaw <barry@python.org> --- Doc/library/importlib.rst | 30 +++++++++++++++++ Lib/importlib/_bootstrap_external.py | 32 ++++++++++++------- Lib/importlib/machinery.py | 1 + ...-05-28-17-14-30.gh-issue-119668.RrIGpn.rst | 1 + 4 files changed, 53 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-05-28-17-14-30.gh-issue-119668.RrIGpn.rst diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 19296eb247a..602a7100a12 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1013,6 +1013,36 @@ find and load modules. :exc:`ImportError` is raised. +.. class:: NamespacePath(name, path, path_finder) + + Represents a :term:`namespace package`'s path (:attr:`module.__path__`). + + When its ``__path__`` value is accessed it will be recomputed if necessary. + This keeps it in-sync with the global state (:attr:`sys.modules`). + + The *name* argument is the name of the namespace module. + + The *path* argument is the initial path value. + + The *path_finder* argument is the callable used to recompute the path value. + The callable has the same signature as :meth:`importlib.abc.MetaPathFinder.find_spec`. + + When the parent's :attr:`module.__path__` attribute is updated, the path + value is recomputed. + + If the parent module is missing from :data:`sys.modules`, then + :exc:`ModuleNotFoundError` will be raised. + + For top-level modules, the parent module's path is :data:`sys.path`. + + .. note:: + + :meth:`PathFinder.invalidate_caches` invalidates :class:`NamespacePath`, + forcing the path value to be recomputed next time it is accessed. + + .. versionadded:: next + + .. class:: SourceFileLoader(fullname, path) A concrete implementation of :class:`importlib.abc.SourceLoader` by diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index a1319c0f6a9..035ae0fcae1 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -1086,12 +1086,18 @@ def get_filename(self, fullname): return self.path -class _NamespacePath: - """Represents a namespace package's path. It uses the module name - to find its parent module, and from there it looks up the parent's - __path__. When this changes, the module's own path is recomputed, - using path_finder. For top-level modules, the parent module's path - is sys.path.""" +class NamespacePath: + """Represents a namespace package's path. + + It uses the module *name* to find its parent module, and from there it looks + up the parent's __path__. When this changes, the module's own path is + recomputed, using *path_finder*. The initial value is set to *path*. + + For top-level modules, the parent module's path is sys.path. + + *path_finder* should be a callable with the same signature as + MetaPathFinder.find_spec((fullname, path, target=None) -> spec). + """ # When invalidate_caches() is called, this epoch is incremented # https://bugs.python.org/issue45703 @@ -1153,7 +1159,7 @@ def __len__(self): return len(self._recalculate()) def __repr__(self): - return f'_NamespacePath({self._path!r})' + return f'NamespacePath({self._path!r})' def __contains__(self, item): return item in self._recalculate() @@ -1162,12 +1168,16 @@ def append(self, item): self._path.append(item) +# For backwards-compatibility for anyone desperate enough to get at the class back in the day. +_NamespacePath = NamespacePath + + # This class is actually exposed publicly in a namespace package's __loader__ # attribute, so it should be available through a non-private name. # https://github.com/python/cpython/issues/92054 class NamespaceLoader: def __init__(self, name, path, path_finder): - self._path = _NamespacePath(name, path, path_finder) + self._path = NamespacePath(name, path, path_finder) def is_package(self, fullname): return True @@ -1222,9 +1232,9 @@ def invalidate_caches(): del sys.path_importer_cache[name] elif hasattr(finder, 'invalidate_caches'): finder.invalidate_caches() - # Also invalidate the caches of _NamespacePaths + # Also invalidate the caches of NamespacePaths # https://bugs.python.org/issue45703 - _NamespacePath._epoch += 1 + NamespacePath._epoch += 1 from importlib.metadata import MetadataPathFinder MetadataPathFinder.invalidate_caches() @@ -1310,7 +1320,7 @@ def find_spec(cls, fullname, path=None, target=None): # We found at least one namespace path. Return a spec which # can create the namespace package. spec.origin = None - spec.submodule_search_locations = _NamespacePath(fullname, namespace_path, cls._get_spec) + spec.submodule_search_locations = NamespacePath(fullname, namespace_path, cls._get_spec) return spec else: return None diff --git a/Lib/importlib/machinery.py b/Lib/importlib/machinery.py index 63d726445c3..023f77d750f 100644 --- a/Lib/importlib/machinery.py +++ b/Lib/importlib/machinery.py @@ -16,6 +16,7 @@ from ._bootstrap_external import ExtensionFileLoader from ._bootstrap_external import AppleFrameworkLoader from ._bootstrap_external import NamespaceLoader +from ._bootstrap_external import NamespacePath def all_suffixes(): diff --git a/Misc/NEWS.d/next/Library/2024-05-28-17-14-30.gh-issue-119668.RrIGpn.rst b/Misc/NEWS.d/next/Library/2024-05-28-17-14-30.gh-issue-119668.RrIGpn.rst new file mode 100644 index 00000000000..87cdf8d89d5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-05-28-17-14-30.gh-issue-119668.RrIGpn.rst @@ -0,0 +1 @@ +Publicly expose and document :class:`importlib.machinery.NamespacePath`. From d440a0f96c6e87ee93810e0184068e90087af466 Mon Sep 17 00:00:00 2001 From: Damian Shaw <damian.peter.shaw@gmail.com> Date: Sat, 1 Nov 2025 06:25:19 -0400 Subject: [PATCH 353/373] gh-140874: Upgrade bundled pip to 25.3 (GH-140876) Upgrade bundled pip to 25.3 --- Lib/ensurepip/__init__.py | 2 +- ...none-any.whl => pip-25.3-py3-none-any.whl} | Bin 1752557 -> 1778622 bytes ...-11-01-00-36-14.gh-issue-140874.eAWt3K.rst | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) rename Lib/ensurepip/_bundled/{pip-25.2-py3-none-any.whl => pip-25.3-py3-none-any.whl} (79%) create mode 100644 Misc/NEWS.d/next/Library/2025-11-01-00-36-14.gh-issue-140874.eAWt3K.rst diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index 0552cf55db1..f9f905f46ff 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -10,7 +10,7 @@ __all__ = ["version", "bootstrap"] -_PIP_VERSION = "25.2" +_PIP_VERSION = "25.3" # Directory of system wheel packages. Some Linux distribution packaging # policies recommend against bundling dependencies. For example, Fedora diff --git a/Lib/ensurepip/_bundled/pip-25.2-py3-none-any.whl b/Lib/ensurepip/_bundled/pip-25.3-py3-none-any.whl similarity index 79% rename from Lib/ensurepip/_bundled/pip-25.2-py3-none-any.whl rename to Lib/ensurepip/_bundled/pip-25.3-py3-none-any.whl index e14bb3f37c0ff4cfa105c71d5e536f2f496cb160..755e1aa0c3dc5a90defba25ab35f4b8877ca95ab 100644 GIT binary patch delta 300478 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%0D(IUld=^fBUkErF+_Yolzw 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%0DTRlH~DS|IA%jYlI{>V#_t!XTn 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 VRG_kNo7^_py%hh)ZzkP({~xhCe`^2$ delta 300566 zcmZ5{Q*@wBux)JHwr$VEwr$&!e8I$=Boo`VZA@(2wsrq=@6);c(%schRcrOG-Mg#0 zN9-=&Z2%=%a0m<#5D*v;`{FMhdGBhd=l^!bpdcW~|IJR;PRxde)(+NghK5W|-h%O> zvV$TBA=mfxV$AKis#+LLxjW)FWJH}22o39Q{4*Az6z;Sy`||Xh2xea10zw8O-oH`W zrKyKeNMa}(#3=sZahjtYi@iu-y{~_wmVZ_O+nQ5tSo}Dq`QxM1)<#nKIr$%u!Z*pV zlkJs+dcvY)_5>?x>W{P3puT^waMSicrmt(_ty7H$rQq}}QXzI(en=mRk>tR}4ktiE z@Vd~^ZB+#_Tg2FBV5z~|D4XIPz(Jt;ERxCV=OjGM_YvP~rL+yMv7J+}<t=z}BR~6d zkn4K3V3;5gbMXn6iTh9-wwbA#+tOz8CI@|l^;;3n^|UV4yY~F=C^fKy9)d#rPrMok zz#+i@?<$c3>gfNOkf3cr(f;QUq0P538W9A9i4g<@4<yAX0gnLK#M5`(Xuj3&0m~!q zxAm6WE18n7&RR)Em)F-e9k*sr$A^;KqtFmYfE1C&R{!qmfDeEW&Aw2`QJGv32<nLX z+!{(y#^^fxS8i1Ds85^W!)!CjM+w(#OF^%$>a5SjNzbRfv@C~NG-nru<Ag6hJ2s}k z<(q8Yq2Q@vhD#4T+mmzdyKh?ct5Z{ZRM}M5fKG05{rj|O>n{6O)uCR5^?CgRxo9Z3 zJEfCT_efqnB9qI>w>u>yKHp3A$OkO(&i-m=sxkhTE-ZK<a@C2no=kQW<Fq5QaT5@m z&mUmOq|mS|oYnnxzWN#m+Yuk*V1!>0)@(O_p|BuMK92>IIwf&;p{wMfF-GJ}UIy$D z1z?TCxsPHpS*j-{LJ6*!BX%x#%G{UBVX|m|vxaRq=1*Ph5yTcOlk34}H5nPt_?bBL zhJMEUa~w37KwX2Mgs*N>EJ5MlQs+YtCdXyl>L;jDNck&MW&1$K_jz;nIWoV0)8*oa zRrsQ*DRT>aD3q@^*N=sqHO1eT{|i0bFV(qNucR@*hIjgg;+k+LM}{33x#8$wJO|72 z=k`cmgbnV&-2-7Fs{9G22jcL~$TV@Lt0N!*axe_k=5mbAb+om8?pPl}JR&SCGhwOD ztn=$cHB=t?-gFO*jltq128u^j$Gk?;GNLyZod*Go-xF@j8hdb8To>B|@<G$w5;C+T z_zMh20WC(kuv6VS4UFi2(OM&Y4=|ze&9GH2{y_&ztOwx~P1S2f*rX`g;CKi*+8B%6 zB9p2>fOy}q`R4|NVP$t7)}G#kXW}J8BgYlMJdtuONm;NKH!#|=y{+vP)1{bKEaJfB zL*)S&0op^_CqjiAT@l<R=6CDW)vUzkMoXy&5Z*DV@g@L9ygaT2R55-$dJ>f86cf@L zR6?W|igyXBV4r*L&_j%oH#MbJCm+KfJ}9`i30TywPTIcLzhn1UVX9<!`6R56y{#zu zzI~9RoVmMPYH;DD*hgiiS=HsIW=xe4B6q+8San^_Hr7s;lJ(WkP7qfa7qvl%o<K4a z*uo+)$dRbNFXm{9XL)GIi_n(%FBC~mUr>J&ipGSDT2SYIVgZ#syKq(<Ata!@txr}* zdVzG;q9L6Un6s`LN|~BrjbV*KNX3zeVNvMY&-j0@)JX4&QJ#3;L&b5|Qsv0Zl8b;# z(zAgWg1cOjC^Ib(dm*JI(%;>9{o_6%fyFLk1g*QfiD$ib(Bzy#BRtP#R1nRV9P6MY zSOQSp4;7l=dqdm?-r?9i`8FNt+qd=kdl3*OB$+`vqD4l2h#5inwR&*XeND~Kj*sst z+d7bbB#^YVRU7x^ABZHP0!tT4DMkP@=M(YDQz(74@-&HPf4)fas4J{uAWo!f{s>h! zE9PtU!R@!8?<=EiuFbY={tvY^@D66uJ-%{IA<?+-8ac(3ZYot|y>*e!s*HC$a%V#m zx9waswEEa~(c|i#rBbs<+ZzR7<Q?D2TP(KMQpDVjo`i(NOf)HIsOug%feDB`y|g)i zfyMXkANIT-kx+(Hm`xwmlcP<lhc2?%S*QM5u=GFU?`ob;rZ4?jU8Ykh5$hEC=m7nx zotjPqD|#F7!u&HUrJsr>+;F}xpIIoU^zF`cY`EYe+e=i~tWSMgO2rCEq&T@!&mN!X z!}ujaKB%nn6HO|Fal)q9=?9QCG-qiPCNnz6aryXKH@zvziGMcV2+w{NJUUtBQYJhE zLTob-y%tn%pPstd>c^|mXFt-=tkt8ci%_aSpxF9FnzGz$3t2)lFDzR6IBiEzY#>f7 zn9&TkfBa`r6N7;{<&g7diPqY2eyZ5QB$`ZCrVz^;{L&=Z&?iBWFB5QHbefP{J*-GO zpo~VTZ6ex8T}as|^=c3D7WA1LYA}FoD3_H#R4sEyXI%7!mkx^5X0fauFAq-F`@rV- z#K@%r#wDPXEuX4XH7ZM&qNh9{s%Q^jqO5X@L%%#z4#(soo#D$i_Oh&U%VZE_P0<XC zDL`~WoJK3)vNq7yu>u{Ay~;M>65SyPNSmATC15`hzaWa{i9F8rz3uVDN8_|cuUZ>1 zA<{Svlz#uh#mn>Se3$=*m#O0wF_IVDyu7jVquvVarlaBAMlOluE~ydvY{gUlIjvYG zNxx_1?TL*z1YJIH46_ct+dv^0sYT+kVsL`6kZ-P<=e$?yzy??yw+V=h5Ocb_JGDfy zng>@*um<WNqmP`XTA|t1r`A&wany7B1OW4xwz1)#thsQTtP5KNQ?$7CUC}|U*MAae zJ}p(3W*lT_yz9`x+-`BIY~)mC=S`rzCz>zkZsHV*wW;YBEsx!_?l(=evRMBt_Ng~A zhz5W@oUKn<paN%vNE|wM(YZ5LL@%BdlQ#XoY6lW#Qhql<Fs#gwx87z=U4Q$?bnzQ^ zqABcXpd_@6rnthinzcT4?#TYuMI&(fy&O|>oJ9_e`Luf@KnWI+fI4h3v5#nx7bwZd zFBK^sI-X-ZAH;-;@zfh7>AS-YU2BnZD0X%XOE%bB83u4EHq(*T0I=F%2xZ7C4GW|r z+|RfQz9g$+j`S>w!sp;1*ptfrW{A){i2_2Bpd=|wlbHmd@Htw_pIDXwy{@BHE7;fh z4S9tAgZ@XfI!3$=Af{Nsfy*42r10|`po_NCee3XfcaKbX)9^PBdq;}NfwYk0lWPWv zEY}3QxWI$`xh<cTm}n(DROODc<-);xSKx@^_D=b+&r<z$LL9MtY*Vcu)4|x6C-U4h z;`06<<7gk9xZ%*#3PsE7Ewhqi;eosgo92~dGl>NgKU)4>bHp`Xc0cf}wa(Y)m10(f zC$weok*iUOp?+4&cHYqVs38ve{q~AHFS`;jIKWS3$gd{b$ui8+c)A0&cjMuL28WPZ zK1^*d4GUj_#u<h%?lFGrwtRs6hOG3|G*s_mRbz>X*@A_@k%@{iRQKtpf1iP>KRtkM zK~sPa^w&ast2q^0Si|2&`QdpnZ-}Mvo{gi_i=*~Md0z$DdaTU5@(Fbl=TL?`^_47f z0y+1QFuTKPDEQ`KWJJA4bVP{=3&^$ax&%9rO7oRiVP4?6rh4O=S4j%{99o?t`G&`c z-32<5SzC<X7>G=f*3QdNp9UFom6qvno3bU1C{8>}l55R0>6OpSZD<z58ZT6~V2?pv zki>Ja^k(xq>`XDZc_PXxKTd<^Ax_1Uff<{7xXa8{Ql4z0+v7zW_P`QcbkI2?xf3te zz}qmcxNb#1%ADdX?5|m?XlzJ>+16qG&aS1c>(fvV<eZ9C`=c<qhTOiLv6BL@nsgdF z)`BA1NnSz2$tyYJ&r9mk1}hX<Nee3+lYy7$B#t~5%s$u{QbA**VNhW>9!m^Z0GwXz zX>(1QJ2%u)3tWL0VLe(XN*S%eK7gi}hrGtc`o2_B6ddFee~c*oHcHURW~dEDe`Q|p z!uM^hr%z$Cwu5LnXcay2lompP)?U@QQhyYq2mQqa!|s8HEX3R+<jY7GR{(kaEXFOn zVjNpo@9Vev>t4!u&Q?xN&-cSE@GvmaFg|Y_Ljr}S7|h-J!AkeXAIrL&3?GyiwtAww z05=ev2!BNbt~?KkOx<4HW;vc_mZ=kNaSmiSWP|6?5sF1oTVMBvB|=!iFObKe&RA1b zZ`8F|{#-4O9?Oq=e)CNyJ8w%C{o)qGLvbmNsFa(KcPfB}=ng?xPiyuVfY+=cvCb4a z$RAQ(x8TTgo0-89H9a4g4EirGeV`6nj%4lp_SC~87Vl8`7B}X1Ny1=g(R7}{)N-uj zq01FC-D|eq_wTTll2d#%{h*JEx-{>(&GkqZe%nSZ?FHd+LkgP=wz%kU%Bu-#*~=&d zf{$ZZNNCeMjV~yLo?`O>+4LjznbEKs1I;Ua?F~g&bdPD$Qw(^MDV96|l^2y$8r+|^ zxMu)=Wv%87E_}}4(5)2^s!92CpBsCr553T)!T09?v&d5D*y5RoqP{=ZDtjz`@17^q zFRYL&{|Gq6yl;>-E~z21RZOF}%#R@7J6`Z23%vr|Qc~LaSOT+w5$ULfmwr|(9S1*_ zc{&OIh%D2rcjncRpMrlTq=O=h?XG03Gy?YmFU9=_&Q&f?AxLjbX}FE2CKcE@x3pL{ zUA?1-^HbA!Lh-UXZY{hrb$EY%;Ghc1#r}uE-Oc+V4VC<?-cmV#tCVw->@@Z=cI?Bn z;hNIt%G_d=5B}qS$IlHy(U*PB`vT)Qv0RwqcsOLkSsTlG5%00*;gx<~7E>8ZcG!Y^ z*4azvk~tgmjlyZu=7tjQ4TDuzC_`N?@wDZE;I|@5jye_=D3Vp@Y~a=c*|lk{qdF5q zqw5X8$EDkl0rx|`&YDD$-`mX<vtXm&`Gx&o9(u(se4!LzL@>zk1C4c`j@^(3f*JmM zSu?_@%Tl(I@K<GU#I*Y8(#-Ey`9@sLnPnx=p)Yh=4jZp#Vp<C`M$=$&aF%CVj&`>k zP0GR?&aV`nuF6YyobN2|>vbnE>@fI&7Eoc34p|<7eOMo9+>EW{cz@Qku+$OD0M~Np z(66W~f3Pnhe(SyAU&kotoc?`Jlww~!kQoP*Zb-*5LhC<>L&EcU{?`AQ;rxL9-|xNZ zRn9X61Q3v44aYc#(126zkreDNy@`>CZAJyr+CI>$VLFAy`mLYA+HZ0Ob~AIzJE7Kk z8?J4*-=tc~($xDIss(x_9f_D>g+X9&6b2~yPK=!C;4(~ho4zzo4a#DHl#<5{*+5v1 z(${X}X_mTz7!PGj9qMC&8K2s<=AQ0Uyxwq-#*%yaalM}XG@#zpj*p&IF<hU!<Y`sw zjQ+GDajCdiVkQY&JpI*E^2${$RI+hOLg@y!NlHr5fWF(AD;tJnyndyo7<Q+Iu`w^v z#jt)$Jd>lqzvH0{A_dWUi8C64a^*Gk_f)-%hxzQxR&C2~>Up9#sh8*zesaLtiyP{g zC!$GnE$YkO1K7qqk?@`E*otNL_wv7`d%gMC+cUp=7@I%F_5*idYk&~oq2Tv-O=$Ps z^^i821g*SaA8r?Fzw8=aM_5aIkhQ_dL(`oVOwqzieC>BDBpQZ}^4A?j-}I34ME(@N z((vEvs=IMV(|v(!*1lMx+^mQl^h|g-qqf0~SeAMa060Tbcu+H=_FF)^pj?w2F4~h% zm^f)sz%LqI2HkYCXYd1Vqn-DRURYg1-qgf@DTe0PrY<bLedSOM2=M7VGNrdjt8pr% z6m&7TqI~O#&Q*uLk*FM~X1j`}BiX3AO)Ds>B|9rCYT|P3ox1VpnBr{<Z+`mGn6pZ8 zB2+@t0wKPLv@o(iB#2z=Ub1hhw^Pk6t_!!5T#A-B$#C;R!;%agr@&aMPt**TwExP* zbdWtvh_tD6m~Cw6)X2*d27osvzrwftcrB{(kHMxdF-W3da^+5u4Ayu*5W?!(^vpa3 zj5RODrvnBOHD$Zz32b{#CPdo#vtG9yNk`?F%wvOfTa8vsABc+i`AR*1V5|JTM>1^r zt+a2&u!8TYRLH70y5Lyd{GVHw)YaWV?q6S@_bJ9w;f$<ggswS+#ag-0ZdeFbdi#?c zPjgS`15(Q3d?q<+MHBtYHQBc_V51lI{Qik0<n%b6$0Za(ksIIe5jkTML=kcs?VhVZ z)5Cn?8M!22xYvF{O@o<Z^DI1LpHT_(c2CddTJX=W>=aYdy}Su|@Ec5OQ5R$c@4Rcf z=k;nb)9}^ZMpJkZFZ*<x`k1`K!8b)0L^A7pw~srHSeb<+&i=&bE>+^3`XLy!J?4~` z8WE|jXj(s7JZB8wlJt#ta7N_$M54v>e2!omf0$8t#O!)?B~a>P{GkcVEa?H9rmm7Z z$dP4(7iP5H!(fEDMX?PW-8=>6Gq-J6gG|#GR5zd(b~#tEWNhBHU3M<<ngWwJMR|m9 zwg=>YUw251?n7l%5RiAC<Q*`Kl#c{Fltf8NWMEj?(*rvSAH2I`g5nY)kxG2d!@?Lx zXDHv)-dq~xu&`V2$+2hJmx59yA<?d`7gyX=_hA41D`r|0<wNh_{J}$W$X(~a1poN9 zYU5ugmdsLz3r9Osv&usKTCw*Z%$E1n6RCq+jo9Bj8hhO`=Q>AZlS7f95$@^Im?pFO znZTvhqU9UCNl~Oa-ZC5Nvvn709h3`2_oyJ2{th^ao#Wg_wbc1CoKz)FO`SLTTs7>E znmL{tSoplT53a<$?ozZrQcdM50d1Ji_}ZY4F&w4Zc+PvM<kbIoss<xt4K#;5Fd+J@ zeqR5?sPcTQPEe+pv)5K@3cv3x_3puonE*WT>~J+@G@d#twT99fbKuOk%ev-rHN}&y zb<}XSD;Yl6Yk9i+6b#g^W%GLl_@|y#ok_2SuVk%J80Oic=Lj<Hc;FA0|1#hfZn8m{ z{=PBu)lnDcyac|nz~0+d%*Vu)zbU4pc>avJap3Y<YL;lb{?tpB)+q}<7YlEQwgoyq zS{kAf$G5AInZsN*`F?Kydn4EmXn^*#Q(e4KJc>Mkj4t--S&x;_yzMH!2ZxuM5cCe& z9NGKZx&<;vHI+D9W1Vdjy!*<NJKd*N+MXUQA#7;SDd>y+>GsRd$NA$6;~cD?X>wkq z&{noIJ}u!5HVZ#mfmukh@r#eo*AsB|cJ~p(zcwo(0q;fPVx!LkY6c~(F0ctof7U&z zYyRQ-QL--+O5SBQwP526v<r2mL-Q+vd*vRZ&${dXQ*T$$?A+UYJyaYyzTe@Zci5Aw zY2T{QaTs#6Ci9+rTpgMB&YjaaJC2&RR=?h}qgA-?Xryy_LL$<N=R!8~FHz^N?G(!N z3r8r}%FfDJGoL-)r#FCuugpATI+-?GV}XRE?ELuU<L>U^f~Rkgm1z*DQ_Q#ECO1i) zzurxKap{|R+*wp0nG-iXt&o2|zMpt<xA)<gNJ&EmkV8`RI+Pe?e9-N+hsv%kzBHqF zE8Figd=g9hhm-VbQ@JqaAAt@K&I9aAJkAd}51zvUzvFy}fj`4Qf=?;N9r!w(H@+t9 z%CJvCq<&L^2?OCU7N+kp>??235X#X9^pYoXI+ZcyID>)cA^F@`fl)tw-cqN`qO&@^ z%k)051B1+|K&TZZJef?fLN@)BUwMERJr1YE^^)iHBV<P9kPj@H<59XCh3o*!C9XFW z=@r*XZ)d>8VivX^q_UFs=4K8GRoa0GOcxUjmj#boG1af<UzZeInHPJj=14$12FBG6 z4v)}kD!8Nw8=Uw~pcXW-SyO~8<<pSD20091j0_>G0#qT=|02mGG6d%zrBe8+2w}0_ zQg@Cd^urc>qDghb#VTi$J!d`1i6o{9^_%%OdXIq`w#&-SEUoz%B(TzWSYTWzv23^K z+gmRlY?>$5dL#c5M>aX~;6TT><w&-qMDO!6pM{l_;@UWG4%_rDSRy5v{A-^nFW4EV z_QV6TkouSCf0;7|!glg*^NkTsgnMTTo0kww0R4?XHE#i0u<W@->bF^amYYXxW#;1j zjX{;n?AADdJ&+6zftM%Daollb%|+2+ZAtrDou=ic-E~n-ejVoS7pu_mJFO~irb8Vn z`0=sDS-B@*vQSomZ#Bj|Pa_yv-8l>xEikeV8hFRs3Y(xcs4@agLWw72BYE2?aiLl@ zlOpCWL&KwG5HqU6>{NvRd>O#)WJ629`%;bQwMa@rN-&1J;d+-F5&2c}qNk4i_n@}o ze${{|gA5g{7HgDAap$^L)VA%G@~N>`oNH>w9TQ&(VWMaRY|6k`#qB{pF!2P4IZwYp zeX{xoH@Yb{bQM6TZOUX;`7|ic2*YPXk1zwOz$O5$OW{mt`vw{vuMT5y3YQj!T3`IT zi<_;XF_z5ibk!$vQCxx`9rTtgcv(~dSD7OfR-y7sQxAi873R|zTij6xyV`?tBp>R+ zY(XG6hHD3g(PbYYX2u~Wb!G;DeP*BjL+sYOMMw2S7#s{$7%>ZursKQuM+sjh4>Dhv z6x<x}LJ%|Y)O+4#mHVxENzm-fE3qQJw<hYzc6#~HV=auj|6|<KX{q}WyAftu_ds{6 z8%=mjUC#L;yB=LTvOQAMFdR=`cK2M@DSsBJI=vH}EK9#aoB1p^b%FvI$}T%8+!{Yb zdlB$TO1nc+upX=A%8@Simsz>+xgg13TptMDW`nxqU(is@{=RvMId+VKc&(Gui4_=! zg6kSzDwh(oQwFC3yDyNDcdydYOcflkICoMJ%>Zpaspo60GZS#OXF5h0n`PZjBEXrV zMQtsvyj|l636OM09fJV0<$e(2m}~?|?qi0DTmrn+_0yY$E%;?NJROatb3WT1#0axY z-bv~j?wa3()1dsF@q8h{k97LWJFb<A3x?>;Fv6m?4DL<Wt>}c+ob=%;j9&SF-HnB_ z8NOQW?$xSShj9CcG(Gr(XJGo0f$)9ZMYhQ&1ic~?Kca)Y?I%D<Niqr53?>hXANAlj zijYfwl6JN;doJkF?TVnuLZ8lkfRU-O-sL51Hx&yTl^IAI*E%#J5#%*RP~~IC2GMC~ z&5Sr0pR+6{1g7VP-W>C%e9J_COX2|AVeUmoLg@GbJ5kr7t*VjrKa4W-IP|w!uf3dk zLHdoqPPHx<A{hWeSy+c9sEW^7h>N92n?XeLhKY8tDUJ0m)gabwwoKY1(|l1<Z4rjM zM0}IuxZO4=mL5j<Y6W5)kJVgMYQ>IL7`%yp`Q37p-$dGI`3f1Uw3t4PuaYlh9yN_c zU*1iZ$E~Q9e=XcC4i8=_lGTnH{aUn{z=hX#11C2gZxW#3{q_PjG;d?#qm}WZAQ4mH zl9(fExFn#DzEMVF-*HZ>ctELU5w?%sv6Z#xX%qEfyy}+J?-ZeBy0bp$vJ!)%2LnEz zw?0V6Cb|A}9k7}@<ecz;6$iB>dSMzzMD7R;a06N|od$FMR*IeDXLY;J1E!j>1G?Cq z{$mz!RKVI(YlkzwrXiV<y=zz<wTh&-=PT(Wp~k9W<JTeH`z6uCx8|Quxr`Z3_g?se zJ$ufooD@4petzakrfg5e&~zY9yPZGU?-4$MnjzNPI9t<2SEO)F|J`DNR_@KC{C&-* zqBV|F8P5pqNu#Het3dMxYf&mxPsR{AwYNzs5qL!&SNbIq55Z!_{mWid`O3l*A4*V3 zkoakqwyoth8@<my;y1g&a{H(!EFQ(Mr!`lg?{FHfknriW!4d1lnSn*(=*7_1nYr{( z0}8GhKCnxWUFuMMtL0m!uP$BP8QW|m+Z2}a!v^C?Hr28V>DV&c(b}#<v&y^h^a@Bl z0?thcJ~WE>M41LX;nY8LDga4$1zg+%ZOq@2TH`e|-$^wDmqm*!&G5I`UFD%~3@ghr zNp9Q~Cs@{*E$#^V6Ebv2zp748LAzQ@>15J$FJZ5OVR8Sp_aqxg$i9Z(t_k;+jvjK_ z!hrtqQ_oxOvDiJ|;o-K>uhgPMbMD!V1ZpuyoHS)05cPljGO{67-e=z#2}aue`(^m6 zbdJ9?yEKw8mgBNsQvu&j3mlooyPb$e=H1|nB&XHpkZ*CJlD@@g@fI~2HdsSMA`Tt) z8(LhUAzHntwEM=7x2(^z{L~#q!R_>v)LQFq+Z-~a?95O0m+nC$2JuC!SF=CE8qgIM zIYV8K>HF$o5?Hu!6Y#fopYdOoLnN+ZWnai%dn%e-`FS0+DiY6UJ&Mbug-t-yQq&uK zTM`qf;I~Vy@&f(!^$C$~ig)%p2TF46WrmHSqsv3pcH`@FW0c;N$5p753Z5Yjuy~5& z%}D+{o8K0q&-BIf4*Ghn$FOsU26(4j*W2-)I5ed{Wz;Pj&LxbHhkt;3f|0G(Q9ju7 zIpL|@WM}!>tbmK!Dc^%4VD1Lr<=}CoPbD1%+y8!f;Bt;gzHiTaIJK*C{I;eW#_L?< z`gc|Ld>xbarSG{JlA_sC+sfpqG%2sxLh@3c6OHIYS99xd`!dkk;KCE|0{EByq3)<z z-Y#1<*?2JQQANi$EqO}y*UdC=OUpRo=RFg=#6XKC$l`Pe;Y1nWEb;$azrct#59<i8 zBkq?_*fMe+%)LY~ObKSkc12;8Sk1|9&|YZkmD{mw`z_NsFPQC~^^sNS07p5}r#nKA z>RRIT;Pf#Hfd`&n6I^yu5A1_{TOH+Ad(i8~#&m3u`!!p$H`bSRJ}#(gzOPb%{9+L& zlJe$vxJ!%t*bz$Hd1Gp&wMVDRRny&L(Mar;<r*-oLh+rLt^ZYagQp?PS#i*Kq}!5f zuY#TYcE&|py6rKbf%vPbRP|Rw#SmPM?dk@pjgowh1wedVav;Pl4lEf!g-*>SiiKGc zq|>c4cW#e%$_qSvTK}?S)RkY?rI45ewM1VI^&PcAxO9-RzP_bR;qqCIA>;tU<m}X0 zx?Jz&z36QPqk<M@g$R4ZcL3?ojRkXORby?O`>BZF)0+~3Aysd()9=7FY1dsHnYyxb zmC3EnHgUG*jy&9UfcvmJdOOMb6-xU{p`06dy{!TKrTGLoR=6|ugPGJ1?%j=`Ow?Rg zUuW{Jlq<Pd^+-*6T&T!5u<tFa+c@Htk)1~7ACJ#MQyNZ%{XaaoZg*Y3fV;{`<ZWNS zA9P1ZwwtT8t^Rqo@1Iv@8OPAUxFnN7)INz{TOq46UIU=O^QwEyB<@FQx+|2g>-jR# z#c(SV269JP3|=wnw1TI{OL`Qy=NW>}4(oe=cBx$9X)A8Y!wy*KP<)9<|I*?8L}=OP z=y}uqkic-*g0pzT+~freCW@|AV)n)i`Mky-D2c~dsC%uIpaRT1S?&8R(bbN`e4dTQ zR$4UM(5G~u@SaKsn09sFMn)okD_+k&<!J0ko@2Um{1TA&zT=xuQ)gFOKcB#fcd;uK zEzG8oRN1eE!$9}?ltWH%uD@oB#eulM|03zeff<P46GK6SXjhF#aLG-y^r_eCei^s3 z9-$9Yu{E&9o9x2f-Tm;+kr_OcXe^c`_5c2SPFn(0K7Qot**_4VoEkD_TqU+iS0ky# z60*8ML;G2O#IRWFLKQv*??ZQ$l~wCE-LrF+P(^g-EK$LeJ0o%~R2|R-VmFJdU|H8D zx@3EGg^CS`m}ZkhgP0Adr<2^w8{o-Ue(P#-L~j@0aK9_e5=9!4wDMm)n!qE+ru(&< zlurP)%h{D(fr*9X6MJ1#)>ed*pCLOJwnWk$puW#iNop}9p1KD^SVaUH>Gt;X9>L)i zl@MdWso<!D$Z~lvet*uNOOB7(Z@XJ<{h{;T33o=TmlhF5b*^13?CH_8M1joNbSR$k z$E~sqRA{f%bEWm2od6r(VhtMr!Z$Z8ml$B&EGtsckQ_9AN5@djN*_kGEYc5hgIbj! zUJ@c4&BB$(v${g+u8n<mRBnf!DOYG;)82!U8X+VQDe?_e#L7M9+$7#YIjyQr6ms=S zc)lV-P8fF=gb?m^_1SU$Q23?*!ALs{R#|9x0lzuvo9j3~tH0hUN`cQge#g$LngZ-0 zUEYtmm_K_9B^mP<rqmGB_ogWcII2vbMC7OwEm`$p2?)|o7fKcOIK19hUoNPN|5?PY zC=`B?V#}u#m!iw}N#5rn)M~#yd{){66Z~DVKe!9F2vg5&UCzI(S3~(JD=}sc1o=g; zloQXZPPWL`-v!G7<M{g>@!cE)A3&MM`En=mwMf#DtV*}jrzlv5;IsB4G<UP-@|+9X z&d_>EJvdP8w`EkN%%88BI7%cvVlkZ0!>z-Yd2FizTRX){=(Q>wvUU`ff}t-(V}kDL zU}(RegWx;L7;A>d0<pyH>{>u65q00m_y4E0AWDZaY(kR!S0v+{SjI&TT)ON_xO_ii znA)kxu2C#KZMIdXj=JAfFLfM#Dp6EjSDS@^1(HUf!`!e0G;$1-t7Y%1c*k`9*gD9j z_+(|r`K7#&U7>>gw~H(=E&IRmmHp?~yM*{h8`B<xdcGPP+7R3Hzachy_&TKibtBTx zf`W1L%0arQe?KEzh?$sxp^oR2{88@w(Vv|<mfoaC-py#D?X=0x?I_QzUs&AdUYCmd zjq%=i6x=f(G`iygG=<DCR3FsB)#h3|qiLzqh)k({QcwpCB+`Qd3WaP=QAGkOhjQdF z_W7s+^ovq7y#f(G(=o{sY||vj3XbT%NAEie$b)#ZrpEIp4&7{kT4a?IR21CU*oOud z+2dOi!ULJ3sRl8Bh6FE*Yzl20iu$)^t=^BxWp^cptmZcR60S&P*anKg)BRq57I;LV z8u&t$WY+?mZcOcDntMJyC}%!2c&+kBJ9e;MTU?Bd)|E<UNVoBE;p{AhoE|>EuYq&V z?YKdF2uzA}q((l#Q>2jLIW2i+e)=$HoF|1o4a=i<`BXoCaMt_LhWIys8hxsA!F}9E z<0fm|KoN41y0*!Zp}=ts`Tefo{fSiO58|{OS#>Hx9+Xn``0Zp{aOrXut?}&Q*3@LR z8(b{Fpk(zxsu%gu018fXzA>je=vs2|9M6d)NiF2gczzVH*_?W1of4}u=Br=bw8I1_ zqe$Q)dmlcSl<veUBL%YmN=0=0moZ#5d@RM)zjKTQjgq~iJx#-Z*4D5a#e5HdtM6nt zoJ$&R4hf4%q_pvH5nMBy-G*i(7SD5N^=;u15fTdW`j9d88wpJ(z&sBn7D!DEacIoz z^1@rpqZDg#Dxrg-<44}Mm_X@GVN6qA%wYKUxnS+O#QT2xU`7MoUnVR^+XK_zeZikX zKXHTBKlPKrcyYh@Qtn^&F^VANsKBD3AuuHR)v>-q!DCPU9Cbt-^7y<ZX7h>y9>B#= z6vucGH-@2GQ%mqvSI!)xqSdkcLrLh5Z$<4)BotA@s>D_t5NfmMd2O`+ctwggZCLA+ zF&Yi!)ZU|Yw&iOt9j2fkN>_?3twrtHsEe2heGWwZ)f*X-k(&Bo9;Ato8jFI;RYboK zjgvbDY}u$sC}g_jYa|6P9Y#FxQM%-kQzpd+ZO#*}ND0PDKkr0a&+Fn$5L+82C;bd^ zk-w5kibK>m{x4IR#EHHP<AnGyY93@~1|Ae2R5ZmJ!IyB!o+<Dpud)d?SPdcq>@Y1< z8do|9>!)(`K175vJ9z;38PcNa1429G=!r`7$eus!oGe5e2Tst}PX}sXQmV8M;n4_B z76%@L<VOHbAW`o?#?SWOf!`uWm8e*@Xe30zAht&OF359?xvPtKEblxLi*zFmYzhHA z4!;^yLaiSUtOst`*K%rU_f7b!yp+exgzC42YJ|4Hf)%3I+V5ZpjWn)>MpZ?IIzsJD zv)2?JosC-x>;ghxDr|~?Q<JqFhbn_JOeaGd9(8;@ycLy-&nl;}H=QOYu)D1%GWk2G z9*v;=2p)M%Ew<YFa{~Dao2%B$n+v8`vx9Xcl0qBCvz>56%!?GTzxqnKApL()vo8-2 zCDV71NRoOiDyKW~TFYlytLhzz2{<L}tRUB`ZF|+r;2N>YK5^CopxArLDKRb++;p$z z4IaAyF3ktO!7>&xWx>$)%IJ@N{HBtx<1&HH%Jd^uvUhI8=LSgdMgSd_Kd+;V)=}R~ zONQ0e=Y4XzVhM$8iRyOg&gM_MbJs1ubHvRUm}FBVmRi(0?AootRmnm(s3yow?u}ks zj;4Jg?wc2e?Y{a%T>%QPZH>f00Rc3daVHO>D9YK#rT!It!H(>3`ow7g0yK2z;eXK_ z$kr^A;N%((ZE^?tiJt=0yvyJBh9ZKy?k$#THkaRTDc>ijEFa=jmUeN5W-h?z^!L}f z;n(N-$RtWO5ywC~;%CdmtD}L^HC&>;pawvDkHEbXF++hmL9I~FDO;~gbQJV7(DodP zuu}!uaPNk(OTi#>6Fo|<cJq64_!y&r-qmI1ZDaV_JaQ{Iewbbyn##?$J-am4LjcW0 z?dSqmJxf2=(X?pibM=T}`io*<kKZlTO+*}VZOV~z!5hU?>&c&1wQj4jt;5i)0s@eC zbN*4itYzO@qPrR0Tfi7_@i;=2U*ho5{2%%1kF3MjeKBo$UvYcp6X5Yc79vhFa>o2u z#{6sCrmCu&_2X#Ebw~12{`ZZN9q%vhnVql>sAt?CC|TuCP`9HsoW>@FngLOYyQ5T` zti2cCNzLNqAxzbJk$Uh*N~TJg(j@?6GDm;zrNo9-WN=vh?Kw->Bd!`$xn-A=h)S7? zPRf=7Xs~0iPA^IyLkD<AxD{s2CzLiFe=^Lzz7*}0!t|hH_ghtJ@SsHM9|o{gcB#)M zgne%UwB@(#;O&`J%%6>h$$?ozUIYVY20b_fCJ=&3+HanNU4jrl{D0goS?mLCnk7Ov zZ^@Qj??7)(tAV5YM5m%nM<&kiR*_a$@ZI6X9J<{u!7c%BXBY3iJp28u&zsMe$BvY1 zL2k9;J#jc1=`IScax4c41Y+u)e#`)iP8A$uJt$ARzCRNz&i)v}CcW8cwdDD94h}3f z-%Rud7Up=fb`B0wBcmjE^|e48Z+5gBl9V_VzGy$JM9^WZO!Pel!Q=0tS??D0+P)iU zH5dN0M1py1NEZxs@?=k`4W=MiMV&G@?!Do}BBwxf_3IVqY*S*nB~hYEj)z1(?FL*{ zw+4Gtilm(v*dMK&<<k^(AgsaBHVk~dB#bkqafG4%)b(a<B;_O}H<|!@^}&w#Eah>B ztW~Osqt;QxOTa|n%pd$ro+?<~Ni&?JNNl2rzf2cj&bcQ$>=qubrxH_=WD$;(eVk<i zO7=--g^Vi*D3gMB|CIyMUJ*b^5*1Gp^1@r$3I1xXxS&imy23Nj`9#ne{?7@Neb&&o zkVymDym_aD`rg`1wk{y*o<W>eydJ(`V?lB#<N?34+Y$C*P;fAD9uGBqJ3gt0>sQl4 z!k=Zed~8yv$sEfH?aVnjpOQxwGIrjPA@D37G@@f0mN9gMh&j-D=vnSj>@yp-uQQ(& zgU;im&rpw$6}C>os^{i2SVB({S}Zg#GS3FCMHxKSCdU{Xv{qmkIu%Wn!f1fCAV5n6 zL~K$t6PB9tQfE;?LM5+IU_eh$_B;5G7LIB*I-#-FQK_qI6~4`wQ^M@d-%*{jGyWZx ztaTx2kpc;P!PaOZB{7P~UOTiyA3ZbE6{^KhaA{1frbxaNQS{W6-kN>XpS`M9zlB&B zE<n{VYH=ZMrAC1+gnhJ6{a(B?QX9<GA2#TLv@+FLkHqAM5Vfk(3vQA;<A?g0e<LgZ zVLSWZC@Er^3$=2;x=9mYwYr%Rsdvl~UJ@RU_Ib*+J>)yKYNn)E!l-fY;|j`<tHhnk zFcR30_{QCTqwB)Xq@d1`*_qftBgrAO(VcSU;so!!YCl=ZWnJ<-=}^#N9)#it@5^JR zQJGgf(Fo0lsnE9%=FJ9JI@?ic0QRSlh!>XJ-AXM9=cer;AP)4Ii14j&fg0e6*K9L1 z$Kvu`GG<8B`U4rbi?!B>3wNX!9}@X^YR`B=DyXzsWfh4EKkxx5V=gUxGTaLxCk0(7 z?cf74PZ$;OQ|4bQiQWd)QF-vS`I?jS>=IE=_mzjI43y9rZv1rC)~PZCie^!ltfA?P zwH$)CZnmJ8*>^N2G?-KyMPZwy<hI<?o?=pK@jm+A_(MXGc?^tn_v0FZJX<omcz!W5 z0i9(fIsqV}M(RKp*-yd^NgV*ODGa<JifzqLZTLs-1|A}LN1yGO1<W!AF-?6Q;&WZ* z4^{i<jidl=$gNrQHZ6i+$)BmwQfWm?AFdqS#-6Rh<hkoP)P&}W<h_zqe=Le#G5&)I z%_r9!kzz`T-rgTC6<<L+{rGR2ULm=+m|qTF=x>;x{uF>JS7Q6|lG>JznkR-^DeFYD zm?yV&k)l20akE&~UFAuc4w6{zx(!&<!dY$bHZjNN_L>Q=<^IJV*w1s>y#{W$Mo-w) zjai%XSu_*kSM598JKVU4;Z^sMD&~J_NTPMt^Rd%0Yt9bKe^szRaE`S6WdEdQTj?!& zF$f2dW5xip**FbN57O$wYbKdNu6Rec?Frj|jUmD12N=MNiAogCg6aPjTiKMuuAG&( z2Kg^Hm1o8l#T++fZ~kR%hn#S4lJGMOsis*GC&F&I?ec@|qRR9*Kqh^sfhy4(=Sy`B zy0wAdbJ{WC+khSBVP|jc_VM^sJ!?wrGqn2Ml%WL#&n>ry4)T)U&SU;n@7U**z`rWf zIE~J1rUgMaI8Z5Grux0shxVhA9|S$8s-qKvy|hqjs#7jO-jab>TrK($e3VCb{O3D) zE|rX~ZcDe9#iq|l4qd<+_TFN*B@r_V2}sn~-|HzR0@%x3V<RXx%8+-ypiZL397>lV zEI~-X;N61D4g=pECG{*rM-ks-dIm*2=k>$tw&n7NF|}6V&F|(%QcwHyaCX;~d&-E) zKBBr6X7KY>Rz#o9+S(BYWMTPM>k|KexAslKNdMj~uPMSCk?mNnF_OF@+_ywzELz?v zCD<o2drhlNUIex+CbsN?eG~@dOjlY+@xW97Zbz{1p$3Gw$J`6HD4VnpeTzwRt=nnb zlddDFG>lukaN7)cRV*YAPNJ7^C2MoA&llcB&V1naVP%(uYz}XKoln*2g_jnlue*LA z+>#fDgs$+~a9zxE@RPgYn+J=sHZ>}mJ`1LtrxQK*h;(hbcp!)|!zV+WnE@>tg<L8? z6H}9Jac><?O{?O1G34x(_ZNOlgNp0Jz5kyaOu(z`M1dOzC|3#ZdjrE70#C9G<N~Lj z%^{(k^MVoX6qxuRU5dMX)V+;uEH#N<y#_T|CezDI)>rLh-aAf`e-4VL=_QL3nU_7Q zxX$IDa>zHYkXHGIcel<_w#ayfA`?A;dRIRH4_?ELOkw7f?zah1v4?^=#4h9n%X&b0 z_VhlA2Q-hbtEB>Iw%|&*Xu_r`k3JiQ2{ekMI;L=If=ZDhe5$a-w}|{du&~Bl5dx#1 z!ulh$GSGs!Lk1TKuBCsHw5a9t#ZTo%;-;Ay5QMm(`h|y=MKYv<5<}QVv8lCy1^^x; zdg>@kRu7j41x~KCh&4-!gzOb*G5(Y#uE<RdwP!!t-Nq~n0-uq28TU<(_3%N2)q&tY z&1<b78cRx6_gEmp8RgE2<#O@KWrd!(cTFpvkERmV1o@D+{hx*65euyXr@A?Z3_Mpa zYI;m&=e%m<kT#DO4dcPa0ihE>NJ(QJA582RYr#`3UgD?`=c^)1K|p79WH}i<8Zlz> zq25$th3pwci$HrHD`pBG9Y?^6{vd15DpT29RCZo;M?W6{162UgyXa*=M{1_+OY;$H zDYcgr`@1z6lbVeF?Z$=#pT`;Ak|1mOmy>oKoNz}~6&?2U5(WAZWSJBoYl#^!zho}9 zOS4kUGp|?95U3A(&cn$(tJw;v`!!l*xSl+~+%C3}244gYJ+Nu2YB$XFbMD$-<`q8b z5U+Ac3Ax70SI|bZV3<pL3_K+Z6&lR4Hht5SBl$NPxpiCmnBFC2HR3&3bRJ{NKsbb^ zdkXECE%^)Gz~P8eo`n-&QWq@q&F?QfcJ-di$D2PN&R5PYfsev~qiPkLHa$v-C+-8Y z(FI{0Q;#=J2m+R>IUe1&OQOgAmzMfm1|R3?D&^{^dO}-H6^y5<Oba(n2UT5{*ZeSE zJU_p%<hBi&OD`G}or{-SkU!y!j;WUv{BQMiA1|+BmdaZ^^?$!S9G_>2R*|#IV-1#@ zjUiO{Db?o);>>5V^0`cVq=9aX2jGJ;MU_&A4zf%>pokIFkZbIp?UuzgUM^l{3O|03 z>qX1gt!PaXAz!$3CQyZ6W=W^J2qP(5R`6}V?){Yp5=5u?g;Y6FI8C4^C@O;soCD%m z@%Vq~U~gs$=nZ=UH^YIZ6@JO05XsUQ)&=WH4m4~RTwWA~e+P^CC_FD+yd}w)wW_4y zUrla&VzcbM#B-wTX2&TN;?X3?g^H!_*r9SM5NChWL?H}(*}*t{32tg7HZ)8A`OOm~ z+{j=i?kAYqF$!0kP`uL0?6j^dr*`gMChbV^0MgtOX=&yTP&$XdM&xOOul(jzf1?(N z&|p+QPNgWyLJefka)j2VQyh$903!==MuOtN@%qKpAv5GbaLfen*7(SG+OWZ_=>H4& zdb@LbJ3H7@FS4D$BUo-Ii4C<TTJ)Z@CKd1rGkbUXDrRi!_2=TkXkG-ZhQQ~}rKMF> z{kOo>2IBY&kjB!AU)lpvw-^l^yb2PkE4OXcqUiGpUFGB2@;ughO#F$d?Gt%@JJ^|q zl_ptbfdu=hC6Ak^L7z`OixvVm1S#@G`!>vNLlV^|A}bK0TfJWj4lz+?k0n%853^H* zW?sL2CJ{OyhV@iiqWA<GN$dD?V<!4CU2)iNXnLIx_)mLk+`n|F-}Dx;e1XbZ7-VZN zE@CTpJt}Ro+~|!hkQ1^ptkKPA^#LC&Or}-0#v1dU<H8UjD?u&ASxHa@E5dZwUH&MS zT<*MDq>mXa+zYNOoYFqw7gIv6Krkt!jE$R>{;@k{y(ok@o<ik@65_usLt`CPM>vS9 z-^>aF1aWz<mT;i<T&2r2v@>2O1g?8makzAQNk2liQHZ<ejD#GJzn2PkbjdSCG1kk7 zRLzv`iH+#Mn$1^m$UdECQ+E7v_i?71Qpo;xy8Ap0@w&~fZuRzcdHwhs;0cz7mfa%& zd?$D3<a~{XEgzRSDsJl=v_?JMJ!YsHT5U=L=Q`EoE|q$CX+?yvHv~@0uH@KC=14Dl zf{{J%IOjhOMvOwXg5c#-o=nP_Qj*?ZsIe3N&}iL{XG-X7wXqr<3KyE4&OzhKizI|5 zDdVfRHQCPf{qvL5EYKq^j$k}N2NMZ#90U7pQ>TX-+y^BxpHR6T9jd+&{<c^!bw>_D zsl78vPY7p7$QQa*k0|emSS_xhhD}k+J46bmUe1{gZ+-l$Ej`guo&~51k*3#0-wDSV zNfd$%4V7m}BUlWVcXX3%Lg@aQ<Tknu`y=p>lbr1eh{*kVe5~>R%=qzr3h^Z96!_H# zdK~tp>UoouyPHYF0a+|lqAirmtloxJ4W|G8)3@3>rt3FtppZG_Kp3@{hR@(w<7E%- zkMGRYtp4)ngbQ+|FGO7{P`(ig0v0%UB)TDL%u##>ps0dJZ8L#od7dOFm_F*QsV&`R zhjp_nFVVw}^3?VfY=03g_m4Tb`d^NFs5{WSzjz#CpTGK1uQg5zPqTBsjjqy;lfheP zEv6E^6(|9_7W;aOe|ZH?*LL95*5&Xi$c#I>swHP?A~-=Niz@S(Q26X-1=%oie^o0N zG5IA~>ez01w>F=c=(`ggN7L{c<2*I2KD+;ePwFgmQof3aZk%Pu<yh~8T7R|AYF7pX z^2xj|J%{1o(NSsMsY{1T`lH1%?q>_$9Q5B|>W_fOay*jl=zBD~`pRC|+jtw3Y2o&x zW{z5~*#>^-y6=&uuDNO|d+2fCtMRs7MK*tlLY_WL0du%>?FId#eat@o<jA4Q)l}J( zaHH4YR=U{8elwF=Id5t=kDvG5?&RUa19D2q@wLKM=j;B%<rqH;p`o1O^z?Sl{7Uxo zJSwWrn2&I4nBt1TN_otkcDd4QVx=N3;7{*dj416!+H_BLGbMswMOI~W;m^RGMvRH+ za@A+vzK9wOlRFd>6Fvq34fsLH8<v+o-<OHMGb$c5p4B~v)1c8Z+!K&4ODH4JBDfQk zstV#D#cSYFmDH<}93y6nejH_NZ^?~~qj7UDg2g?8ZEtY=hBKgTPTH+%K6L5>+!h9g z#_ujGGbd}oa`im*l!{~28L63cp*vV~2gX-pbhaV&++>p}`?L~`E>OyPbn43^7F5bR z{HGLZyWT6#6H37@`!*YDysAMfXhD$axaZY1k)5R!KV^?0X?>$Q8~J$>r5VQNmm!%> z;IaFv8XZU$H3o_cO6?`xHJ2>_$o!t3(ww5X2&i~CLY6O>)ZEoD-e&?GR6cTsA0+`= zl@7B3IiMw*`4=Q=53PhVYo*an<W=CYiE$$kqX%4aV*cL|FWm!uwXDS6JUGCog0TV9 zfu8ixzSXco1!v9~XU-jHq;4B%BHHW5#^4%DM@~5R^nWnH%tWIn65#|mKYi^z5>H?v z$W0xC&F`aX%`zyRG-4;WEh3o&Rno8+S)3hG+Hz-Tn#K0*gTedt-aI?wnLkG&;UCX| zs-vy835a=yyDN-r)Y_QAZtCHG+lAnyI!eJ}Yo&van`~^qz`H&$ThvBMc-%g=>_gCp zyk4o|2a=**itNiG_cZ~%1Zd&Kbw4dxXv!PB@IbcAQjEu>NL0L${jB~(zDz|-VR`T0 z(a2_*#w1=dC{ZfXq*ExsZ87}Ps#T`ID2zfPcZiefCn9I9l7Nbf6h=hQcvQ&)afDk1 z1pjT2FAVG2ZO|xY&Hv@MHERpx9uCe?t=awBK_)&Vxb+r_AqD~NXCw2Xs6@MK)<b&U z>Y2_Kp=1iDSX5M+{Vt%+-WaUUEoxOuSUx<>_&B(zF$o(IstReBg47L94hQvWFLL@E zpEWes+TSn!*jU(w-mClnRiEM_C8U{zHbbalPj(r3O!bOF<pfz+0EdF5<1i<FTcMA7 zeQAktu~vm~t9%Cx+)Hh)+lRp3ilHk_`K8jEuoLY%&SY$fKLwn27IvW9oaGyj;%=YN zIcHi8`%tE4O}Zu#QmF!yY?=u=duA3ZiRW-+eGR2PG=(dwCUF1jA?~l?hUSuI^Cf?Z zD>>fovwCzw&t)Alm3|X5#i7zDNxF?Tuy_@DwtjiuP}&B5^4+xV;6RdVTYr}d1iy~< z3v&_cF!7@$@>bj}Hn}Xq-n&LjdGwI#vp>nqduLkgXZpmBy>EI_%6uC6-xGx~H7FAQ zoO(2eE-rI4Y;}oqOTYYc=-?@Wi#1hIV+kRKL|A5z=s~n`zX>)n@`%kxHyfWUTMbh% zk8mPYZKMqF+i`G2X(SnDNc#qcGRbowVT@jM@SJp#*|JD}T<$j9;&@om1l}#8YlXwm zL$};88?;``f<o6Ae_S;9WtF)>CNX7Y_WI?Po9<ctv1hFF?6Sz~7Rc$uZJ$a<FHKI_ zsQfYi+xOxRa{BKs-(0->=E4pt_h`f|?}YxnCpC3|7d0C3)Xen06Hk?Ka`5%^{`r`B z<zBL1T~qzRX)EvPyxxPWY;Dc)fDDGawM|OsbZ5Pw#ls9F`cfsXVvhOZ1^)8>yMWSf z@~=?;)V#5^-(S?W^_LQ2H;#<!KG<3A5mz?CXoqswqVAs==sY_a<^;q4hpTf6uY?J^ zY<ObZR>!v4>DabyCnvUT+vwP~ZQFKwzJKPKi<!H+uex|^*Iuh2N>8y~|Kgw~AFEUW z^BjC#Vj<gg{jY<Oj$^%S>Rk;scSs#a{&*34r*}3W8b##RmpN-47x4N%IT-i^C&db) zfl6q8&k8;f^|QXo<rJ-Pb?o{ncRJ0ryX*>^Ed|I7>M+$rO=~?gw=dI^c^5l0WvTJ* zQ2p*h3@Cn$;|8ABccotc&2uB5dxNY5lE7ED(7b3O5`=5nvN&H`oJRDwtEaed+7oYf z1m~j{iTd?$oJ?L0%B{FweMgb*9YUOtXT5!_{-~3cl@jIQ>2yGN69|e|%7opp+un;^ z8fq_hPPHXfRR1@lK8YW>J(5k`$j0C-Km^CV)8hL|I)C0qHkg`eEMQ4jLTCR0@~}40 zl-nWeUO{6whEKbyb(CLlUk={mWiT++QK3lRUV8zS8*m!*bJ8)BKJu{7yQ+Sa|E#Gs zGy-wL;G#-@{j9e)=~6+%GYIrtPobn^D#OF3RI08)>Kwu|)q_OzD9b)sw0W;=!}lUs zm#t4zt$DU!v_XlJs9zTP>QV0kRnn*VYXjHaT!L%fm*v*%b1)h8<iuU2Ds>7dv=qpp zRWe}ol~aHbuN+MH2}4zFYgrNf>`Oq6(E6AvDF9ZuL`_#cFGU@mV%Hh|VG57ea!3<{ zU`*wF!EmOrAEueBHBh@{hChls?*dI5t34;dnGfR&O~Xr>{g$gOO^ge80>Qk-p<%^o zqYD=&7*-KH<6RyTpY2UFvnzw|*5@U<7eU>3^!Rrw>Et=lH<bE^&tp&N9|ei`zj<99 zn-4jyjn6if)OcTq3{%Ep9bjNMYgrw~3pDI7G}Ha8d$nJ*It5{He^kfWiEcYXDt{tf zD|Ad0{5E5|-xTQNU?L2V5trH-J~v^+nGDx>poywvuHc`)%1G{VaCu4Vma-M`Pv&3k zqkgY-oVwBNWEjjlqVZAT?a0=ed2CeqvtcHYhrZ@!413piqxaCCYy=xK-%=$0Ut~Ug z(L<fzG))SEwyOb5s+woGB=;&++xBt2FTGE_K2{I$=?(cJHj8^e%f(S!gE;NeHEqmc zE!kXt(NI16l>j31S(6W$`Rkt9NE?E%3r+E5$L6ltT>r`#rgom2J?5Sdk>@o~6Uw<5 zA(!oSaPQ)gS-SQSm&=5n2h1~LnQuRe>2H?`*-nYJDy3UxMmvWA10Tq&apZzG${}t% z;V`NW2Bva-lqq(h<BtTMvGi&?k*W6uN*<$_P~;>-u0kVACRnP#e@n{&KkNU%{$Cp6 zDtC_Y{67_l(tk=243M;dBwVbdFpWpV|1n(HK1V%w!+?MY<0pk{5(AetckQ>h5dHFc zMNS1w;HS9g9fnPz#t7XSbani@#p~%Ekc64$)*hm9<BpP!ii7n2y^_(4r=Cc*ym|-+ z3~`7jpugW2ZQypBU%K+P^@l%aj{DZd80*knw<T$<uhVrs_VTn>0H`|O)HXGvc&xTv z3rjH+BM!Xh+X`-jv4D<YdX=_cA4xn@U!Qs}%O9HM5?Pg}&uF`W8m2?NP?ke_)SOmb z%ClWb(?pA9Dj_Oz*u$ODJ{97587z6sZ|%w}_qAlb0@y{<q+OIXuVLOrf<Z`vNR3h6 z8jKI}mh{-Jzl-_IAtlDuns;9L0CC*ix5R;I!;=3FBWoP2lYk$*)?$+@3$?tK+gh#G zms2|S0(DG<6#wN899r(QESu0=YQyAfh`Ci-`Fnx;6R0z7hR89V52`#3`b-KMjcRzI zaWcOEQ4{s|xq=a{z%yU$mAwr+0qVb6Y}Qp!%2BT1Eh}<agA-L?Nqg>Q2@>+#wI9_b z9bJC?k&accqCoY09Q4mPD7cYtQv9&;hE4L-FGZ{}rQtw+bduyZ1sezT<-NRTxh45= zUZ-l0Z1Gm!>8v5!zr(kz8qi^9yS<;Akq0wBga2LW=rkB)68y&kU-{2q;tZy65z0n! zATOew^@@=6714VGXr`_qCPfFe)+4MWlwP;|)y8Nq)q%&MjTE0yt;Jw^xPOh1>kS8Q z1h-hxLAsmsga7ef^Zy$6dkL%(n~v3XJx+gHaBobl<z?E8(rQs+TP`P#gVlJ*s;$7! zu<KU-c}(Fy-6^LWxmSfGR|!yPVH?3u^#gPKV5O6ti0%%jB}){l=SClD=Yk$W(TE&| zbt(}I5Cl?BC*2uiXV?fcHu3ViAOK?Au7^@rq55V4FG1DXObB(;QNg#_o^R1wCmE19 zB3@$SMlEB-MP>K#qNR)sRoONr4+i+8r2N4mjdz%+@Ildbd17W~<LwBgtY{aSxHZtd zN(Bvb<Bnq#{0HQ<&j7F4lYPsG#Nz`KK{Di(JRszS)Pg@w&33Y%DIMtt0uJvon}HMr zg(b7`iDzx<A?iex@_l*|3HV-hK?vb>Ks?@V;#V?p=wU#fOZnH(=7IIpu6_0VKqAM^ zYarf}PY{hW(1Eu+to}3-L-JR0e7eEaFU&Cvi-1u+&)R%&_{xe%a*z3jsfzIsthq@S zM&Od-8D>04{7yV&>+s&ju`K8`6pLF5&dMtxnw@b{HL}Zf7bIn0mZxUyLb8hWA7YpT zLqo_MC%w7Wl7DJ%6opMju9nRGc)mWO9&=zZ^NbpI?5)VV-v$IJR@7O<AB-qgV4Egj zHFyDkQq=zZ^5DBob$|KK*Kk8iI0iyuJ_lqwFbB&<BV|mJX54>VxU9p*fpIZKzM_X< zrk?k$qYoE_{es;LaKr~`sD&ETa}$}T^h@}gjy>B5dTu`6Kafvap@__$XwTyh2i{h! z)lLB)Vhe3~I?c55Uwa&_PkIi_cgpfA=#k~GS2PrM5Suxi9#|I02Kd?4GvY`PXCY8u zGiSPPH4<QwwD(6J(fH_|<b0NU&R4!2MdOQ<eh6%v%Qmj9<mu|ZZN|dZ#gJ~9062mg zlaj`m!NTWwk?PA51RVkYWA+}U7n&Sr0wBwt`1%J)y8wU-c=#rB13S~x@6FMU)J#De zhJ|&W-R46W9#kwRoV-N@O5O5W?EyW)rCjy?g04(F?bS~}D;NAitv7($$26oFd1J4I zSj6N|4NtQO0qsL~q_|qMFk>e)*w_$)nb6PwZC~%fzh7>d8C8?wG%FmTl?*ZnN>sHT zZ0)a3AOY<g;fEYf+*MR32|J+uH#(VFa3K05Xn!0QqEuFbYz`)W&F4(VZwsiZa7TO9 zxH`nIwS>|4g|NxJy1D%9N@mNiaW=B__RF?q^#)hL497-W?vv5_i1iPX8%#fzgi(GP zUPJI62wmjK$989yyL}yyYr_aGGZX>Qc@^0qcB9Ua93r`=$MfG$SOwoBY*zT^Z>{6u zBoe(B$m~2sM#NoJT0@C`kqjVKlC$`GZ%y%`G-e7joZrCVkR0e@hPDALKg~A_W&7!f zb_{zCRhJ-(9m(i6Wc|1_%rks2!f6JR9o4`EL1c4Kzq@dleUAk=I&@?#!4Ya^$h|Ym z5w@Z{;x)qO-no)iSDCBimZW9sL!4OHvpN|ZU$P7e9XIK9{=h1Si#D(vsRET9#=ziF zuXKqRrSp*>&z`Q7bAVDk%<&$&4AkQLD|j@!+@BTy#ZTyWC1>puodh_0B`-0dLF^jR z91g^EPz0{I2bm)OE!bw>2+O_rQqV4LKXWu8sMwyY6@quDA%~1-1iwibf!EaC9b4ci z#mFl=ZQ{9z!7yQ>)(7zZrOv)8>%#UveHK@e9s+m$Z|MVX|IxJiW%KOGb4NP5PiOpB zffr^&I|P0-O@$95*~M|Y_tZaDRw6SEf90{<>44aH=}3E!y@~M3UTds1YQYM(a!NYl zUmtTWrBij=AY;DaXN2ZHHY<jOCgB~Mx&XkaxEnkaQ8IxHlLZhTUzA|@!jQ@~SA!hF zoP4ptbUT{hnheJy96RaURm`bjXD<5)wy%-OQR+=PIFd%2@2_5=RxVSwmn9dH?E?p* z3)u&1E2$bNZ!91r-La#qh{XYryy;MdS!|~%=ON)uq-?_1!ym15e9rYTF>SHaAG^)n zqrRtBwRte3>jEs6hBb|ZtaD%QhMzXZ%QPcTfU-FFncCf%%T&v<0nJu)v45D!5S?*| zv(r2s-&F=atxlLT6wD)bK=H_D_40-S2}j9O=>cdDyN<-lenSt!zispz>c$uKr&>wX z)xDreHhss)22LZAXI4-?CRB27{>(B;-`L&(IO6RxbAjr~;7IWxarG~TQ}Y%>8D>6d zeTrmwCi8@Wh%_Lep|S{9eR^$90TspF=Qj3LYqMjg5$yVkm5>Ht{K3;o%iAD_kM4iV z(sF{IhJTiR@8Xu}3S4yU9}M$QD?v>1Y%xG^1#_9u#ISbNzm<jl<Wfg`gWH62I*jY& zDFw@yqXT~e)WSa9GObV~GcCQVDIRRQCTq^Mf{k3zfBTz)C=b1aaN<FZtK3O(NAQzf z0qDGt8ni}!kGS8k<;I)}fKF!Y0Wezwc<*Dbih1)h?$4U6|CF(g1e1$KR}U`IQuNW# z5z^x2$1r`v&O`7-N#9YA+9mMdYJ{zUpL=4q6ap`?ZI9GQ2P0mTI=3CKBGhh7M`fX@ zql+r?gr<2}GCc@qgd0otkx=7_CeFJ`E5DpOm>C|lcl7evo94K}k@9}(2G`Iql~of4 z#MK^-ul_&lnHZ4^#yczsNa%lUP4fTihO@MDF?F&tv|)5MwMipN#zg~`XlmPUj$!z% z)}{ZV7HMYL$A<fhJaKJ@R)%Mj32}J^&=@4PaI%n<anzWZYs~!UedTo~;YQCfY4xff z-sd)-`7Uit;Y}?yE$d7zqe1JGXvs8nrFSh2sa`hPpNW)lrgz&COw-M=rlCDXYRTA8 zJJ=BPQ)ztJrS)_syTAkfqsH4dsk!+58-p`r=R?v}Au17jFhx_(RN8SEz;KliG8YDL z`JTMJ9L(gOt^Czl`!*oLRv59!tB2J%O!00j5k2JIr5Zm&_G3f@1P|TMGoA4JeM?9- z7wLm{I#*QPHu`N&+j)XAr@OM_787>#YZmthy8PsI1H5PSwV@AKxYAYA^b6Cq|F&O9 z)lsAB`L0gf_j8L#Al7{N)be*6{{yf0#{&Kg$!*)rm;&K`=;hx%(-l>o<L4X;)9%CO zT=5jhI+bd!20%=y?BwLm2iDIUft=^$Wp)+!FZNaM^_{?IT)gWX3x=z4e5Eo-yP0Fe zo-<~FF0aRf>#!)`Xg`UBw*|SJH)YX(@vlkGi8T1W+zDmsf>KMx4s1%`!Ut_Wq8IJu zv(fY0b4fYZfcT>>QOEEo9h7>-4m(Y^UX#z{<7P4`UCL)>tM$r5DcI7Xi@V5i0pIB> zW<JjsZI$8ofe%x0%CO#`cbkhj=(V;Utu%9p#qWIakNq#;mt^XbCPk?rQ};|EDR21^ zqklm)Xx$&5kMM$;)e}1DMV?I(Lr^QwStA-6B@$Cd)b*}tyLZFEWIOe%Bkhk~W}iK_ zZUGCE<lvq`e{LNHt&FtbFz2*!qvq+EnVtx*WYw<Y*O0b=eteP11TO2KQ*PmUGQ7V1 zei`!Q;(y>kqVBGrsAH>k9f4MlvF@woVQm28T0c^NB#hjjRGcjj!#&{G1z4)N*ChYZ z`uY0#_<2)OQ`75g&w93R6SwaRZInw^L?rc>70!-*0#CXEJWOsyEeYIXpe|8*HT94# zsj^IJbQd_7#z(`cGu>+fo1_2Xa9mEW_@sWCb;}p<@%;Sc$)%n2FhfVqQCEQKwVfDE zD+SwhfH6dF5&Atpn9Tf+*{MJ{&)(#E?_AWN;W-F_R;-p0w6(Ou0kX-@D;=B-QM4Zv zy0GOeAh-VYIzUB#yqc-<<W<CSp|t-BRGizWM(}%zzjkk!D1oaGq#q)&3{n>b6iq{6 zD?1681-F-9<uCmf4ByP%VZzuKVdFtCecxtA$v|w?c;({xuzDOBsf*+yV^2ySTnr$l zTo&io8pG<H^$hxYcsb~@r6~TUsN%Wl`8S4ivU%}VNhKvAOW{;d6?DI!n#faa1e5q# zi-YJi^OB9x=vTQeP{l68EgH2*FXNUuB_#pmxG*1L7=}T0R*N7N7_cdaVJ_Qcr$=AE z5WQ_>3RkpF_c}WSgW~2bouItI;zzB%{O+>WP8y&Q#D~f#oA}{%1Wu41(UQZE@0z(= zC{i!T6B8O@3G~GLIb^QV%Ebs<8)f(Pw@DnNP6gY~fi|oLv5Zhysc=;j6<+EMk_rOG z&a^7`CQYHe1QtPGi%sWHS|MtpjLmWRm|9Qahu-z^@^Mb>7fmvbvgje~$f*5}87%=V zXdqc1KTB*FdmP(kQ6}vb(YaMJ<^ZSHLl7wSBEl0*clmI&!{XUOd#U`(Y({sCKSR!K zu?a{lUu6K)Q9SLiq--lP|BLQ@wzL4seKh3Mv>sOKy1_*i_WN5Z!u6x_5Ez>kD=r=p zmcGh%7LHMw8o*b$4$N6SQ>5;Ub)W9j{59OnB{d0ofZevdU}*NLR$fX4Eva0K!B3cM z373C@7*-K;(U8)zl~>8f8@pqX6e630dPa;{RhQ<HI%It>6Eef@o~f$clb{9`^OTaK zeN-6nDVOS5A)eTj1)L;aYgZuL1;cOSoGeq6({oN?HM_n!p?hcbX&x3FYREbS3avrt z2?`0olV%l6EY}upvQJ<#E}^kW(|9P2f3_5v$Br`w9jKzxuP&|Fq#AE$e0VKI&sJqi zTjTJk;oNEm9EpBC@n3|0DarzY0}MXHkA@Z(lMz-P4AKd_)*A^1@}{+BM7$+bD&&8S zYePXnfs)2~=d9ZK%Z>6SBZ&9p*fweEBS*TI>*0h>rBy$*Z}K7UP+m48e>7CX($*R~ z-|RFX)-WGVPfEpqdx!lr&8On>XeF)NSkJz`7%u57U|(ncN!pq+=)nO}!E*i!+3GJ_ z^il7Voj5M5OW!M82IpmVUQsY%K6)4}_p@|~mhkhJp>&FzKR#wZ>h9<g3po6^c)+b$ zO7Yz!Lg`EK#?^3q9)u?1DEh0KE`gqt!64XN6?yoAA>HsQP=EXTx-DPCY>!PzfybZM z)ID9=;&+qbB#uwLhK?_g<pw;J2!GV1U@|N9aibgjI*iQn?D1dymtJEnjm%i9?l&|r z7OpclBKz3DRB>=K;ZshI=Q6fno~rA8-!-pd$?yRzskFh-*_%~{dsRtoFkdR63^{iz zSs4Tsaz#165Da!ja!1*<0kV|I6sbTPIz)&+{ptFBs~}{gUQPvQZBN0bXPUYRfui7! z2h~bo<TfMutwg{gph0tySTO1{4!3T$zZc%w?0laQqwqI!8c*2x)>SfBJ`&o@R<s|X zekmsXppBfriJaNqc-Dq@2k|X%{()cu<$Mhb>=MaN{V$Gf#LC}_Vn?IJIyz(4qVi!I zq@UpKlZ`W;t%L;N0!Ay&vPE|wP#AW>xmR5!6vFIz(?w&IV!xE3W!cxkVDxEEId*^O zn@I1Co2JIpD%HCsmOh(I>t$DwGUVh)fZybqo-r{E2NeV3%`Z`D8-ZRZ`}FNNn9QJ| zOQAv8rfu?%*{Vm7%;;+!)$LuHMSE8v{+tWb(a8FAXG1d3E-fRNl4rrjBAna@zk(f| zA%oBRdqF>GLD3aXajj*-33aiT%?Aeus5iV{fiUDG4c&n3b}XqJn1}3w7t-cewOuz) zUH4I6U!$fqj+da_{WbpI&eQHMj9h~V3W{jt!;077n#JjlTw{w@M$HlS**`Hd)~yt! zcWP}?!~X&Tk>f}Tk`>S<`Aae^rmiMv-2vtj{j4r=+;O31r?fEI*K?t)3~#RPX!N^M zVc6tRN^Y(V@U1k)Rj)giCM5;8Z!Zoz+Vo~@fQrC8PP6iA%u&97CO9u*HtW0ff|bW! zPW|{V)(^2{CQ$?)vBnvw?|;1l@7D{h2CYsOke+~l5LLT>qkMOEyWhm_7?&eYgH8d+ zDJoi<*R^97%QaP=OgWGGYka`hm0bee472JLXK;h+du5aP$raJ3K-hX_0Ax*q<Y+^C z&(sA0$DXCJ<VYfHU@{`@)PGMhy@pL@1~JhHI!wfQ1ZULe?JEzHawK|%L_1}pJT_r6 z%ld)Hb;ELNEP5?py9AZ10b6XXwm8zU#oW~awlCScBU8naEY%{%{;KUvzFGB^<UAIQ z5E2}Jg*zsHJ&8v9RWyCVZNMA+{dqt5Dtmf=odt5q>vw%bl-emImTYTu$dV=`-9Xe9 zKd7;7*ccBG+Bhuj?!?59j<x$q*;;wg9(n*Ze6P$&)9AM~-rDoBVsDsc8R8Y(WeKEu zRHi<@(t|_jE>>@QA|gMN6dwiTa>ws)iJP}1`|Y*=1tbmU%t(@h{gW5r|CrztB3ex= z{s$uvDsP>jx^MCr(&E1D9w)#TI&MM``YzU5bmPsMI6}VUyRu>dTU7Epo8$m@W!aW0 z)kkaLsC$daN|;D3>L2L;8>a4Gc9C?$fq<;j{%^I6T{156e@b9$`>m1t&+ka~7-GI) zBU>`J4R8h_>&busazt{fZv*I|$gNJ}Au>w_8coN?U0&ADqr~K+mLDZBF9&yXx1Bg$ z3B{+DNhFaMg4?Xv>%CJa>Z-!(*Q*m^F^ctC>)0z<dT%{d^MgSJw*%@6y;4Q=8a1{o z)6N1So&?isAYY5@>VI&x?CfV_#j#b%Xr);%RM$y!g5~6Akmq&qyF6iNPc`~USH-Uc zwA^I<6To+tp37Gws@K2&@%m~li7$fAd_^7#&=@5ENKajr)41y<1jW=Ii=;+GR|}Ks zCw1SRQ2&VfBIFO-Td^5_5aks&0(h+TO1Pm&@cMwjLLL2su`Q*0W-NMUd>b)?x=Cs3 z3t-Lh+(DNDt8-gy;VI;8dw=lUgWAi16fw+ZlmHpKxi}1mf*#_x1~j6ovOjaNs-`}U zjo`pPcRGqLvfNWlPI?Qa^E7yScnQ(W0kyxgd48`B_!-m_gfS+F85y7OE-7n|Uv7vQ znHi$M<p`uVaPT7mxuOEjY0#wa(hX~P3r(zgzMy&2cw?HHGH50=hpECVRJXl{`0EMJ zT8wh^@3|LQ<?(1PHjCIVX_FljJs3UnuFXka2ZS`+)guuvIStheJUsv;oNa;`_#5Jm zen7bsv{F-`nt|vkKB4iS6;7P!X;7V0H;OM{r&d9__-G5IbjcpB;DScBV%J#XvsFsO zH=)B-W;I%U3!3E)zIMFDmiWE5-@|B9Qm^UxFy|%O>&sad8g@9U&i6@J-fnJA&)4TW z4e=)w?3>J3!38K*cnj(+t;67}@?)FS+QQVZRr9Z*SW{2sSA6*C1?))#@G`xoq}z2M zg@tXC(VfteAB7O~Xd@=}!6$e<LazhOhUPMxI;vYID)LZ9e@ZR1$KpdQ5e~86De(s& zc>8xk<DyHR2%@E2Zi)P~*;6a{<hfNL>5D1!%LvK8A*XH*n=w&VfAfUrVcfStvk}w8 z<>V&T<~Tz*?DPTVX7En*qoFgUK2UYwIRiVoGY#Y#z=U~=Xi3L7Hn>C@`8YQ8Plbp- zY2V=!dtI@{d%8orO(+AEIldut|CpiYlpTci#f921&dcOE<9|>U)l}%;nLk7OJAacV z5Lm43``7)TMjc6JSI0W-khLCq_E@PQWahvIc?dnfGbZNVMlT@t6lRlgh$1tA-%;ON zOGd1XH43L%RjiR=!Est?$YKy;Etyk2!+#NIP}ri@R6a;5QB}4b$k7C$fuSjh_ttu9 z)bbi?SfEAL1CaNr7$d8&#zxhch4E+XBVdatf&q$5)Z@_@JfHWnC722s76XJP#JP?a z)6_L&cqfqbcn5{O)Ao$BI5r7DeRk2jMs);5@lLO((2hb6r2AG-a}o~jRGCrX`H&Ha zru0Z9I4rKL<|P9G2NG5|7qc{l65(!uBl-@LTNX;A35*z_xJ`!g?Im(=*qB^^f0rS8 z)G_42s;e3KLA>a|Z!o1*x!8hRerh{nJ>1SC)GJw^E1Gv~yN${#Lm6$LE_W6jvJ*dy z^2B$_#ZKYpR&ty0J`Ja??6@*vv{EU>*a1I-jqV>|n=Vp~PtM>~rs%kBGy>4ez|zC* z8jjb2C5Ui*B^HtRKb}3&H>KUOo<D2b=uj(J!u81|d`8NXM_!7|#81cxL>MZ`gLkJY z1`PwLKX%k=zx3vu9Ko%DT2|iWHN%7T;h0ljaynWym=TKf5XnyNS;EyZX)2ZOqF1$3 z4)g0A>MDGoj!A%Mse<1MHs~~gb_Ns~64jf7=m=@4o!=|gRFBB#2n9M#lAHbzvoGA( z?>6cfaa%4}^W-7-1(-zIOzcb)bon@jCT>@Y9(9u2c`#96Cf;|za`{NU^f*-Bnznub ze0tvP*g?MYV@Lu-G>^;D(u)Po9q$Or;@}LNg8T^Kl}7weAzswwr1T2e2}6UG(FwVA zhk>XU;0VDYkvzfSKkYUsEK4t|)2Q=W%;>y)NwNmdSMh!J*(0d!nXmh$L$Eift(PmG zd}u6$iwZKnn1=@Nm=O7zlwYvGWKdw*_#bOW9IsHf9xD3hjv^7iY@WC^Y)HC-^lx_4 zoTvo$ji#gs8d(m+WP1&xdZa<plvfd@=CA8k`E+q4IdojqU#L=%eA@F3@)&*CA2;%r z@o$?2Wr6U%G6<D>?FD>=5^U10)lG^u_;kGrO`nzR5WBj-%$Dr{35!Nk4R~c<7}=m& z71_~JkuIw>w+%eaCg%zzdU+;TG69u}4DhsI8%#5qo#*4&Q|ua2QJr9embW4FVbTFS z*_v&F-$QbKWKn+JejBbrNWoCEiz-pMJ9o8!QQWM+1H$iX+TUgAZ($a@eZ6{8cR1K_ znJ#RE7=~;>#XmH*g>5p=#q?rPHOMt8z9m%}IyI<80Rq4e^_+)^AE7K-T9oHz<Mj|v z<d!BGo9PLWy$!A-lKW^D9Bn<+bgco(l$K{Jd-Er~|LE{-se(5MiXeSLu6E1k5hUJu z9jK4I22dsM+w(551~jJ<_cH4;{S(!n0*-F?(LQn@i_Fo#cj1G{Fo?#g`F>3w)s<nj zPm15G*-hLe-ox~;lea+*Ht`}vL$S{X0qZ^n2!56&B5aoJPi_J55{s;*Oc|q8UQKm) zJjFcRif?l00!wz+wLE=JfQgj}*50VZ3W%vDL>Pjqhu+epIbTqgvND+y9Px~H<p<9~ z$IWY?&k|dl+O=Ed_4~8(=K4(55+7Hla|64C?DwF{z6R5w-GyERHRX@|F>}grA^uXz zs0!lin7<uz<4dZ-LdX0Vyg$Ea!Ln$~s$=2U)*I@-!@C9qmR*zgv53~gzucNWbvmoG zf(~9vDyrslB3!>O_1<fD+#yt<*YTdQ^LMj=jj`XjeFcQz@=k3<;Blv@fCx|d^6TMw zQj%iYj#k;Y>E>PrM=?2J7n@?V%J;svMl}3SA=lKR#;*~~M(yx$)Q*K~bx51pJbqw9 zj*9n5`zC8n{K;tw-A!N#*>upGMzXg7m)<Uxr^ru{vp0k7v=$b#VFsUBR3!%Ok)sPx z8FICvT{*X+oR8&3PO)X+MYTc4g!khH^>eD;7^TumCQ9TneXm4n=CaP4FaPM-7AWLY zzV6>)!l_bm>w3cGSz$8oo~|)-ZXw}%$)!NI^z|`f3+VsSTsPv!+RALRR=w*u=3QMY zvSJPvAO^W$Vs9L1Dv%&kDB!WR$B+eTZR_<jJ4d7F+{RvyDdsWx79|JFpKO*xiU(T8 zat7x-;H6NT$9_>+HJG0l49jmw2IQFHcT;w%dXsc^*wc1W$aPPDDc{}mmvlR=ugR;J z**mKS^tcelCjA=}g=l2HZRF-+R>*swBETX><olK0E`5pAW$rZQOaklMB{v5A&rtS? zBvddE7LvCWY)R$jgy4GrLmEJ|7(yF+Wt$^I^<ujp<Wb5a_1%u5P%Q9?!V+#{fSE8+ z|MG;pzXy5PB7e>!9Qd(`nXIjZ{iqw8$@)q!d6nZs!=GeRpqf}3Cixp7r>`&HlLG_} zf{w!?$*sRv6xz<UODz{L`Ys8Sool#ub8<Kl3^!x3aL<sVx$G^I?yNh>nu)tnopv>k zdTqM#(!pqBsXqPU)LtMtU9|Z~&xz<dPbR>emepnNGML-B78d<j@$|93Ad!fKhu_|! zPm8weh3WI#BdGJfJ^Ax=M|BzxuPX0JRz1o<oa3nJ>IfGO;WW=a_>&K0IOmyvkaJ-I zAE7?$a|N}g_^3kxmjbcp0A$QCJk>SwB$}CJs3b<`yvu}e&?gZ#>1*>5tSDCWkhTWE z8ij&*ll7F?ZyDVx7p~Fx@vBFH8U$f6xAmc)ycDh=%7FyYiht^k2ZFEd#Xq!!oGAKg z?AK-4TaPA~KvaWMYv9^&5ia6=i8Hd#bFKL%YL~hCxYS~bI+hBWotKpunHXJsw&&y8 zcr_qv+-t_cuty67&#WGbJ>{WTa&+a5jGi=M*eMqib($v?F|aJ9skl=`EcgC@Q-3x2 zFBpse67wLCw7%&7jFQOdxDaVQ$+&PysBTXG=hKjIw+ZO~vr+?!!2R!Xg=4et05&NI z2(fRPBN8Y^QfDM8(As%ZB60Vb)@;9nwEYjMV^_P*ec=*Kq|xU}gKA>7j&zxbkP|g2 z7$^gX^vTE9Zk_%kn6PH)!A6yHintTgBJ1MfV%@o=n4*gU<AU_8O1o8KW4Wr<)RL=e zt@?9+|FB4vY6`}pO^S8%T#A}zO{UWH6CdqgTV+gTew8FwV9}3G@^qf6+LEjmo92ea zGTqa|9uFgxWv;!^Gayryr)E+_)<sQc@bPbh|4pM=!2-<>*HuYX3WFIxyDZ$BzA7H1 z0-i6^{B+Jrh*pVe&80?7V~UUZYR8;vrnbklE=LG6R!lAI6;Nui3hS6B52O3UAZ3Ni z3xHo0g$gJEmd>$M;pws|@XvPl_s4l=oE{TI<03OJy1MBOL7^Dj??s5%+#6@F{?%as z4R#52pzWr!2VG>^?a%=;`-6=QkEmFDa%rY}j@?JcSv8F<p<=pdHso9Oz8`0)cNyfV z%AlI)a7?ba?xd=QAOky>yFiVESR5_*V1rthMaNkJ^z`&aC}I_y-0;yZnFjlnl^5f; zx}|y^>788{WvQI%Sz~hztHya+#v~!q>*?|H^7M3d^z|QD`*7h>zB7kDb?Qt`|9FU= zr7HUDoLT2OJts2@*pQh8iFvST5?Pu^Dgs-1#8#uuIr{Lo2B9Asr;nG_9IkTlN;<HC z0e8Cs3dxx!$S(4IhRH>`;IOe5qsl+hm1?S%ZaZpDHCY4qbg!Q}D=@tpu2cWycO+H* z;?+JnW<$X3;uqxp6B`$+pG_{<#fJx`$oX4WHc1t-^F|jd+?0FKlVEnzDuV-N&R#jC z9rTqfh0uAQzoyBkbT>nyENe0Cb;))CR<d~m$OgB@zi<)=D_>DvMPDP*C3P9n;>_R6 z<^A~K`EYRdH9Y=)BQaT>n@L&En&||#<l*4I1P|w8F~$@6qpzqbSWqIH*x%m|>OiYn zqFIBsBNs8!eD?SdtNnLObtKg$?V0_0Bmmd9OfM~jkcj#%%$rbf7Ma0D$Wk&I+A_HV zs9^muLomE`Ni2{bD!J@BouAA?2Fmnf5F95LK<~iM%Asvv1&G0Xhk1?5qCp&i!vN8L zJmVky=))Mj;OBeWQ)m8U$_3xNl>8M5gz{Ss3v=wAXet%_?_g~0nY!Jr8Te%EXapw- zoiFY-WP%!ZLPR4vLg^!1U$t^#?{6If@#vSV_X0yi0ji3}NACQ#<zUZcOBTic({*Wm zUU)`#Tp*b2{N#y*Fd@11@3|fGSsPh6&5ZbIN!_<`LwOYWGkCGc>+z#bIEKJ^3^K%) zssdbOFePCL6kVJR=z`I68%n9`$uK(6;k7&3Nf8+=FR!F2p<;>@v-X3`RpWhNk7&iH zws*BdSig?cjm_du95sr=5m!V;=yb;v?7{ThZik%%(Wzw+{<tM(zCbZG;j_$3A9j7x z2CQ*nem@|dszIL@|AY|^;RunP7Lo-SD?Rs+OM<L6m<R3>_Y$}BMZGpAr8x8>tbQk} zXZT;q_la6jY{qLqb-*{GPhlr8Hb4KyFI%pWK;D1(7<U;e5x#Z?!xxX~g;Fy?EvuOB z0f8ZT_T-Z(=EtCXN7A_Ojq9V2O=;1^9QXMbf>ur~nkF*HoS;QqA%*;p3${x{7C_N% z)JHctKlyF$dtN}1F+n}xFJ=zPS!oyT153{G0F|T5gm$zReL!+K+FU6x-=04H!bw+_ zeYuWBfw$%$lA_3~q->optFki$U<fI#6GP`H3IW{*G4_l*1~U|G3P&9-EIHI8gf}5n zVW0)7bU}`$MB#3<bghrdAAmcDgvk$v%dpj@XpmkLq#I8Sv5o}%&29^#H;<hyPEtM} ze(q#)hUyj&5V{+16dDfvm@gMMvK8VfvSr7`IEF5z`U9#Fm;nb-9EhSkM3RtEvlwU^ zFW%P{psfk0_yAeB1WPN1G}R#n#~rHGpr*|V<Leg)gAxds@pr0nHH0Z~7XAhl9#1t| z;lm#r!$}YF12O{oR*DI2_}nyTG$2hYmg+}c6Rhhc3T+i|?D2s*H=A^-{DW&=6P4Hs zfJMCiCod8ZvKTa#yBID?m*>q)#4k!&^Kv^yQmz^(fzg0y&>6s-(5fydm_eXwo|cDi z!O8T-Gtts%>dH+=_KC%Cju|ar<1`MGv-j=!>1b=>C>{7C{!ErF3}JE`$Bw(k0aLy^ z3gqR6%gS8v&;r(Sd|z**k+jiTbmQUV)EFu(R5t4{%~RbFtwn^|<w6`o!R6E?HZ(^u z9CC4-V1i*z#=xkl1yCG7&ynm(K#TbWBy8eK=mn&o`l0yoN6%mxU>~kxM?y)#COx|b zVh-vJ;wXL^pd%Vt)8#w(pIKHmw>+Po^VqN1ya3KQtbk9jZeJZ5AdsiTV0@jnswq`Z zxV~7&9TiPD?@U5DkQ6m~@<I(@gf+gQzwuPcSXF0YC`9a#OpIJv!KXFSSN!i1mnrVR z_`M;l;KM<9LAL}!gw6YC=UWpKVr}zjg9TK`ugB+T?3ya+##SA()NtfNLYP?qM1PbQ zZdWm2vVkCEDL<b)LHD4=W;rJyPJ(}-vlD{QYz3W5qB%C(z3;HA2@-(N_kb$-NYn}Z zp@Kxicj`}fBH<5RN;jCjEQB`Hk~@O%ED(JPsaIHNR!}@yK5dvW(mj@@Z)DC27cbUI zPN9*>1g2`9z6E|=KX}V20<bO+1XE`flI3xrE-*X0Tfpo2Vs&0#vMw$+?<-$!xN5H= zYDz()dEGvAiz8i0VJL`=Tt3RbQ?HmUzMCLrc_erm)QHlwdspJ+Ot}w&41N+mu<W?Q zLJQ)(L@?@fo&Jti1JcBx$`wu99@J)Poz8lX+goJ585S06L)=Q1_ECm?U1^o2wGiT3 z5GeVtPk;-`Hcku5nT7au+iL#Z3-S#rLf&A`c{=HDm7=*f>iBHG)skkE-aunqJTHu> zrEvMkG`%r|Ur^~KA=y_a9tkMS$Y~m1vKrrrxQghnGeVU^M`lXm<-Al3y2VO5GjgOq z&y(}(muYo*NXz5v>shP-SD%-MgX8;oS|AyxFIRgH<fARJgFyWBYTMg5`F0XDLNJOD z^m+i479m?!3ILyB!p2H14Y(ARrr2R8twNkYx_9dnOQh<Br5kdV8kxf-NcGYv{3m6q zCKOS}10Qx|KZh{>7R<(CS=57{NRcifg#0bNkRuBMiODD2^{TZ?Od~&}h*BUA3E1D* z-l^n^^RBVK7!c>DQ37SOU3rLkKgaO^j6Tt<{YF)|>6Y%IzIj#Rq&dS1D{S+=JjA%$ z;gwDBPNR0r2>2x+Ey4rgAKPjQIrmOz@+9`HfXUB<eTnDfkAfd}Ie*Ks`UoeObNL;= z%q6BXc^sJy(PEr28?%!yk}4FV4Mbk@wnGYFVNnRw{M)(KPG!cof#YZ1%I^n$jiV(p z{h|Ca_FEWjw1&Cnj0t)K@ls<4<W|@lJ8x`EC0F*B3#0#euNe{#aX42x7oqQkdnaQS zgMVMC0lW!&#kEEA<O!2A&IIU?Lc6C9<lWZDJJvgg#eJ=NR(Hlqr)-w?IuLaSV8GlO zP7O<uIK`p5v_i;NC|?LbbFLWG_i>ESz&mU>+rAR4EjNOvXw`sr#!>=*E}L-au1l1Q z(dKE>A9);afmmJraq*MhHJyA`DnU4_t_qDU0mY1_tP&1T3+Yj2WMGh+H&?URuy;I# z9gn{AC-yuVLLP~If8QhG1<Hno&JaBrl(>MvPe%An;-f2Hs>cksg}ce&iMzIA!3Dod z8;=e^x?KjCZZ+ouq+qI;IZl{vI0SM5(A<)z#8xNAPk>$)<jzL7P}mOeZSZHP%wL|R zLNHZXJcciuud!)h`blpIS2KP_Yz3W=Aq4pWsX@bRn@mEZU=n0EK*J$^u^H!CWyFuy zTsLZ5662bPq=P)(FV=p+iv2=?Muu({GE<EYPlxM78w9c7nEZI6$G?lQ5rY!<Z~DE2 z{@ZN2@T!7%bA}*3op>g+U)k_|l|xDY{CLu#sYq7B5&3lalObA{VQui^Q`GBAh+Jq; zY(0(thAk%LN#JrU1L6N}9g>9@%<D(_6^!HNNHhq(r#Vw7p+JN%r=D;1)5$7)>sT-I z3Ia=F`RfHQBYOwFyvFk8(Q{H3n_U+_i4}g564Eey6c%49Wgg)-DB=bg`p<+KgSz2X zLrL$q?#R|!xg?{REQodueo;R;1!4O0kU2e39v?~!)iJfJ0#mk0G40v%WD^GV46*KD z{#0VC51><@kZ7vE>)|swSI2?GfW*^k*{WrzlM3)G8fFpi=5|}{BZx8`WX4+72gKFh zDl5q6L-OD|W*%g5nT{f0!(fj$45L7)5$YH`C8lHIhlp)Bm8hHM1V~Sq8+fK-9>nqR zbF1nD-=`+ffJS|6Y`ydI=aZlW_IHZ77IGNSdOnF|IP2#C|1(0@j-~xT=rp@ZwuW*q zraa0QIN4UU!TnyyjvJ#$JDMBQLS6i{v{MM){^ttEfT5hya%Ek-YgGO~6V<^1$S_?2 z_Th_c_3_Xgn1xDqPt8l+YrY%lQmEwH>%oGR&6Q|c;FpW_2}ML0GJB=`j*`liqj?#E z#$RU-mWif=QZtvxBL4Vu4XOg3JRxdldlItVJo-q-y;lm@KN=*$u#_jij%#!p{D*Ti z%Eg<eBGwTcrzutvWCTVeq?j$)bxm4@ke_kkaq?k#Qq_F<`lIXq31=lD4i=o*B_KgN zZKvv+0XG+z@*&m0lKdsY6f^mWxmwGDz>#suYVi@~5hYvNN1+Tfu*;5HeN8E!9`Cs) z6ySIFms20ToXq?u{8zHbtaZGIn{ko&Fip38VAJB=$ZT4h_XOskYT9*ovFv9U^@zS* z9*8OLm9@4VuxZ?a-$rH|g4NA1#Od!8LZx@DfMTZYJ$mKU_YIu1ptIq{;$Btv$QU@b z;#4yh-`TB-C*sIHU?ZjbZjM4N^{4&7C%=1qF+}^(Qt(4v_5@@p%T*lGiMT`|Y|!?E ztE76`-GugFLxuFFXW*wrk|?RlkN~8;YB(p-1m?jPp#ahe`HzGnb<J?<?mbZm141AI zkYbuDUplOu1-XTyjn9KGF@Dx}zCTHOo*Q6WVJau&ZH9j24e3n<s9t7j#t8cz#*E!< z5n6lYB;H&ETIX-t3btIoos3Nad!HM^0zIE~SHUg;=`OB9j?Fh8Tj}7Ks*RG$&=^-4 zBw;|xx9~eAk8%a9s*i55lzG1Cz6Jsb_`qbOLw1e<1qZ&h`Q+asmvIta_i~FOm0HV^ z$`LRT2)b1JVmCOq%={NERR0yK1{^be3t|cT5H=+*YzH;OPKONEwwSbXI5j&MTfv2+ zWIena&RiToHQv!CzH9bGpR0=o>iY^=NUzp?9ZH??P8iCqRnva&qN{4**uV7O{d3k* z9Y#(N9>T=x4op%Ml{}H>7YB#h6L+Xug&Gq{*!c)0qVn26GqO6AYO^Jgivl8r@rUOY z2baY)_=8=(0{4}e5Y_eL3-kb!G0vq^OjXL6`=Sqmvm<;cG1rT6N6hsRPi{dfKA)}e zNY&jx6i)5OUL<D#XRKBWxl$Ap&|Pd{K@!<=yFh*8Zp4<B^sHFarTI;Yit#)@vgLBu zT*oj^TcBqlluBBvG~zRSnHkP&sq@k1&)Va74Voi=PT+Dt+*~1rYOWnIHcvrF^zuA9 zS+x~KLJ4X{baPJj<+vdnfp$wMI~z&<5+G+EuO*Dx3%xXuvm3uSd`r+2h*-Jjf1o-U z$anH*_hu-buiYhdwOJ_|?X=Uhz_w`!5&kds3)r@G(`%cFze$~e&^dzht5ds(eTsb< z=uYan#PlNF=&ymW_5KZyOf|KpRMkY#W}7PYAH;vzPgmk^euR-XTa!R&*+1cL|JXC_ z5H1NbTf{Esp9as<%15{$fZ&WMD-tQ9ZtB>gv+DTnN@Y+dhrw2if>)kLG}D91^5D*s zc|<EWY|t$P3%0BO@LYsQp6{kXZz(~@-nAE}-5U~FQUBl)+Ub;YcA8hY3eQ-@)<rFE zYC(1KlMkobm!%qOYmXoFy^AjekB@A*-y1J^@M{zd@>b4W6Ch8p03kamAVmiMu*aOx z=4R-eX<})zTCpX4Sn1YCcUeqI18n%2x~8`Uo&S~W!tMh8K&NK?GxJEmRQs_Ck6Zp1 z&)|r$bIfB_j^8NEtSpu4k`DxL^9?=Ko$ZORP!^j#us<+IsTiB{=Mh#Z>b7UVWR}OP zB70sAr+tES_^<6O3Ph}vx$D?KR>$L5wr@G(B)0AZsppsEGJvNMA{3UJh{nXkCHb{@ zx1jIQFLguA<VB~@LpSxQOPRSm)0x%yCk!ch4Cfd8C>V0?HkB4-@PsM!mJ+)SeeEpg zbD;*<rXiC7>r=&ykZ)OTOe;vCO}J1a{XESw-LIPKNG6M0OrZJ^gMo(u64LVGiVU1< zSw#UCM45*lC@tV}0`5chsMO3x0nGjVIK`KEyWSC|2#C8g!Nl5I3_GWwvN>6HSVc={ zp=_&zmq&QlL#Fhb$RhlNIp5cR_Y6PQjB<m~$%n)D>|zuVJ@M;~+1$Rh7+=h*hBioW zk*Qcgb4jS#9%$P!o4<|M;VVqgxES=G2<6i%K|Zl`IhH@PbimhVP^rv1;U3cgd=Pl@ zV5L@H)Z{H?{G2QBPsP{24)X|VRK;Bn!0|F+NHZ4f7Uol?DmAsvQG@5-cG|U0l<shR z#Eeo=^B&U31ky`^+?gw|WI6BJ>Zl&C_$Twh)mOe$0yqy`jDvHRJKwcnT}x7KXy>mW z_PvVsO?nR3et^ZT$Og6FtVb(BJZ?xLWz{uJckch-6USuj_jF1eG1rPHT7(2Pg=HA) zX3)$`n3)8hPKgA`>Q?o{-Uoww;LV(9ewlt9#|Nj<yaC{{iNT`|ppWivP1&`~#5Lb} zBSI5Tfb6;oCP4b%_$as%8A0=He&2q8{U3CG;;zruQ;cK~36HVu=P{*|*wDD;>EM<x zZEj)7i_N=N_r5%2{qE&TPL=Lm&NuhS-S2pTK4V7h|2n9N$88ia+-YQ%ls`>-BJo9c zD@<a>u^1Hr-OHt6Vvukg%biey(sgjiV=r>n!19%S{)2B!6fFR4Sp3H2@r4g%jg}fB zqqrX&aBILuE$gUG-uv2lBCaMV&Ie+ncop%Iusp76U%P|zU{tx2-=LG-;W;~L0R)xf zI8(M-#Eu{faX|TNWm@MSfKk85_BTJv+!TpMS_Wn!+%NQW31X=Ol7;#k`Ltw%d=4ux zU<tDY4NPflUQAym#YoMZCJp+}>k@o|gTAZ|n$L}C4z$^doQsh2y>rT4_CPXam@tCm zlv`>ciObGCRO7QMsJg4<1W0y34LT(x^t=JB>oF8`%D4efARb@;+_K#0yqzF2=hiU{ zY@ZufV9S7rPs@HtsLLJV#lPge*6jdvph@esd_j^E$81Y&L6|Fdm(Z>znPyA)-h%-w zZdd8#t~b;yNpJ$NP>kvbU>6n8vhWiu^Ojpg3lk+y9_7eBtep4v_q@wHro8Ky1I}!Q zjb!6uyhkyL<AmbFh>vRv#03?XS`V4ooJp)VqL|^-ng7IOH)=04)O>ZlAAGDJ5R;?b z6ThC5x->C^*+Olk&%QrkhZ&s^K=%p{v5;K%V?2bcvD@cS7VU*Xj-4dDDmun%;SElq z+6~f)bLK|vkIPH0$n9$%1cj0Vt{NH<b63eCJqiQhhE1-eZ4-UT&hFup)$4UMaSh*` zD{bDZ<&~vx*yO$m$49GnbV5T0>QbN35sruACj5#GW5Sd+a5xmq>Kf<#y4y|FjqzeY zcQeuRsNV<VJcADPnGt(}b4`(>=+xjj**7zjzktgp`Fy#Jfp>nHOf*9OQ+i&PWS5j1 z`H3!!2GkcG;WTGWQ4$?ol7qorLG`?asQaClPQrtvu9jaY9_i;ank5_p5&U6oAT`q2 znL2pkCW<K5KVRZ;R-fIUc(FIzPPoS#Q!l{A0$5+4PmMhJ6x*6_7Od1-W$0%%p}>;i z6LpCriblaMO#9W-4Ou{t9?!Q+vNC6E8b?-kO5G-DSVWIpmKLZw^Rihd+Xl(q$5N-n z@&8E;f9@LkBI5zeZLc4J+e|F^Zm-$(ft{tH@9Hmv{2Z}bkQ|z_7A>|^ck8@;Nw=dJ zXb{eN`$0E}zhB-ZSVF%ak5Eq^U?CQ`d6ccWFBJNuF%dj}7dZ|^a-*1Uq!S3IaWj{f z<t5yxf|ud<QRXclFe>y0MEr*zIVv+e?)or>{-=E{){VjNOGKRlc8oI<7k$}&;lZNO zN$}DS^Ex2j<Nc15ppxu^_|t;T@<3DUJpDU{t1(ObrVQ@z<=0Bi12$Edjt52iyo1<^ zy)KGk5+Uqb^Nn!h*VDu}#K2rZqzHr~lz|oCA-c%cA-=obE3R<ojc%I!>d+*;l)07& zYq;!E`p`B+02AH_i2Iz^B8ePH|Dr7s?PVjia>joM&Auu)qTAj~$Gmy}Ex;I}3)xVj z)%<EKw8XK;qV2C;JrsPAkQ>GOnbc;D=k+J1XQ(2!?n@D+_oAIxaHwyp$Z#J5wQ*I? z940;4fflxqGBdbFBr9<X4Pp@=uz-M>MO_6sBl6Ilk$6@G+;+zF;;)`dJEPw}LEYA* ziu;}0UoZ1!wVfhU)Y%y~9=FE<GE;hdhj7b*<H@CI{!X_a%;x>c$gBpnA5E8SAW$Q~ zLp6Y_FV$15;2Q1<7m>jkaGJy)4FlQKNmiEAUH8bpf>>O3ng#gdUwg{;&FMZwp7td6 zqb08@=Cz^*Oph=~O)9@1dR6C!j?8x}tDL{FvgbccPv=pZF~O4zw(Of;mG8BbZMKCO zEHWukSGdo!h8^UR;EVmX-8bOjmDzV@(cIL%Y19tO&9Q=oR44<z4%lkU2j5)RfEoF< zvrEX43U`ct(GrFt_@OIZDNC-lyZM+D3=<50ct(;6Y=C~G=1J7T1}9F+PW$gMCs(`v z7Zp=-43kR)PT{Bb^}LH*KUiW&Y@i?M@O!gH-&o@!4tOqc4O4}tILx}`9OBAW>>k<n zrd_uHefo9Jv@ZQAJk!xDcFGA1;gEtElxVfP$F{y466B!;-X>kWo{FCuZ!Gnk!uGLC zY_>fd5Z5H8x|5k{Sl6TcBLN%}0lWs?;AJrSl35pDlw%tUnX<HR%d;sT%Ju-3Aph~K z1Se4|#Z{JyiskUdFG}lwadl47fdyN%jcwbu*<r`(*tTt**tTt}V|LtO$F^-J{rcYf z^WOWbF{<|YJELlkz1CceQ=q#(($168Id-GO1foIRS@8yC+*D4hjy5q{{;N|MqMM5* zmeUO$p(Zihe<O77Dwfj4kjB27DV`TZ$0cfl0Kj*6TX7$YG;NpQR-4-kUc<V26_!OR z@5C*05VIZE{2$q_W28hJ*U&G_y6Zly=)KT}vKMR<QbWiZK%8M3eh(@-y0-TITr(qb zt{v>+SOR~H@BN&tonU-ijPOvZH}m&*!=FsWJ0=I)+Y0^yAFn5=0g@@-pO=Yy_)H|f z1B`|<;YE@=-G2>biJbANL1NnW$4M2#Bb}SqN|LVkn`J9JI7{BMf@jly{)l~r@$<Id z=s!U^GqFsA#>>)kTRHW2s|Y$~A<exgO(o(-#tFv9bu84zb3e>)_idtF3tD6o)6rY0 zj%5-lb5dO1<Hm}k{ZkgU+prW(IH;`Y1MF^a$vZq?DfJ2%_4MaiJCF_^rD1x`()Q2} zxAtSan$wiIm@C!HuahtLBq=3b_LK10vITA;slY_@f5-e-f356G|7>@R*`#76t`7iL z$+L{oIT?p|g<IS=A7kk(!x8OsVB@E{no+X%$sFFus|d-u*EOpxcYPF{i50i^1=?v= z3Akd0p6<ks_%^pO%9r9FxX=+0=Fn6W3CO0GyQ`kQK?H=om-Eq_7^CwANT!C7E?t<W z(ihekPssnHMfR~K$Pp-qNE_0e;9&mNUi4L(<=U<M)W1H59!T@~afN6W)e=5csa-Y? zdE!X^AT-PK_x@w*oMkvXN1Kv`8NjC!S$oUVD#G1>>CX(nc@hcHAJxkhOgd8U{QX;t ziW!L{8~ncYhQH~h7H7@e<DkTp%33lFn?1xZ$&BU7VLqm8hIz3;x?7*gYf!l%Wn#?8 zxiQr&7*C5*tMqA74@yx{t>)LHnloq5qBuO~A3g0%w)Dm*_*5P!`6{|`NMKR}k&WL? zW+HwX^&;xBOBRB}tL$ff3fcrTp?ywiU7cG{J@%WN)1MjM59J#FhKrTvw4>!i&$K|^ zP-&!0N!c}chW-14jS|>mSN=&qtHIeWOA1c}(!_cm`%NO4SW^GsxwJIAZ*JPgju7zz z2vhyq=@2&<=ckgv8{nz!W`M1~n2-WyTx{-c?JR+U!{D@>NCN~*jKs=GgMNvD-)H^4 zdb2BAu2sQZ>uHn*CrmnJ=ruHRruUdCj}|9FI8;ml-HOjd7T3``G6wI%*LQ+(eC5{d zFA^8%JK`*CYRF(52u7&dT@OY}1ZVA5;e3tTJ8MH#M}5T&6iqK7Bp`s196kLG?3l6q z9Y-lt!!>Y#)U2MCv^ys~r(**(?8Y2}h|Dnw;*?(+qnA0*z5_!>*}_<Xa5MeI)N33> z<VDP9!R}o>htC}SDHF*|WBo<4`2b>|hAXEYDnx@_Z}zknv8aR<*KMpPoLrsyLYklr zCWXd1j$$0)H${570PtQ>^91VsYvVCD!>64g*^GmKp^U0PmO<YiB~`yX0>ZrvE{v{c zj+UO1s$RH;>6c2_WCY7^3Qb#IX3aB@pJ<6YmOHE_D=RHQnx+*GNijXe_d%*|7^9v) zjCXx5BH6v7W7H}x$7Avb^5P+R1JSpu+0SxsnV&q$HDK@bZh_UvT;PSPf#vsNB=b%L z7hLKb&o2codE=zEQlE9KL(1F9uej&UopZbHXSUsU3l#ew`qdIwtx;P)=93qud&Zcu zHy*cr4omzRJgI>GU^%J*FPp|5?6#0r4Eh^;J;d8JS;`z1y(JjiLY7%>kyuD|Ux$4f zgl8hycEdM*eqb}5x<!8vw)?{k7K*;iSU1Ho&*Y(2JRoPupY!bsmxjPf$-?(+C|v=7 z_-d$=(?qa%>|`%A^ptTbF>N7u8^%M+f4R}=dKtEptoP@Ad;G@h<@fMeolKlW{3DKO zEAP*`AJg!Uz}F_rC{)B%cT(0YgHO({D4R7oL~^7~d>~P6qEMfIO)xs2NVjnrcj3i! zI(f}O=?^VENRU}d6JMfYKi6)U-BnM#(H>2WgngR}$B*-ocz6?`4TA-w52)<GWT#?6 z6MKYm9DJUGpTh@G=yu>Rl(dcAa<R|Qd&qB4>p5F$_m&)dnwKoe>e%_Xl^4yU6tiaj z)xkpscK}RsqIQap)FpmRC#wh9a?a1JXP9sYq_`J%$(tw|*uJ}M6gNkbYbgc3?U6|L z;0u(4U=z&oOH7d-#DA`g^2TRxG4IEe>^2{rWP125rGuz81CRs4Y%~X_KFV@H5oN2q z;F+X7-^=ULKSS)Jt|WIskf*_~?G{>kwM#GXz%tJrQpDS$J}+HqHkFLmvrku4ubu?~ zBlT9LGW<xdF17)+mw}SzG!45>Ci+h(a%)?9JhjkjN9i*%)#epAEc2xKJIGzLmfj~M z>|9Ul7>JBs<i!1H><jS!DOQ^CYkWZer$@=EJ%Rq;D(t5u<o{NGI<ufq{{sNwlh!qa zL<9k8paTKHPX@ifOlM5TgH6sF5CkF#kd7ZS3zH~b=~H7O)Sdo;kld5p#Ml<wq>~u1 z))fe(lx%$#J!)uRgj$MJLbzfTG0Mk=e+gC(+mB}Fc0opEWp$;$>))<z6PCVLu2{IO z|5>V0i|RBlEz6m&o@0E0x6zbLJ=v&$DqsBgjhNuK?k*>DuTQ9BINam2MF6B9<i5ak zo#Azrr?q(v1j;PW@-f@w$*w3eM=Pt!b!O&oeO_+91HJV6ejhJi`xjz=Y+viHTNpNK zuy+T|eBZ0iH>iwdXb3Zj+aG+cG{t4rX7w-Oa}*!+dMK3LWOQMbn6YS_W|!Eh-+V_G zbUqmvqr!-UjaH2tX>8~yUx8WH(zw2+PCv#@aJ2^8GuM<pD>|qc)j#L9KS>+|3BOS@ zP9N3yC<n)kBc9hmXigD@CD-D|_jRwi*a`CV8S6BVY&oi7RGL1zOZOIUD%8d3_r71f zEp3}0>{-mJ4o=Q_=4PsClXj%t7N$q(vWnXD+GvW~cdYrmb?D=JCIGyajTh})Ha{k6 zS`?5TTmn_$B{^3EFaTdZ+_9g!0iLUk;2(4QOw@z+UZqcOxsA@MVxVZej<dF;uYFos zwfXjAkgo$Xkin3MrXAR^Siec)Z4>em9gTcjK)^xtWa(w@9XZE7d;AL^VacI+-Phh= zr+-I#MICWzz6`Y+Ap^*b4HV({Yj()ip)morXm|6QpEHqpBwFXh%+f`)3SJWU&V8}Z zXmGs%&n!HN@IY}th&2)vX)9BR78R+O+Ce`CB#~+Fjj1}%<&GB>%87N@yh=x#kqzt@ z!~nRzne4B-&8U)N?Dcy}-oADgFy5JwhY$73Bz23l?99cVAV9<>q*zD?<30<@tT%hZ z%%<SI=zVlmVsQUPO-?JLO_eJj)+9@IahQ%j1*?#&T(vnOQWEZKrtb9YE+PeXsy-k5 z*wItz31{*!Vy%y3Rt8K6tGQ|j_`m?>Zjvz794R^8*}~0atUIz?g!BEq_C^tQ-bVAH zjr~HVHl4jT7I4x3rjc~e2XBAK;<PJJjJXp0sOM8oXk`6Cg2o^44V;-(fihW>!heyK z*#7L9LhJJ%!dAgs+GFyr5Xj#lYZ=vug}@MA$Qv+7=(ao~|1c^sYU2$5Tc6QsR>KPM z6Z)5C(;=bdZ#E438FXt1*k5jfT`wY6ESeunzHA#?o4{b}zw|QI<^eq3#LDlO1?Ik4 z)@l9u=mlA3CRU)gks)AlH90}lRGy!b_*<%$7CUecQ0`mHKS$_tD<T7GaFa-60t!UE zLLSH&i)j^MrCZtgIav_7Tnf6SCG>+d<m)wK_q+oKnkr4v5RHDCBFlO6-k{F#lPeS8 zREnJrUI0YTk+?DQYpbB9to(Jn8mNJe?=xh%%Tf*UB7M?N6`XAuI-gN%E2gp&4%*%9 z(ec<^WzAyzcKymRegeXG6Y4lI9<47RJ$BCJZqCR=zS!NO6Zz3#E(sP@XQZYvjCnr^ z5=uDTUG8h$?ahD4Zz$)QjCH^9jgTAR_bMG^odczQK4cAnhZ$ZOMpIHSX?!uHaf?Ep z#p8!fwH<7&9hnfm2;cb*e4@Zlz82I2-m*7kTTA_FO%eJmNL~Xv3_zws$&8?kpOszA zf5RnX37!NP8clCMqbTnh#77T)u|R30fx`R|4nlqEfQ%o20Chl?6`oI+)AbyYtl$$y zYy*TLIo912lSLXN11}h_BsW6E!?hl~7(;6{VbmGK(s>jc4H=KlWKX%cEoWJjHNSeX z7S^nwm<p)KNkE>kLMBk?C->l(KnrNrQW)ZzR76G<@DZlr$z9usag~2>4%*s3juvE% zyB}AW+$KUTge|&`2SdfO+r6?T0jgk{xImoxuoOR-TEq`QKi_P*CUugzjHo&j3$$g^ z@bedGkkDH`atA|Hvmx=H(oExw-zP55qh{&nYbwUBH9588AF*PJa9m<Sh<5NaJTcbE zCH^?<Qt~N&bC;oCNcx-WQBzCWRmx(+yN=t>&F;CNa>F_fkv5w%pO~*jZUAY7B@Zm5 z26ibtT(-j;EFNRVs32oJe^2DrzQ(4&5t-)p7MUf0l|YH&+q%Uc8>w@g`5sY$>!L4G zO=s@e+w0ChHrUAKAPxg<4z;2U)!M^xt{`4doLvrB@q>Pq#p#!%;jO8F4rq+RGUEF- z5y4ub!yaQF*%F?sXIvD~n|C)|*#Vl^RPrlLD^0m#sX@r3pxoz+3462Fyl9zlhB;Z3 zVrZe5=*W@sp<v|DvN;e-uGTrpF*t2>4$s{i@2cU2hEi^l%2!%mcFj3d=+Eb^-L0Up zoZQ{$h)<sVvZQ5B-jLTv*_w@~?_K>j&?zFwWleZQPPEpxx-~HFx5D7{w19AKfZPfD z#DLXacPVGkLLkT&b|1@nh=$TNtD7veb>r%2@_X7>O4{AN@=KC#b8)BzN<&x~@rRJD zg4?LpiN$=J)=UdbKu#G45B>W;7I`f87j)dxl*S5MTqL$-VUyx1mlqO`Dl(ecD)?EY z)S}5BRD2}WeBoVxh^<4^TmU3s@1NNQQ75pdx?rw0Dne=ilRqZgD!9{c)TdTQ)rOHR zVa~jgao}S!MDyF+&$wi?QpP`lq)AV)X2HVZFfA^3MAw{a_%ill*M#*MySVIHP1VDg zwaJHu6pEcS{U++t35U~T&>R+WbzB5Sd;!1m9NHS_3T)7Mf<DuNaR7CeI#u&zKDbz} zo`It?Jqr7M?eo|;<u!DcHSvr0+vjMSGR!qo@1yV8N^<rbNwtpLu`j`OW8oUa1_pwk zq}n*NJV&{&q?Gb=^BD(^?7uu>N4CP%38yT1am`3!-bM}x^~;Wt`>(3DOPk4WWVUvc z(YwT5NREM|;}dWkdY~4gWS90w8Z~VgejZ`Ul;Cht0=IyU+?I9_AIS;F!D*K>{0eEh zN)z5<`cb!8&5c*4cv@?#2mbONzNi!zRQlgN1VxJC1S5o2mPh34k0y#%_HM9yPlRB} zp0K*KqgMpuab2gz2Uz9@(g$2Zi9JcwxEG9ta45Ccg-T>K1|WkI+L~pA3NxQ0+!2x4 z4s2>%Hqc=pGPMdcqOE3VdX{Kp*)pCc7mW=`TuIc726PZ%U=%72<9onl7&}FA+%ZZr zvVb|uzjpTJX4OgV<|gh?iD60r=ebgt8dN7)KAM$6m&MhbLlSeRkG-N2Hw+GQn46Ij zyhUOwrVHVFFu?2fs!WFi8O#37ktaN=r$qB^G%w~jS8-#7sz$J(9|s2-1J$kU;jSz{ zQ&*Sbz{k|d_})P-!P#fXFa)|9r~l{U_l5>YYOmC5XPjBx3<OYSZ_Xkpm8GLgs9T(i z;_t!W6o4%LAwS|yR0I46s3n$zXXJ~GSWU~+<TvlDcmRndPCnYF`>1#p8}9m5-1$s^ zUmhghy+%=Q<_2YA!wz+7>d}<CU_@_eZ==H=uMpQmOO?GpL!}T2qZW1Y^q@ih$}rNW zO)CoF=Jyd;%Lac#v`?3T2M@Og<xbGcZGijTB`OW+-<UO37}lMvMDpYxc()|D7(3{U z+~4v#e1SU*c!goh)R`YqrGsaY&JDy$QN2pzsxoU<o6BIQBr~|NyN@)7zUcB0In4`Y zdN6tqgD){FT)%!Coc>Mxs%`BiWQn8Z7sfI9KIo3p_&^$H{L=9Y`~f8=zm;^OyETd0 zk_Gt>t2pVeEFo_sR~AnZN6?8k1BTx_p}5IxZy`V*R)?ILwL}f#QcM*kYl?URBilSp zlcn=L4S#rom-xgvZtot*F;w0K)83&A$<vRcu86Ck^ibY`wn1C}#(dbElpqsH4(=6I zHnW^uZo0z_Hwk?k6w!JuqgKI9`jiq-r><pDA*jS@N0Z+xnY>Sse7`<7>IMh&fnBIH ziWgv#vL?gCZ8~>=_u&?jUxakZE$om_adia8O!tjs4eI>PTYoA!(1?dnl`n^{lc+)h z(Ox2^g)IB7TsiD0z~qnO9@EL}7oO2in1)_%MeS!72-QXuTyLL#G-Bsj#=O*wo62@j zInYT0mYj34pQ18I0Vce|E4Y;*{9?O=81_J0QCHlXC6|7-zAv@R80heK`7QRl-nz7i zDy9Z*CwZoOA@JYp^Y~0}Oi<k4_xDL5@Ie;ag498^pNR_t&+JT!mxb#V3_k4|Sqt*> zuX;itoe>x2rK`=}0<*6gT{jU(H%TG|tLG&_X=9Mind@cF5$=!azQ&3%E?KA8F#f&& zSVne{2*PLxx|c<FVb8wa_Hq0&K1!RhZD{6;Lsh6r4T$xB3<qS^WyY(!PagzFG!Det z%I9xbr5KTIia;4OwlgzIPRVQ<yKLtYm@%n^%EUuYWkT;@76A2q2rtb4ud(<1g4Dzx z5(MNM8VJZg%|3`@bN6&5<bQSiRZyrGj|d<jxa{c-=Kpm35eulmqP9W&Iu{!7Tuzb` zz5=h)?!!Z1DC}X!eMQ+$u_^ncjUhrZi)PnMA5BMFTRidDwT&PxT_Sc_w~jH4t63xF z9CmQS4e%7d`ua0QMcs)eo{f$1Ke<d%@;4TBVwIyavNaV)C32L@h&QiZCeq)F?gV0o zpbg8=Sq+a#|HI@@N1$b?SdS$#)=E)Hk<y9Yv)e7y7&4G^bMzELKQS`1QAx|lcJA}m z<KgIhtC!NlU@((W8ov*_QK2;rjj<5ij4YGXXIVwjrJr*7MK6A(l?V%)Y+4f{sa}#( ziD{&UH+6LXYTKH=CzfPtpz+gGdhV_qvL;Dg7sHyX*{%cNA@Fo!p?L3<q9=7;RPNuR zS8$o9{|&b#qT71!leH|D*;_>$D7${|_Gd*rr8@>qUsVo^Bi@~Jra1)j`Lh4{u`UG+ z@)ptEF4~7lJn;CwO7UMN+c-)-#?Tz&emJ?wD`zW2_)#}uMjqQr(3YR4VTkD8l$B<? zaebqGp^^$fHaau9Vta7QP)7DG3=mbVRCI}8d{1F(K1j<iU6G+Zh}w{HpAok!c@C1@ zBrq;n#wrD)fTL#6v9I@X^4R23m`{aD8Q&_HL-SEYL=FDkper6l3ap4J%d~3rnA)=2 z8qZJ^WI!U1IRCRXHPF#!pV366jn>+C|FI)o3sM6tNUsfHwKq0@ds!iqh*8K;#irr( zYp<xIJ*{8sM}I7}2>gO<MU0vUU1}8NE~6e3Zpz>77e+LVEuSB^V<o@Ymk>h3NhNXm z#isv9Z(4o#iePd~EM5H!VSC+79$^St6N%={x;00e4e9JcIhjDwiNK|3rGne@jIml^ zCi4eyRdX*Q)AgYr`&do>!P1E46+4P$H8V@Z^{sEP?)^p!6jC%L_cz7<oKU5XCFJ4) z-BQhne49LMH!=Z*O;;jvgY=bfGe(qmm^xkocH0pj*h1fuT{H#l32e-_n5<8iuAbMo z?B(?X_xGct;cAcZ%XsefXW@TbF7CIZ?RtHnPpZ|S4_OY+&OzUP`=2La!`^^;*}$N0 zBMho%{fqvOu6lDki_gf@!DO{b(oqbSTZgbmlZZtF#d+WE-&k`nrZY%cNK0s(B7u*> zafri^p4$|uwBW8ODa!tLObn0e^3dC+KM$Fy1Ux+zW<2Zy4Pc6y0zw7bHrVvFj@XyL zp!plfgUohRP-f{KvWBJ+3rgC*(JOF?Vdz8$;yq2*TA_ZRlSSWpf$f#n0`cB(VmTmj z#U1!!Dqb_Ors>f|W_Y*57;YsPASGmt<-Hmpako9NN19?3AZAL8co@iv6vim^2ld8} z-$WJ0CFu)^IKjaYtIH;qXceIp5>y5#97K}exLmq$UPa{cXSulYO`C~8+CwQ2!{Ipi z26_Ago@}lq?ZDSGG;Y@#1#tx<At7iM=7`3Kq8{p^<@TCo*)rt2E)197l;8joYQ9fm zFnmn^NQNhi_`H)j$~j_2VfzfmH0-SWd`d8;Sn)ytg~A@uq&()Qgpto)sV*x(?oV$O z&9A7Aei#SZh7-y^ArHL{!9Y}>G(@zN8c0@vl2?B<-Bb5YqRMIPhwFBbH9O05Gs_=D z!U&2|5n=uYI@@pp-e!5gk2P^9j-ahbo=f#1V<eT6hckqt)i#fSp^?{bK{c24c|c_B z#?v|cPX^;J^7QHc)35meZf69T|CgnL;)cPwV^zKjeHN<^m0*qHwS%(e@fL1Ar8MC+ zjl^NM#8}O{Mj^=rqR5dmo~MNHjybf7gKR32+B|vG^RSLIA$ZKc{+b&0TUe0V``Bx5 zXoM$DTGr*~nHsTPabz(_eRhJ%(Dv+#8=*c->upeds>>H&**edy-nJEByc|pX^J&u& z4;C*(>^91749Q-^&w^@NoT);OpP=<;^N!Z7fe=cZ8Fc`7J5I|8>+5_~+e~2l47mKW zFC*?2k5$AkF*6i;{Jw=iD2GaFvD~PjZ=c`+8OSx7-z;Mr+)nsJLr6!BKo%?ES(X)M zAT#QvSinfo1nR70HQ+}8U1Q{C!8Mn+d@A03NL2Ie>&mL70@~jyxrM!+2!<nh;WIk- z%dDENWlbPL4LFbIDglb9l<M@ZnEi+jAB;u9&z$G2?}wL{h?(K9QrZMKBBdr($|s^j zNG|w(cp$popbb2yK9U^6lKfIP?s9j@PeMacAR1PoAY)+n-aHDxt!BN5)>0bN6-4zs zMz=L67|%2w;sPv}<LivIA8gDBIl(_mA|Am7j9puk`MiZg!-;#bxE~&g|6${^D6cXG zw9&i33O_nfaLZi?QPr?2AAVWD5EjN~i2o7l%7+iF>UoH$#<mEq1H0k@B-lWhJey%= zqzx)?!13n=KS2YLYhC!sx9DtQ@Fr{Nf)fv8b6xJsj&PfzVT6ULnv&=~4&a%%(CF2L z#RJ3)uOQ`>NracBXIyWCQ&4-K{nVNBOQSg;VR1FvA=?+po^=dQTk7a|XwUIq3Dbqc zoAYO+;xW+IgHsi6Az{70CbFAx+d8PV1c?GNw5WpOxTt{Dkd(vm!~s*eqT$pN-Gh;F zQH5csK$v1qxla;*8VCZL=)X1i;YW5HlSRU-H7gh@Ed8`dt-9r5i-#gXlD{yJLx{si z#Q1i&=^NDx(yg~^#1pw)fFG3ffA5a_w9C+an6n|`q;VIwq7f_4*er-iB(xK0q6EWE zTqh5x^oan(v>N7@RMaGil;r!M;gyFr2MuDmXfN=eye3-V_pPJ@DMy+Xu_XIne-=ZY zPT|FrJjJ2%$=`eh%oAG9r>(oe&p+Uquf(fr8=sD1%&2I=HRgPf=G<0#tYl)oxt0%Q z5P{`X(Pp-`WYm&v53r80kA~p!2>XG{3|CIFdJh0qk~216bzNH-^k;EKx)es<vnpwi z79LJ4E9R)>X#N65?=%+R_oV{Yln6y0<mij1A)I1PyiHP<*Pwj?LZcwV)AyeLe%IRu zBe^?M>LN;;$wZyFGZCSyY}47y9?umPX*QNXr*>Wv?QN=&;O78gs3le+t{7=3b!MXO zcL-pb@fbA+4;#P##N>qlMZxeMRaY=zoO<_GltB<Tybm@Qd+(at@S1ExFQS8be`;LJ zM1HEa;)V7!Z)luv@PIfUg-8HRU7pIOWqTPtR+fu+khMRSNp}7BIi9Ru`<AxD7!Q?7 zGxTwZjFRa{@k2HeG{jZJ)In53=iMK~pD;jLO^c-4RZ3X`^c9jO;;rx-7(r4@wXVtb z9|hJ{-cPYcVz#$Ywo{dhAe}{AWY3$eW`gZhR=z4R(PjjS$B;FX7p~2C{CXH5eMtLq z1}CXABmo)*dcd)*cs9&?$B?gQ4C|9BH=2*ARzRgzV9;k1+*kxA9ujp!pnymYQ5hKa zpZv5D-w!Eb6sMs;lx2<KRT+`OsBX|LGG^gxsX8dCewu_PKce?T)l}#hi=G~!wTk@> zZDRJ&;;3t%ap21O@NV-R=gt7{Je{D5((A#nFUGxv-df10T}n;6h`MS*#r=`rW|FuX zab|4&+P)1RZJI*FbIH&Nu1umws~)g>UUMH*o0V-*c;%n*@RttfzA+#NI3IreUA{Vf zR@C}HM8(-k>1(XegXni-6+Kx&an;#|&X9{6k?S+ia0VurtpoECHGD*)M@nZqyobF1 zQU+CmWsEQt@A6?A2BM3xh}tC9^3g93bwuM8IEtd$i4W$&$kKf&8R_n_Cj%xne4PF7 z3D(AwFFLgfH^q88Swb^Pvd8!;(WA!(AK5C`(MrLrvmGj+wL{F<-42A*Q4B&+Svl5f zrv$`>nB%yi+>?}@<#5~qGv2J|p%usQ4L*iXc&4K)$tBh9X-Ccahs^hV%=waK-CVUS z8XCAi+~yDqicj}=GbM#!^#O(veuJ<_^Nq8%m~$f_W){OlLQyiUZ7%Csw6jx`HH3>@ zPRmnqA$NC%tpRN?iBRp^qQ;u|O%m=4#Bk5@t_OIGC6#?o%4}}MqvVQaZ^v=QHAYvy zCZC&-uY|s%+R+Znj3gDVRbQMM1@_*wFLp?;LVb+}!El9Ke58zcBmnF19kZi$b9Ter zP^ozX)G`>o+3|sNvmsQ2?m7#)R7ItGVedSQ2NzTa@4oLfR|Z~g>x=jUYw$hIW8ZI> zUrofbVUJZ!;70LQ>b3u>O+eg{KshDJq<osVzp?YTQ8J<u@?k6!iyYHKIQbw24;%Y; z5T6Wi=-1%HCk0FUdjQ_r&XkS%S-bN@s%14|48FD8&Au0GmV22+%*k~QcqDu$_LZ#M z`Y7u8jT}sSJwbPke#P_h_|(sR*ug`M$aodh7!k<KP*+hh)`c?apbyDo8~|0FfKZ#3 zd=T7fgo>q+cHt>vwvTkK(<i0vU_1S7<EQdd>MVc83P6aJMJTX38tefylxsCUA2GcE z|8FnVoV}+8``^~-(;Gxq^ItdR3X*IH;G`R5;2|edUKsyx2o-&C2l2mE;H}aB&VTq= zEA@kcfDFO>$1+Wru9%4j(~SB48|=TsDwsL_yX=2GS`+F&fN8puA1GdWZYCaLGxk^M z|K?h4{otU*@c;d0n*}l`2kd{#R+lwVw*Hj`V*G3E{#yo^I(XRIIvANUIeL|9{==Pe zVfa7SlYGNS2Gv<73Ii3vM3CY(6lTScT}~%4v^rfe|DH%E&FcnyBGJl_+40s2fcHim zi{@SHTIqAk=#VYAPaL|BbV-_-<JVdj;jOdS1%x~z%e5Gq6E^P3D|ECJP@;R$dd{dV zVZu36PT>M0z6U2})=v-%Y(0>&*}<E%UJB}i7TQ&v=1pgze*<S2mlcCsJP6IC^Ex*F zD1#*1xhSMIPPRh*RAV7H_}cF361uuB7gGh#dQ~|-Pm8X4FHvv4gLSR%*GfPsQ!PZ@ zLSec^5JecfEbuJ?YpwaF2;ORkg75~GQG597fRh_As|PEw;+%~-r%aStmb<AJ#y9tf zh&;lou(q3DyI*5i_?KS8**kj74dY;yDmZD*q&mA+6rV|e=zqS<CPP^rI5%v|MGwgy z)~z(7Ors9jW?G)7y)g;XdvxFN_X<)fc%_l?Z8-Pd{3w$C?&{`>If-|fzRM+J0<Jee zD`5%PQHmg1M4uNP=O$<w555WS@|m-Igk>bH#TtCmQDb9OtTLnvCx<Fk0%{phinaB? zj5;@Ndra<qBPfl2(HO6dfLcgl#yCmdJI)k?7n$h>WEShVcW-ojK6iuFM@fTC5{$9) zqf`gcLjOuogB2#e?ogl0jy&K*;1@|nc6<YfYei80XbJR&bpMKNJs^cxB=-`4U1Riu zA=31U(EtbXjx9K9U5jeiZ}7zHpGzYJ+}_dZrMi#7KE<DQILe>O1HA&OgAk3>7Rz<b zg>A7+-DJPi(d>g1L^qn~jpJqxiiNqbTDymkYd|t|sdN~a&|*cpRn_UVMO+$J)GGi^ z^x(~Oht8v<)4|dpnW(|kqS!FR6Z+6truvm@8KF5scK&u8RqPJDm_nPfdZ&nMkqw$% zpQTbf?O;9dtbbmb%c!1u>?=M)k0+bG42Q;3P{grp7Fiy~ZFP3X_GMqSsdPzt`W*C& z2Rezu`Nm&YxAI|i$wZC3dB$7IPToLOIn>0H9Qh0^Y&DimC?m$+vi0&C`W=#(%H$7z z`ZOwWee!oZBHEaQC`UII%6%<mGH4!hBZJFNveta5<ot(D=+4C|JLL@%18>AUYR)l- zQ)m?dud9eSnVb1C{IBj9g|>Dv+utkO$x7@m!6)k8mT43jF6d>)l+%bhzG%S43iJg^ zd~aU?^T?Ak#-x!{2wb3Vy$;TcKZ&B##L_QB5W|CbENYB{Z!b=G$k7rBh|`S3o3?YK zOQV!pZ9F^|0X2*4FzBZ$_<cmNR?h(@v`Ay0wxO=(dR@atD@1q6?9bHB`haInOexbS zeGBnD2I@}fk3QkJj}`bKtw6wfzL6_?4=<N*de8!g{24#kb)(;BHPl;*0Z$5DQqCDn z{KqwGu_AM`(?I#VpJ*ezmNdU{7P`oH)ZX~yf&5ySZCX}d>BMiPG4pQCo*gv`R0iI( za1udX#yjHheZ4Ce2T>^t$AzMuj`7z-t4e>+4GR-~BRK`-j+Pr)ub~5EeCkWZbE{JV zJRv)J5WDqo>4wSfg_l%uv8GZkv;wAYY@VL0@8+(b(b(O#2_8P;I{PPqW>#YJ&{{1* zm$=FyAL2|c%5~A8%ZJa<8hfQ=UY#Ux{5(+sGH1gy*wK{`@~fMMfe!MBTc*)Mx1Er* zgG3M7q_>KmC1IoNOvAvbB@krOmSMa~uKOU=yZ#IN_A}pV`TN|IcJNAjN|SL#q0zL^ ziVBzY4l<#@>~x#zu~)y-!p$807@yBP1#S@9Ab#NG?&M(n<nR+4g=jnNckz6WfZQ<) z16{Z<8@mPOt`Rnp>-xj|#y}#6Jl*2E9*5r5J*r_~zr;DXITIMLaS&gI{x$W-t{!0B zHwR<h`8sxtse9u&Lf;aUQ+y<<*tvsEm4tR?VNfV3viW9%m!P)fz@>zEcVi;jRmeFa z|D+c%M7~ZX@r_sSV_Au-&tU=$-EY2A%LIe8eq$Tgg>6QNmZ>?hK}vL}RU7GV^C$HG zbw`2!Q$DoC6M@qG@BV4)Ap=DN|L=v>B3y90^Dihx72-d9zvTQ|*tR!H(A)n>DcY{7 zK^>v~d#*U)^+gN<1jGs<{e}&cq%D>SR1M<4J6dOOZLCBfAOUXw*`WhmHn{!+{Ah6P z7+TFO+*_b*D_xV$HKE79_OeSgqi!p$z(I;k#BmveB~Ci-?%nqP1Q8B^kVNi!vZuqe zkt+CeV$bmgIoVmqMHaWLx4y_0C*HfN?na-~RI^qxRsD}mqc~%#m!A4{r=ybkc5O|6 zUQ%m)-}49H)X>YuJ_2;Meg|_RY1*yZDRV0$dpun|JWP$RYJKdFPt13`(9-g)I?mh8 z>0`diw0#S$hdz_<joq`+GFKzCOMdWk&P-27MOryovQkzz%cBzi+Gb-)jwN`mIlN|3 zUl+UkPS?g^viUCVg(%QG-}%<y^L$iV?vST{I<n5kyq+=Ud;>`3P^338xxbl_N4x2| z*`>9xMbFb`_F~&nCY{sGh-p(#rP`9+cJ?Ga%5RPF3R~9SN!V7^(3iAVc9Nmf%QIz0 z3Q?V_F9Bk<L-KPG+V;^DsjCEE)}2L=G9WSC3;p7~P!Y;0cO%g;L@>gKX4-ZEXRlQW zAp2Af^%e9WP{2~1)z}lA6*7Jz#!tDXToD_lDm)^rMZ_3pv<p8<vp|(pV$-#s$`p@u z4(BCXkuASVDr9i#tqS8cFBceaJdo28u_*RH6Lgl$l*M{qBP5Gz{C~s^BmaF`M2~hV zQ^^wkK9!e`I$`lLwB9ln`^~N?%FMLLXrXP^k89iqw+)c?qh0sxvZ)sqrg{sa84-S$ zS2pi-@g&)~hce};Ka@zjWcL>;KOW2GVKh~CUw<92g=3m+yUfCYwQu0_x<7i%=4I%% zm@(XZ^^RKxCog8<sXVVT=g?<?mez|QMtADFz7zGh-P2OF+CvCCunr){Xa2#}Bp<LH zxv8DD>;>qhN>MssXZZgS>j<?)01NM#^1e3cDdZO9skua1v<z+D`#VaqaO$JA4#UfA z4?1jI%_mg-+byyD(j|k^vbm$6tx=Q^XS}{g6h^dU;N&Tt_k=#<{91)j>h6KhHKSI_ z7?$Kd4VCV|Ea_@bqIDC?lwUnbXux=G5|tji`3}(U_WpXasQ+-fches;Uv6<O?rJFr z-^=E6Kvz_BO7o$e$c;WjdxnUnX5Nj=$KEhW=B|_l&8z(ApNi5$_=WsiuU1%#tK9(% z>mwJ-t2Y#}$UXq$!%;`0R_+xtjv$!k=sX8fFzm0ep82ADF_xQhx>Il5d0k90Rb92r z9|GW*LFtcOmpgN0pr($P^p68ZUjXSs)+l~U-XnN%-d!4WVyqN|L>UZozi!f$@VOSJ z`pZXG{nkvWHpv)_e+|J??EyZtcX~j%UqFE|(@jn1m%`c6P65eMq}j{}OPPQ`oCP#A zv{fQUY??-C@vp|c976OZ5tNlX#ua~%#|XfZn0U4;lxH)y7lpKHAFj<@QxW&VWJ=Bz zF-mRoJNA@%S-*;*PYK`OhYJMWzxWAyu5LzoeGu{*8QhtBvP{s3hSZR=Bs@ixoqWT& z>^(uD2veFBhWH9mWoYtk;-XyM9pvU>AN{|OslwoCGX~>gB;XN%T#F<U%F+bsW^iC& zeOF<MeKisyOWhD%(;CyD81onSI0IWtiBKn$6b!=L5au3hj0^2_s(w2PaGP|&?^Jiv zM*_zz_f8W<MZ#zejWcsi8ZeEGcGIbVpRKjMxmM`d9wBkoD+nk}eqWI+Yb4dBS|%H3 zs}=g*Vrg!Co9n5TdZ#&nVTx857zkWuRa~PyNl2Q`Q{+rtEn+{VLb+Fefa)2YS~&Y$ z+plXIr~5o6;#Pn0$C_y;6fj|F_2>i$!XHgXQw)zx6a<FCv}$jTi-5D_teJu2?mJl+ z>VsiD-$84}&iT1fMqkbi2i|z<r-n1et?kAcGwhT=+@qvj>wYY8{w@v>hXJ}u-9gAu zV+sX=ArY#ZZ2Fmnqx;rs?vkFcxVEsPKbk@!87Dg*MFLCxV5t1nbSJ)>T|XybbP$?< za$L^@y9*2L!0C+YXVDMhE*$J|Y6mP!BkJPz%n=MMx^RcZw1O7+QhtFI^xY?Yyyp^y ze70m0@CA?7e6l!t+?7$v#{kyPmU_Es;f8**3q05T#QF@Kmu9&E77+XK0a_=l&VrUh zE)Vx9h|bhUTIcY*$L;iS+H3^96IFzFxMHr8^apI+5w~VBq(SdY)9V><DPmrNrQ<tn zl~YmkUDpZ$<D~8lf8J=|bS-y3sn8rebYiIbvp)05owcIK@4SeG1ONfQuZPR|(Y}wO zu*&Y7uS~=qsSpe0HBB?MwOyHgv*<Ar$|4U7YKOh+{Jh+k0HbQR@Mc49(k#CLwN9F9 z=7Bm+I&tE@jfk0Jk|?MqY*}B_ee?HWj&M|3Ses}!P6lOHoO;S=tvC@>Hje?v?-1Uo z(Lpm=RJN(-AHg0)AHdm!f}6KMW5its&UR%f&E2AIe;K*AN=I#LG}xHx3uXGE5ttvZ zJ!W9#yi64RsUf;#W5{KFG-5^tk*8sMKOBvC@)B>tA5m}*j+Q)pvuCiS0LRR`z54*= z^F&{)4Dt_y;qD^{Ot){Ou*krtn#<v`K~EP%ofF$1CkAWze|XwYHI{fcMI>nYSCL@j z1JG0-?uwEFt+4eft2@jJN@3T=y3(B20L`?IUV&ZxB~6ubkLDeyyAOL?$co&VdZ+Gd zqHfM^m28G2)h<%4AG$wH`l+H=e`tz$1!9%Qmsux=)`Ifs21X?Og`5LDYJeavksD}M zF~Hlvq&iTLcLV~VJn5M|OsD;b(y2~t!d4_tJeyE$VhiY^H^~f`WoX$m5w%M-)R1iK zy#%eHL?aS!clP4K*qHj|^}%54tFrUTgo%EkTTLZ`w**9f_@6)NlKL|cRrfLZ%04L; zLhH8=3aP3_vEiO1Oq7HLW<kCBo;3E?QJf1Kx~(l1H~?!~zo9{7%*DWEYPHBeU<(}y zHlk8PMZQbhtW18e<0cT59Vmcr7k^PDLin_j9Uf&O6?fqCLcX)S6$QbEDbQj6RK<As z&G<92d~HKs3BsYoY4~6E-+IQ<B7afgw2$k$xDF%B^^X13bKYKbBNxG&?Ut5Hv1iqv zL3xXyYA%49u{Ddx@HPm-gaa*|xMkJTqp!^pq4XphtZ}Q^sg}Hhf#-YQNY`y*2Bo1y zt<|yp)EVe``n;M?j`m&X#k4mC(%usAqP$*U9HLDWdzV9mBEAb^iR90t_3b${AnjG% zzR&CK8X4z(H<wpxx?P9@DFjUIxZyHp|Kd3g*$8l_=o>15_`TGlwEJ(0;6-e_&cHGX z>i>7JNbYOed*2U!)3@pz_j&HCBnnsMA+V^mOWpYE`hEd}FVrJ5d**zdn>X*5%U0kV zuqem!r^ztgsBk_WIS1>|7Uv(&^o$FtYL%Q~E49|K_sOQJ&dxA3V!qq~`1t0a{A0TW zO$e~q8;3)QE*IXk*n@!Oia8TN>2K}7;$9DwnVKls+SH*8^m^#8)45|KUeU56y~SL# z+W=0^(4O+C^eG>EBu6}9L)|jo<F1N*;U2MM`L(4M5kDw<YpU(fqq2I$(IBk%qd<2h z4uu~dvW}iJdeYs_kzN)ze%ZU{+vgL`9sqBk*T4H^NYo5a>PP>*x6Hp?UlC`$Rx>mv zfiS4twly!;OX-ji6>zIp2-8Fc;0`j(C`MYdFQ-F;49b~7vvWh=G;9R_ZdZIbkToUi zHd0$ihmXlaA%nF5holM@-*Vo|;fgPt5+0BnWRa+~=fhqXrHWN$Pxj+oFb0(xR|mqK zKkj$H>gB%zYjg|x>ZEoU1izEV-3IG?mi=vz^+!r+FbvBJx_1eccd3y%Iohey{nZAB zaOvWB%1l$8k#3i-3Zb*XMI^789&C9gpC2khR@~{%p8QgoOmJeV0Ub&~Wc`EsjL;cr zCn13NY-q;Fu(n5X4>$GKib6|nP%n^3Lhy7XmB`Xx#ZAbl*{KO`R)qWzP?DFI6XnqN z=|VBhvl*MfllBc8j~U@or@^%a8A~a6m&AR2`~gxesk*?J5jjy52YZ^<vSjYe6iW)8 zz+LdQzKz;JU%>D6jySqc$nWu?o_a>hnY9IZ3Hdi63^B~-ymE#&gNVpcnKMuZs~(s| zd`yiZntzQUq4o!UrD~vbiWLb@3r{~8gPyBOQ=I9diB9pe?oMPYOj}CTdjU145G#jw z^D>P}JAVHAX_oCN-2PUXx~cDiyE!wchqedFu%#5mFyTg~zo*BeTq<tp(j3mnQNuz} zEf$?%vv4%!O`XlvG13VrmnGn!|A-T#E2s%)7n54(Hq8~x1ge5=te<#kAs-^^MOH8e zXZj6cONn>Ngy9D+hT<w$jDb9(2zZ&4jduypant<zpG{3h5iReKQM(QJ9cPAQdlmA< ziNu^P#MS+D+@&z-`4z7-&bWpC&V%z6#wWDD6q(RuyhTDMVkZQHaT>snmNwJM{FBD? zk--Rz0MR3#b!~Xu5M&r6OR@2)0btOHT*D&<i_xDb^07<+YtdW4yL4A&w(>S`)a=`t z#Duon6uzZ9y)hS2X;&_BJf1h#loI|2go70a9mig~7P-2iaK$Fi>3qz6FAnIuvDfOj zrEQKT+R%$&HRXEzw^~5NJm-8f=CXK=h0<4cP?dy9&kJEYxU?>sFKD&5uBIh@aC9iL z@tuU#Ae_k(o{odyp^@rb!h<1~+Y{79u&;qID9kucmMHdRTEe?V->MOZSsn>eblHj2 zb6fgZtC(8l+(}hT9+J_-|8&t#+XO*?{+C_l*P}J-i0P>>eg#0~VC%xGc^kYe2y8N@ z=)duB(`CEo%<9vGJ9FOT!>#@<?IBOc#GksF)WrLw1;>i;Cnp+WwiR6N3Z!HJ9Bh@G ztnX$?xZU4$S$*rY4w)i*)RsQgqxTW9IMUbdNW)9rdIv23NxL)&Uc4yuv%3@QOu0{_ z>{xM^_g>NeWr`V5tEoR|`~ew?RxaJ1?^ftS6sBc`B%ZUv95S1pwscd$QcIfZuLzIL z*INOt(AIYs|Hp}lH-*qfyf9Sz%Z+}70<vZR(dNXerGk2SZFx-|`|V8Qi|Dq*7?I0d z*n1mK8+N_q$}C;IWq4DQ8;9O$PMl5{$^CnM-6;NIFD4L6Y~ja{>##rKqXzOP0>ZKK z!0}<NF*D7hq2y*TOmFKdO^oV((T*3N0q^c5yyY<5#M@ei`H}cJZ?)F7b!m3)?H&G) zr6mpGW2G1C>SH0?>g=d|X4Y5mao#J^NATfIV7^$6w)4qg{K%BZ1T8ekNkg4?at=IN z?mXrjyCPspA=b`d+AWjyjZ&Btez2gjCrD%>2*SD}dV$#F^}AnDE`eGgvN_*5lEv}q z)R5nbHrCUu*^?C`A8+FWfhHWZi__VFiK7{&$|%)25yB7}!IU_QM2M!@v_M!zbovH! zP$8QmtleLEAV4%vdN&+l(waWjh(pwjDG2`t%?!v?*ZgQi=ydOSabYRW$S0=pN0Cc$ z%eLbYLv}YrB2K@&JMR#jN#?N1a}j^$C|2z@`9`N^lMT+$@WyS5lo0VU_1?fmy*3Sw zpaX^7P^&YOG6aon;=C8m#rJb+GxU=PFnocaI?Eh|*se*I{({}3e#wM$bG>-IWl=h4 z!30DebW+nv#|Zxh4$fE>@us4r2tJtmN9?aI8p<t}@_wD*jePe(62eIqRx#!!yY2r4 z4{pilXvgD9{iSj>HjMwh9NY*ecxVMWMkMJt=!OZ8<>wL|*_uZF{OWp>P|iBG<H0(s zACcB4BA)R6i8=+QG@0H=%5ZfqH<`!sy#r9l{%h`)OMR7M#`F_}>DUD6{i$n4hoXVP zp1%wWDF_ssY3){5%i)}YS@=(ZegX6s;>hF;dkp$4fw1_GCrqK3nAVVK<`G8SKF?GC z%`Q;zpBo(t;zA$CA>Z5XK|E(DecDD!Pjaqntz9#qKZr&JX?6?1_Hza$SPSJcs;~jk zX2T3GM~#S6?{D9F`B7B|<I$)Mu>3G=M&s=?Y1n6KRa%U<1q*yGuJzmf2uHh{Q4bBp z%c42o?F;Rk5Gtm_3u;<~G<Y*U2l!BXiCh<9@et-SY9=u=K<HuZtH<x#i5knr2BZGI z=>M?$P~Sj9xe19Dpb7+Bm+r54)DHkx-+=XfO+2{mR?pFERxCdqLp&jkMF}RqTi*CX z1=V(eFhTK@((7Gp=Z;o>H62sFitAph2RDo*)3<ORHG{<ZJ$>KPA!l2mXl8y8a)Ax8 zi)+4IAIHPGm|o;(SNpwIQ^@(zuQ2M#^g()j(Qr!>yF_lo^_|Ltvf$j-Eh9jyqMj?& z7v>f8n^2;0t-TH+i5XOq`jskmf?`PYwQ?c85y)9obY3EKB9JIyxfp(ZXkW=Z5Si2} zo)*A_u$n^LE{yk=QX->A_$LrubbmWfQxwYE#%JJ`1J9J0qZB8is*3qELG@rtFjAbe z?nmZghLl^|*Z-0AsqKbGS_)WYrhmfh4`5!xa%d-P&=zHTh1t2~Ue5)yT|MlcIOoqo zA?Fd=x9N{c1m5zq<AjHaEDuQHZ<G&9E}qZxWV2oVp)=$vaf*n8B$H`vPYz!(vz5_G z8&?H|8Ys$LX$yY;WV>Ix=CPWf>s2`W28x}z^Njy_(<5yeGk{ob>Ip=d$z+T#5#^JO zj`2D9c($(S<j(HY$f_uIds@ytMN<hVc}Q*NJQ(~Q^zoBurKdyZeR}Bc?JSh4)eYRS z=^7W49QKgC9V(rJYB=P_RQ^3PtcH%hquGP|L-h&!MlN0e7jd)gxSc?W*B6gTb!9RU ziJ;MC6yM9?z;B*VOdW{az0^7?8Q|+RQg9@Aj((+4+!(|~|DD`P43>v1OGb5KWF*K? zRI-9wH!AWn=`ta^0{#$6-7ssj-Q5l{SwukfAgO|f0W;~w@~l}?6{V*?Gll-i$z7QH z#C4sD9GV!c2Kj#7I(S!(`AY8TjrX_Jo^O{WPG1)b;~ip4CJK;uo9ZYtQeJZvDF&S) zVsP9ieEds9-TPBUi|2z9A*f?nzu47%#v`;?Cf8+KO5g&Y^)6WAW0vDrL*Y7DetYy( zZA8NeQ3fJc;v<t;+ZJ@osGGS;p5pu9T;2`gNK3#auF8*3q-GX*pI-$ymJkCOjXoVW z7m5^y0f>%%FVa8-3#Y9P9@Oh)*d$|AVXV?~#(cw<FiT8gN>{@G|Mpp{hzZYR#EWZr zzoW$QXf!o8k?NzQMnX)F0+xvw#Z!UxxvEf;Nh_kTf+6JR?AvWx%PR3tfx;|EL>9u7 zk$9%M(+PN@XAgWf_cDbAm6P)TXQrD1xvbui;CQ_s;oZP3d%=LKiO};c`#9T&rs_;K z8T0mb;@QjVFAPdMkk$*v2RVo$+*|SV&Ia1kHEZxVvh>a~8)f^z5Jx<fnOF}SJz0u~ z;XM8w@Gz1|+IL`B%0{ObocL8?sbZ_;XG*dEmi_z}qCefYmY@X!@cWEH4xXRx9AR@Z zGJ2EY5IKMrR|4}iPfL)TqTFI{{55O$i5(%(-J0J}-XA{M9I1o*-5^Vi7lyhs!l!|o z)<nDSk>J*Fbngi6zOj<aHYdZ4LP4vQ)*{<5*K>Y6GTYKnfRL*yl#YlOOT(_q?jIAF z$!PsV9fy#A?W%Pgl|!(oY)XmN0m=fyH>FV6V+Lsa{Gw@=>x)>l<0@u(^F4@73gtIz zIxFgcI)52NEcn4~b856Rfv{OMP|>O-g3EekLDyY-C78(55LU1Az7vXnMf!7JA&dZ$ z=@G9T4njtUK_%xP?+FpMtbnT8v^a5eKltb7Zy-Agj#Cj6r$Mr8`ccJmRXlFssrrC# z)CAakJwSvydnoIqHPk!p3S>m^e$7RzX?#=iFGvqNBc(~%`7+)3A>@+&J-a*o3@(kM zmJer@-J{c|YKD~4*GW!!9h>GWn#QVwy<WdhfOlkF1|?pCV<#n|W*v);kp3n#s;DZ* z+-0bZ*}(@poXp?%nRN87zfM(l*i4mps2Nz=@7R3Y`=IAWkRhG+R~9gB%chRB52Ezn zHzo9KA@xP0Dk^9H75lOh@B4MIH}Ct40Oo~@6f^34^_0GaNYLp(arfJeP}n~wCm%93 zb5G8^L1&gsx{P+QAJcen{CrAlQ&awt>g1P`hFd-aLny5d(@O>GU$LAhM9o3u_b0%a zn7^SDQBG2*6UNKx$ErhHrBeD98^>p3SzAcoXY7M(S{Qlq)I$N+Xm@O2!~{#g|KaK! zm@EMnCEf06P209@+qP}nIBjFvwr$(CZQGvSdH3!{Y`h;)kyVvnX8u4XZblaqNw!1V z_-UX7u-kF_I7I<bYm*HZ{wuKw>!&Y{yiyXcI^`3nGyo{U3R~bLE?6pw!39y|6jFU5 z6<Q?HyLwGPCL`<a)R<@i(rmL*{pkmcPw+KrfQuiwP7Rnv1MUFd#1k{muRNP$bkF$T z)X9a1fH%fRiFLhGyEO~Ga0Fe72V)=gTpEih-;Mo0!H>Y(;W%%j^}Hp{M^-L^?rGCF z&ti}af{G!DM03g}GEm8Ki$*M-ACnO#)FnTQ36R|S{G`nP9&AZ~scXoCBhj!0_B?Zh zCwIMPaRchEY)Z%48XZzN-Gd^!^ZvylE{JLC`{4~zU@LJvl(<qd%@pIyA_M!t+qMay zbBhM{Wdy?%6#AEbIe#E;f-1*HkVXuhp@?u~f}kZ7l@z9|kRGLG_ArRo63ufXvTDW8 zlc5f&^@n@VJ-tyt=?JUixjt8(Kq!264Jx5N=3uK?>oTMRzb=h3-*K#{fXgM`_L_S+ z*_*^t1Xp>qs{4?P@AON4E@+reXZSNc!P_}%VHo*_*tKg#Twy8UaL*N6N?|REJ(>Ep zDTY942sHzSryw`Iy=x>lhAB=}V3JQC_37A~N&Tem@1|{l7fn_^z7bZ;&DI3_=D=me z6FN8Nsz?S?AY~kgb6<}4W!Sq@DPQ%EXB02<#sn4|ECMJqc?jnA?c{0)U9|Y#BbEy7 ziEAu7mlT}{sNmUs_EVw`u!p<-$t$FuiZ10jnA<NKMXNP2VVd$iDd7^0tcq-of=YN5 zBGGp1o#I*mq>IUN%fXW<5)F!Y$BXo2nePy`{IoYySSw9*RiQyOB}ric(`oqOj_ZSj zC2JH<Mp(lTEyJKQ2rAx*{2fptb6XC0O9?hWUXS<t@3Eu|`cF<bkoxt^q;?4u-uT6c zQppYVm|gBjjLFJEItAZDIJ95%D~>zKllf_db1?wWY0;&MdEGL4r12sud5D|KHsejf zB?^`LO9jvW@VYn<MAlr?E9!>RUgp9K!JA=)-D?QRH_FH<22v{bwJf@tT`$OF{Yv)T z*)xESBMtuHbJPmGz`0YHI^6a*gIe_XJ?Oj0lQ05`>5?7pea})5MPos+wnrt)wN*zw zUnvkksQLy(h5sp7rF`WDJdi76&u;h#Nh4xY{NVP2N+(cs?MMuj@Scv^R}g0BSH>^q zQ@orP*+6rby*{V;PmO4;r|+vIjkD&nghjYZ%(u}2B(hUf1$L6F<mQXeIvtAmp1()& z9GfSi*7Ds`E;^yniNSV23A|xKa&OZg<GJ*J9G!6ffJ8{URN&1374{<kRo#5LSEkji zgvw&A$h(NYuE=pjz|jM><`qRc%w4OfF(m|q`17~J`Mr_=5pwzZw0v?9$>NT9D8^B& zJaaCgaf-R_@vgWF?!?b!lbFrdQ++k(dg!bw_Uy#8H_=Q7Gm!4V<U=UZ15?}{g@ZQ$ zO`v^;-zl#Emi$plC~|D%1q8fAoOUN-u5W;fI8qp>%$OU(w~`i7r3p-M1Yi)FxLinc zsi*)Hn##~9P>SuG{dvydr*83K;ig527M%qKqPLU1-Y&}%2Wy<M9kIh<=}TrZEPJ55 zvih=or0wN(cgOOjRyzOYDYtQny<iNmEA9Ajuq-dHPsA***Ujho+_IWpL0qFPx3ow5 z*9D(zHQ(sBMBwD(Gy86z4w8c4-fuRu%<AXka+k;6j^9GhZ!gr9kdjV{ifedS<;;-+ zdQV(QhDelUKC0HL4bo+}LKPTieNl$=Q2PO^Yk5c$@2&n`qQy0}OgTXxy7U(S6Kl{v z!3yP|1#>#okOd1<R&h;&fgE;_D{OtnktEYY?jj^Le8sukh5BKz+kYFw2jyNT%D>(Y zmN$*bqBam8C#jp<z-^z7%x;YFCvrzy@b8-3^^s{s33xMgzM+GA+}0Qz7xd?@zD(0D zoO$p_w*1*72JCx3kctQ!<H5QB5D#~@8atcE45xjqK$%_8&&yyQ5sjjJUWo)!k<Y=y z{1vfX&OEEsb5_E_WVv78a4eX2qZH+xAMFtKH)7^O_mEQz;8@4Y@fAK6v2C9q?+pY! zUrHZiH8dT45q@GC(%m63C-PmwKmRMxV#%x+<O2uPx~d3V4fh{sdI^Ks5E2py2p9eT zdE2SA`oNHYYz^Cg1Qf#G8+}0w3IfoE4POm*Fx7g(Nq-<sY!*v)0*YT^1*UW;<q1i; z-=DbpBvG+AOKb+(NVl=>v$(gSRhGp}60ctMY7K%7th9<5L?`Gy2s`t2D@cv|SB@GT zA)BHahPJWk3@T6qj|oYcihcapQt!pSOwtqU0vc=pRGXQiIq_f4qP_b{Y;cIneMxi( zK78}`xXMlW13DkiQ@p#3js2vhA}+Qq8hDA7aE#dy^Qg-~d!4hXsp{_T&NOgyj@hle zsmF`ILOUlB7CodnD(mz|tVbx(iqf0)T2v6to&K&d>b>j#o1Mxn!xC9t+Osf}LoqnK zGX$vu?DLF3_Ccz0R7<WK%gU|>dU53Wf~CzcFeFTNm=)xdIf2TbsNUhgEt+<9F5^?& zr%=IR_q65a%SKVNZTP{z{zECGiB)?w99lW4ULuRu@}usZ>NS$GEMn|{UJ)S9AICKG zIEoWU9Dme*cJzEZ{(fEze&e%S9D5MXOp+@BglX0Un6aTRRGNwl3JOf_)~o>a4p-C; zU)QWsw8dab3OHju%H1N_Eq&$PAQG)jCe{!PM*;7X$GBF>U+`DL*$0Qt3PE>{#n3=~ zJB$G;1ikU0M6}$hN+K%lhT766XF!G`Z7CA&)gj8n*okVir~W1dI2z#b6^XFdY*kx; zweHp`kO&aP0&hT;L|ini*;6=B`4Ng@y{QzmIS~#2!2GMAL}lJ6Z&6SNH1<`K6Ia{z zUL66YN8Cj$1xNr;l%7AgkOkZea7o!!63G5e(T+`#0AWg2n3f#uAGY^14flD=JHp_J zJ<DLd07X`>UyeVhyj5rFPqE?}n>eokykk$l;M46Y;oZV81QzPkpJfSv&VrQaKo{r? z;uD?6w%FXI;tyh2WDR0@#eQ39#=|pG^-dwY(p(e)GT|pj7>>=kOOLsPICQGy2ub#C z!^<IT$YZ}1Dfglq*|^!t(UkI7SBV5q;$%8A3mmJ7v`V}Nw)u+e?m}UdKgc<N<7)rW z$<AcZe2gurRn&=DtZbT=0ymAkYZAvdl&alHutFBR;uP{BCF)dNTrmfT8!pnt6U!co zUx9QDvr5Ql%!$0QIZ`Vn(ZgaG*7K;8*-ABMYS4-hbr@JR#utJU6jwheJ!Nb8N}1Fp z;~=G__v`Bt(>r(e%DQ*e_wE)@PS|YLY>N%661(_L>m-A)oLkXO3uoRSq)=1NmB=Xq zy@Sx5Z=MCIDV%2cC7}|WJb4<69JvsjPbkka*590<>&<fL1r<v+!y-q&+EQtg;DW|{ zNdV?2+qIu*Q?UJj(()_5?(lZ8#hXU30=eeNgd)uu(JTtKBL(Y*WAOw4oUp2{o<$IJ zZJlWU=|u4nr?klOex+5Z-lENEK}S3Ux*1CW{KQxlaUJ6WS{nud%7wUj;}ceswSEO- zD~Q4Flr*%p3c)%DCs=8OT}NB3mx@hfWnqB9LTa&{F1OAK1Y9u$+&Bz*0se#o8!4_p zFMq;Ys++6qQB>WWkFgJ^OM>7am$AN-VZ94(#NU9vLN{gz3^&U9b+G63iL%eHmnB~l z;=}kVXYA4zNuHa>)jSA1WqS9zT{L@=H?}UExCQFW!a7tka3%&v8_Z&nkf&2Ok|LO? zgDQDu0#D^VeN4=A%{T;MpoRKcQP@ZhPqV6gD%!j~^6i1su-5{x0rH8Pjylxldf4nH z)4sjhi}|L}lNq@S=#M*hTgzbh4(8hk#qk)P8}7V^KQRhdufQ^kzFo*C%HOQietNmi z^cnD@9l%jxQgwrqKp>5lDd)A5E1Z%gP2Pf{lWZ30{I*~~EVS5mEKrzm${+?NH#oyg zse!KwC#-pQXL<(c1vO_z^sF0O+_!~)nT46ygW(Ms()U5Yy*a>xsUVE`d@8tTdL8ic zAPnsvpR6j_q*IOLP5FIwi4y9GxRc|^;LC`Hms^a#Al<2IFmpfw`u!!0L2}L0&tc3z zJh<KM_En^Nd_xlf7{<&5YOv`1RrgUIvIkvhI=L3kV=92dBQJ*{a+1>%u2L~=YNdaz zE-u2CJrdD6mKNmC9s28j<eoJx_MaoRFLAM{Uf6UfSP0Wnbr!VB@K#x=BNCy}AUoJT zDPd7dc)2u3Id^~k)%M#sTuT0dCQ~Jen7>gOd}|ZlU<1|Y&ihhcz>V~7DqQg82r@^W zkUiIC+yVgf+3WV0|FTs!Df}NobiDuKg6u81sa)aviCqh9{2Sd89*+Hm@AnRLJZ9_8 z6~~N6zcp=B*}4epa}6qSRcQ76Y?^O<LV@C7<_#0FLd95OPqbGXiYINBN-*eN=F^a` zTXNo>gMsFGr#$`gb|^Wj&xg{KL57`;SGi<FOu~sHmDKiTh|Z_Hp0$y1Va_&(-X326 z7t!2WVg_9F-xi%#IXmDdsQ)s?VjjR)|ILU3Yn>D0{?pb5jQ@hmQNF;?fd40}{p}P~ zpe<Xz7!Y8pj4ZYRAcGJz+b@K_z{I%Lbu1J~DNA>1cX1`h3rI^{89-Y|6J~MWw_xH> zI>js}i#BXTy3?V4Dr8<wTB=c<)GV8gE9&7^AvDfDqbAX&rrg;IB%Kwt=U7uM`=f0C zaly3yT_q{w`ARMT&?wyquHcI4^nmhEde3|}ZoO38Gx)a!XgKb&>*ia{264kyPr`uJ ze{^wD{Z<P^ak2rICObe+AJEpG9@w`E^0n2`FSrR=otz_72EpPU{?voo@YLJB+~onZ zExGPLT^LKL*k;q}qIg(Ud{s9dy?>f$k8@Y&ay{mL|3@0Ic6aaxyUsR|J&XsTD~q-S zCB9y^1G(hPMZi`I&Xe*fVOi?74dq8@$DxxquC>nk*ZlZf^zE&qU5M%j1GqN{{5=a! zx?^o4W;+?1S$15>*jjB3?f7HHEx{~$<9gMft+YL>9ft8WW;FHK^mm_|zq|1eafUkR z+V$e<Wl3fL$YraNIETTpeJP7Px_mmYG?x<+kE%XDcHK^^xlkWa64TM^g?$y(0JbS3 zkvpeZYiUrG5Em_Sun5IYF$tZlRT~vk{B{VnmJBRI_>aE8d*v5I^ETxrxD?`DX)_>+ zuEdizlVYkV)J?6;w94m==6uL|BymM`swJ$8<|8yfwC-nfmr$n16ZHz#9kQF4!HL0K z$<%t~0eH<|o|CeLBRuJ5AcVY4X<XGN4i?_hUe_&Y6)OR}1b$WaQgSL*Q=;sauyuG& z6+4nKJ$5zQ6`RKmt~YZXEHfNR0aD&>1B>h|ff^S`etbx9DNrl50Pp7j!_^xkZfdiZ zA2tC%wg2C~IDsehBiS3yxZZ1!H<47?oc;uz5i)3lR^77pOJ)V=?o<8=3Nv{O^Eu+5 zCt{rL%F*|SBGcpO?g0VbbD_lzo0}k~txn<EP3P?CQh!wY;^`#GC^hAo@Qzl3Oq!h3 z%!oMQ(%C<WlrIqV_Nhxn+2-yEluKu>d}x6HXjaal!UcE~7SmLR1&_M}j?&>)Ngg{% z%jySjMQ2|Lv{YbKTKL&uHafE$s1m5oY*7r-Z8?{6Fd0x!&}n)8zhAK1vyh+Jle4&q zHSHL2{B4;9<hrmSN^K^tly+&lbi-J>I?#yNTTz7YT5xt(Zr<&l^AfZ9sS6lXtna*l z)!=Rp)M~5d0a{s=u<@aw#f+<CaHz?Xk%f(FyM39D_p#!Dh$Ut~?S*=Z=e~@J-|TO0 zGP4w3G<H)ie6n!q?;0fRie?(KyeQA--@C=1F#Xu&H|;nD^z1NZnLn8i5jH>NbSBy$ zc8PTh{-Rtt6h5BKkmXpdm4y+}+GySY_cIro(gwa|nk0Qk@mlFYpJK;g{`ikv<em4F zOxmHV0=>73I*le+0`9*+f=WH}6}CoHq5W4RZH$aTc4jwY#j~?puZ0L-&GiGTll@m{ zseMFpfg}0_%Bl=}i?-KPiKH--?a-$}Rld&<yG0ByXp8sRmAVY%@Y3kQW<ooFEIux1 zaP8B4YA7MdC^F&BNB_ZKU81Dz_S-9kNx4Cwo%kJfb_z<qRgywN@r&5f{Bp(by0;?q z5@;3PLr>(No(&V1o2aY`dOZZ3?3{$evqS^J!61=dI+>gqk$Q4NQI*ih8xnutojwro zS@AK0bl(wz+$XkZU}hH8rsH@(wKuv31Toy(?e^bt;q!UBBI~>j=PP>DhJIfmd$3hk zBd{s@vm^1px&pt+vfV+A<hJ)&m+*wlyGe~U0uQSA<>0xAJI&@dE&aK@8t(Ajy?!?e znA*5MYe)}&!-~gZ#1%8a_Hwa<=$aDA8#i>RuX&E)$bMg9lJ{vj@e?fr*hfU%p3Jrf z-RW;7ogjPuz~vkYE<P*DFT1>^vFrD|)EO_nyU4gfR7FQqIQ=`e_b}0iW$uE}Kqd`` z^l<P{?LSOIk(SsUzXO<FAxY5l$PLps6)wVDxO<Q~gjrkNM@cHSz9C8u7tckKZ93e` zc{Fu~uhfF2Lhh9#xx-2T@^HJtbfVw>i}=4v!0Cuk=wL^j3Yv?%@;}kza78__@W`6d zv+Sf3Zne$?9-<uH_bvIyuS;m5VYN0+e1wNXhN=UYimbzp3AM(Uzt&T$G@TO8eIJde zKC^}tbaa?ley+Ed#25yKr%U^Wlf<(bP%@LTsW`HJ{uJvRuXaBH@LbUZU#U5Dciyna z_Uq8A6yR0@TsqI(p)1pdeW8S_<PRK6R>SHi-(hZ(Oh}QbQV&56hT^w*89%>AwqX`U z*Ms#xxu>7vFR|dE53$nu{;CDrF@3!U|MIiMc*iuz>SZCv_I%BkTx>pbxfOXdT0-)6 zL2=(kkO4-zAi-;AH{dt9a_!RL2{rcR@1mAJ!QaLj<Kr7pfvf6Ve41yAL&~ys-QJ!4 z`Tx=E{~@!zgMoeiFLC`D@{i2^uh=3+0Pg_*=OJ5Rd8#V=Pi!f$Q@<mDsR5d{af@OI ze{=NfnJSVb`f2g=REsJ%1|o9u)K$tVpa~o6$=i={Hm_=K!)so5xf|(UYOaHwlB7f{ z2-dgL*UfW0&Dgfbyha;r7BAF<QW)#4t;#WLE3}+xFQlf;ZaN!w>_nThZO<h1Qb~`@ zgeO+p`Q1wdA6%i_5_i}=`T(`vACJS9FK?%dqq8$JxieR>Vl0+qR6MHpOuDTFG)!eR zU-7wTQHvK+V;viU#ihSdg)Avos$)#2QQsE%YaByPKP2mnCqR70@)<`vtyQER8(4s{ zq%<eXjk)K#_A=c23ohf@2SSvJD|Fc#W&~Y-n`)~PG+O7=Glk^MhXHVF9%%{wW@2<M zNAKr$?w#I%JFyko1e%cZRiC3hIX`am{4>XdnP|)!HT9gLA56C=csjOxptnL|Xe1MM z^WyX73XQTToijorq|D7uD`5=U>h~7}rjtTLZLR)MFga$Z0{I$YjoJoqv-F4<!!FY+ z2C%lkh=*8_q@Y6x^newrf}N;E^sD_Kd(G?Kyhl<S&mMDo)Uy`JOx~btG{LPUdu9Ay zxLe36B~Yc?=~583DIyv4-XYisqnf-enzip+MwjuwMyIaeXkg}NRRRb@W^u70Mo2rY zLLkL~&B-3SLha_4yvX6ologXnI-iTbcs|UI2#13xZO_n1M*wt{9*nl8mJ}+jT$>U- ztfY&>u~0KSDVk}~f02ZtYkz+b8=DySoZ+s?2$^&~a@@+q$C$pBAJUlh#XGyEwGKK9 zEw@Oi+vu*E_Gak|_@%GQg0oQRu&c)~yT<X>diH1VntkD3XjNS5wvz;+`VTiFqt;!* zFibsUA;tQS(Eun4HrJ!``m31J;sxPTp5KuEfW;UVMI^eWYBJHhBO0Qdpt|P^cQU$I zrQl%si*Abs)SJSwpwQPw5?W;gKcR1)<(~%*Yj`fdo6VJurn#NN+%%R@^`nYrli*{( z+J#buySZXrGq8{G6x(dg?D+&TM+Ll(CIO;DtfNRp6aYjG3cqBtq3c)lm{m6a*ia=N zh{yE^u4ANi?}^QIIpkHn$-kRr7Ubj~Z?VWvC<ove_nA`8y<s`Ut{beXtwMyxOODKd z`Lwx!C$NAKUkm*k_G>L;Kzrh0p#I>Xuy(=>b(}_oZknmJnm>*cS@?iwW?_&<r>N&m z{!HR6mj%SF!Xko=NUJtpYUvTlHP}&py3oc9mv%`lA|P&let)gM*8;LiDTZDp!^>Bw z7E#98EMs8AOxM70upQu&U^^j3SBUCItlII{T#eSqqZKY5S;=scqs=`a?MggIpZJ(F znDCjdY4&{UM`tJ>pnyupt`Xc<`?oJR8hI;m^8t@)Qbf)g;bm;<1wi8PD76rnV5)Qi zaKi&sB$kJW-53*R!3IxmM$>1-v!`@V%z81uJ9pR(L1CgRqK>$^;#ZeTP&nhQ$K8a| z?eICS{>a+)89JTB7oYDH(lSCKMD=}InM|2!I|ZzL#8~B|Nx8G)kx^-eo`}_KZO3*x zW&&u+x59?o#Q&9kX1L5v!&}&Ijqum|;@kC_*SJDao$kM%|8>%j!UZEMnY^B1`7%er zG2VdRCohb`slA1&@rvM>T5<G|PFY%Gkm?a9(~Yd)kQ}k{mo6&uI2r1^o_az2_7+{n zg=*bXFgZ3Jc&;ztQc$pC`25%py7kEo)&PXSC$Ho;G{hA>Wk_3z7^h44Ii^K}=ceJr zC7yggBugb8yPO}KA0sBeThP2PntmCMLP{$w3&{Ojiw=t{rYtwH0mD!7^IunJp8spd zQLnqjzI<Luvf}jddo=Jpmfcwe5r;cTQjA<QYWS|A`$nPjR;$4Fqh$GY7UhR}&>G-l z-gV9_79i`uELZMpNgNT!(|$UxJU8@*qmbnq8$p&sG7GQvD8DU@?;_c`qW4`mEF+^m zeiP-(>d*Sx=}YZ!8*1BX2EV6`e+B@|iy!Pe>@1Gcl?Rt^=t$!E3x08t5Z}zg$m~lA zcl}vg?;6u*E<t`O=_LU5H<Jszf*}C3$6Q~uQ|;Lvaq$>65A84$yI+h`>7_JVf)Nxu zUfq6D{}0}9c&6u4p2t4GPtM_2m=GY3-m}RVFYQ%&6ulj}1Av<LpcwZ0S*mqM+&4Dv zkK?Pxs~DGJBJT(bc4IUl?a%+L3D@iXu4((Fe0sngBYR`VLA{^oFgod{k^y+}G10h) za`s3TG=-SQ-q)Uw3%y?j+lkD$wzg?iGfHi`oX!*u>Bzrx73*zFR~cTF(Ocn8qteL` zwFB|6gB%n%C~P2dXO8}M8@9X4PM0?H<Ls491d5MMep+l#b&aNtWNpqp@CXW*D+wzw zBp@H8Ke!f*CQlP%PmM<LOtjc?F>;djqOa)MVby`DVul3%)(NaU%#WzMlA}-G7RNdb z%`pr-oI#Nh`VKT)cmoJe5sQp2MdkRWxcJqu77>y8O8(w=u6Or1{QE50zP}AV&dcPo zgM0qJXOLUB;(<Lt{-egJ8cDz!{}E$sbr)h!WFR0;{{Lx5LP<5t!$N93N&*f-{!jR} zTi4#ck^~5-G8G7jG_^ko7`-*F444)e&`@?!p@WwBpq{KgT#V)Y0R0DO2(G+>+QaVI zy@yNsp*BiwyjI(P9Zj|}q2QRlealr+S&e~Gpi8D(n~KQf68Xz|*=^yzP_{5~d3v$p z8aizc75OW(;X32NCTy0kwv+t1CV`tNJeNLcgNKqG+A@#4eA~7jdDz{thlm3a0B=4Z z>spBDt)^y=e;LJ$xd}nn!bp(%EM>a^Yi6tp5=1F&gavW`W9;h;*4JT27e%MPqPn_- z4R;Ks>pO)mo+I|8^Ch0qQelyT?%7$@3bCH*a!sm-<*SsU+he=b(F{L%jP9VyTA@|J zDCH{ZT+`v>y{M}p$5k9ff3`6OXk6vU{J_lE&^Q$PMw%LkyGYetFOybvt%tPGHRTM) z{>CAAgbj|f3eBtyI2H)jxPN$bCnukMNc`d9_5AyA4nG@~+eNVf^SS2!WZl;MOK=+} zz2yR7bzhuraz$deQMX<5IQfF@+377J=SoYj?0B=wu%nqtdvNxaDtM0v@bgfLPVe`2 z|8#^c{U@~!rpgzJyY%=%;!vmiQf0Zr=>Wyz>$=85O<dgbw=)#>6p<(Ez1oOLii|=+ zTF6VGm3!uI`rf?wa&n1QvxkqfGYes47=bC0QckEj9?1H?tP3zl3|r)yKNXv{>=&wB zzP!?J)UduO{t8~-A7f9i03-(Rr=JqLJ<ihf?^)D(=EiaZYM?4^(I+*l>?0XJo#lO; zy0sy0XRULZdy%Jsrm9O%*nrio8lGCKwL;Le$0OJl-M%l$dP~7jxx~XTmxxnHcF(WR z!a%qlDFZ||bOC`kdQN)7I0=t!GBbAz!>FFEz_Xxjj&{1vs3L-Bz-=q6e2TI66ucY~ z!Vz~e?)*0AudM?njQPT&Nn<QkrCLXhvUq<4D?>_xMM5ef9;A02w4bWX!^wt%pHx}f zIZ)itvCQ^uIK&fk-ZiZZ1^sTsG&@TYsJr)Ht`(oz!g*7Lj;mW!aCHsSxI%OXQB17U zti~pSQR%A#q*nvq0K-h#b8P$dShU3P*iR+>`m~3P3AGCw_Zwms&qx#E@jiKS;Om5^ zM6-;(7jl_zN$&J*CkgDv-#AwRiMUcV$O>Y<fiuP?z*O=Lk}tCzRST0wnFCz8o+O!a z-J>jf9G@PtxLm+}jUB3%w|t7QsjdZr{R^7O)*I@bLkd;L0KEXg01w0r@WRO5E%`N$ z0AHcb+)z@mh<IW^Bm`z~s7;!QZePpBDdNGG!<MlOGuPW7#18&b(kMwGkF6WS7;yuI z1j?aYazRL9b{{}F$F~xLZtP<`QdhlHhKspAPht57jFf8Oq@-*IMDT-M#BfVf*S#%X z&+8jPdrqMqp!k~$v`I%#WCJV-%jzQG0PNTR_`|s^s7ZTmLo#dsh|(?;Sst2Amqy|E zX5!dRnI!^Y1X}W6gx$g#bOmQ`z6=(f?M`k(CbpIRyb@C1=luhJ9nkgh^)_4kWBfsn zoZjg@&dxfq+n{CVM$MpC0p=3<!G+E>c%$uqLg0i0DEe!9uSQCX(Go#rFlJzH3#I_` zLN(@y@!JBedL0cML1U^y;IPqOyd0)^B4H6`4ad;64^8E2ug3p^4w^w;u;K#lLPl`Z zKgX+eJ82T>z8Dw0Q{0MH5I;vSl{C*KpG_OiqS9)M1UQvS&|1nqb}h$J;sTsOWc|2Z zH#~0xP)l12GcQS*QCMMYk6Fp=me^jMT`2ZSUFKNf9paT>yLbNAS^ly$X6xNxdT0lG zS^qD$r=I#4+*?>Kp)3SIOR>rmk-y>n^N<(ZtAC3Himpl)%6WXFUh2*djSolZD5*rU zCWhbYEM#^Jeww-oh06pTIG&WA{$krKq)V|65bT;`K}37`S5p&pdiom-uX}f;)2#gL zQxlFoAZVf{Oo)Tn|Cj!PHyq_ho|S7A7_eb@XV*-SsZCO*ld>E8z|$Rkj_$O_b}uzu zfV^N~4X*&r$@;{j|J2;1Lp2-rN7R<AETjwnm&JZ=*%gm7R(gz{Zch0!^e?zz!)F#v zKoojEXcAG+X&p1`Fela@<v*B}MI7`}b+n3pKJFji%!>AjT2CKG!{bp$^0|QXN})4< zu9YtDF4W`Jv$g*DQ+NQWg(^zDjUW#Tg_%DNk%JN<A}t+Ht-eA!R5m|=k<_wDb=q`2 zg8Noxoi_5=ew@u2K66mj1ffQ3@__*nPywqL7;G1XY-F)vHqK6ypbd}YBBmvVrn{P# zJX|lz_B0I48rP~D90@BmPV@Q_6~ZnlD6P+u2Xd?XOAFozu@{l2dU#X_CSkAV)=l#b zh99GV8a}`L^Ks0EsNrm#HDv+fAg!3hWBvDX1tRvequ>|Z1Ukb?(z=+b<bm5c;7s8+ zAi<orF>O|pR#-cpcDRX<ybreSTA(uPzz=Fe?SWGNC&@tlD#5M2zmZ1QSUrj}Zx?I( z51m<K(TP~yWlI1>M^BrigE5t+cd>cn(fJ0p=K4d7<^$#7C*_#SA9r&T#}6eXelQSm zX=axMwNrrL416cZC;s(xg>b|&0CeOH>4F9n=819e;lmV?>hOcCr?09xxN=<Ay+}JK zoAeUYl+<WJpX(6=3&t-;`^X8Ag8RK<z%X9$+X0D3a>JuyT?Q{S#*B!AV3DEB^C73# zXYL~}idEuWN7@sgyDMlUcVyP~oZV+No$dCDb{E5yaAr$fOM#v1EnCbMAk#L_PT^g6 zn+e`%5*UZx<S65nR}WyTD3I&irz=c3z`&b_TtQw96l7<asvl!WqwBj79%{ObF&e0t z6@ec{j~ojTITbxCj)9ZsaOCy;y-6HMYpC3iN!GCVnUnR!(7w6l;QoEDgEraW`?~iC z6&u<H%C-dKPLg=F>crg+pq93Hqo6yGar{-!<DUF!h-=p`_mOnBx}myjOOR8dAwd4y zHwpY6IaU{Tj|1kwyg=4OOy`hmPwgBY*kT1{tQd^CkdYjq@vogY{~i;s%g$;ov<cwv zc7)=|GZ3)I2h%ac#B$z_ON0Ibgpo2%Dc2ruiHzw7e$#NhKoNiduqBHK$oRM`b#&SS zT`_RRZKVVO*2agFqc&en)@5j-qDgB3YXoIu-P`nnoUrs(T*cj7jI9UmG>7rBKucBc z!^}X#&^J&ggoiorgAD^^qFuDi#o3{Of4uh+`qb;biR1CL=X&e&>OQ27bp{cZ$<z%I z%nHtO9KWykAa+~?KnCh`I#?(j^o5R~Flf*Av;13Mt2FS+@Ncklh_fAE!5HYYb#fy% zgTFKVW;^xupB~f%F+<%_<S86S|9N>obyHKEXo&AJUws?I6Fj@cfPGlO9;s%FnyaM^ z#(J<YR;i}U)4ygO49`?F_}DT}O6=hX53idO_xfe`d%PzM&`ybqSUUe#R%P*_N1pMz z{V3kUPXsEa)M*$SikD49|NX^kW}2x^9!uds&wJWd{SK^^8<Mg~7XQgjzw{jc0BN<Q zt<rBrKXv{k(a#3H`m-beD&oR)CBdY{^=hqKQTF@Xd#5{ZnM05$3!6O;-Pp_>LoOkm zP<aGjV{9!S@Qeq;7L2?Th5h_13XT1@Iy@5l=2vxxWbGK_W!fgn{dB|CbJ>N6t+GXC z3yyex^;4wmAD0B#l5lmHl&uP$FzUtdwwpLtro?ZC4OZ%3gPFJxn>;RAbA=I{Ju13b zE<D-4FI@!+M5FD(E?gH0>!0-q{LY3eb|BBLku`<)0m<Qa4+R#;6Cp;G--#<Cl$;nN z))b5dR~B{NqalXc%4i^G=-E~VkVvU6GZEB00V|dqVLcl+sx14GK0jZ3I!?MninFam z^$<oUh-rQ@=TL^}OPw>WCVKjLV#Miec{qks;-QR%F}i08B*`6Q9BYJBT4>=MsdI{p zq!`oZfU#C1UpAl}us;U5qdR70(Q~BmnGKx;8Wkp|DL)8a92FV8Ryq3dQllD1M-=bC zxm|SH`I*!74j=b;h*y%%Y>ou@r4qGAI=5qrVm<6eqJbh9hsBo#<r>pssL>uyq?GU{ zlrEt5l=xKvnI+mFD7T!8P-MTj@^T~mnu}o60ad%mRu1lk0-C}hEs9Bu!LsR9)_^%2 z^G>qk`jsOTh;AhYjDJ@bBCaJ$CjzT1G6L{W1{w`zkvdv-_PpSXe8I$q`9Q?v$`n`# zJSM(gCEN9}Ci(9r_;o##>Y>#xeEGMO6Mc$ut;BT@q`rt!tKo}8GrTqMMAdb=sL-}T zKm~4K7$!!A)b+H$Mh6T>i}j9l4?E$KwJZMLQAIYhidkrZHK)cR5by(N^wOw-_(>qo z?IYFfL9t@3r{Zp-;Y&Rt%`7lnmKEb^h9q_ZOV>@^MsP*K6}^xLbuCN*7ef8OCegFp zMOLtV4*PA@Ga1~-laOvv%Zz+3!4F^=0D+`BV0Oo-rr~csTdX_k<_VFIQqDK{50s1r z`s*y(co1T05%Wvcnw@HM1IlZwpbJ^*vK+XUD+4kjLB=d@70vIiZ3P)ph?}ywXM1DX z(q0a4!tQ`a*G@{Wp}=AT$N1tT{fd@h|63yySJ6<Wzi*%LbU!mcilh#fL?dz=0PV^d z8Hgy?z_`fR(2bPYj72`}Q9-DHg96J33{!+TR{`ksHOejYe$(LvFlV2lK~14|KB!qh z+6fCq(g8?>_XGx7<Ia@%?j4aEg0YB#`~f_eGX>@^y_r8Jq;qwDt>TCscA^y>vUsfY zmpRK*hFi96E#z%3r4KPSHps)*09$ajMR^xZSy@yU0wRS{7tBQ(*e2@?D)(h?oCRux zc}Y4J<&V07ZZ{mtaxj_(2TLjtxZ@kF#(qaxPj{@1>GMu=-;X1c_tPtP<ot!2EAU;2 zrr}-UTZ}&C)N5Ugz4=<{8XUGi1lwm#Fnw%o>Ct48M!rFM4Z5X)ISc2RfFq><yxxaJ z&3LJriLYN%ANp<_#vDv4?va%RTYK-Q{CIKoLhuMDMKtx<knqUtSG4jGu6Qqw$E9@E z8j*<q{-5CkvIj8mtHv$#34*bY4{S6jasGz8&0ZfCZoO)7^l-E`z8ey+d;Z1}D0MEF zqehU#9!M1ND1n@kf4~-D1Z0uv(`J$F<oVX(vfj<doRUfOl7(VjyPK%9dx(EjjtJm| z7@hZJ^^{#<jxp{Vd|-~j$z_)@cH*eGI5`wD3hQ<Gr~Z}lxF=$EZUfV9cSf>G&3wu8 zX^D?+(%@UitH&U#kC_nLTzlg@shI?Yhs9_eMPO2#?8pUSXNeG61B{CB)@^=Vo7g)b z)AAXeq>UdyD~<G>lmrynFe`%0p}@NT<o$t(m}lWcPD@>9?IeBQJ@<hUi*Q3d5Bc(- z?;lSO6;GBuMjFH5B1-&C*kwouILz;ZIK=kWvFigon?*hu{E97#;^f)HOYj=dh5fc& zc&2GntRfJk;s`@c1n?Tp{E_}zdW_159|2z9j1nx>+jX@Giv4yd#J7Q+7S;IfQM;1z z7u6BGa__nW%p3@7rP=4}UWG@5yiVF<prS^dss0Gg-ds|i_A<_|;&<9~dx)H?8C?RD zt?(#5Ud(<%iA5ztBO-r3hI2iL8_p?_tWDgWP6oz!=2F$p0I@pUQl1GAQY#%ceX-&6 zvh2!KdBNjsur-jgk&4X-(BSUf5qVd9r|z}a4n%poOdSJ*Y~4B_w}Hkhy)^Mw5>{M= zmD4yD#kv863=ca}vGt(8aGU(yG})6xV2x3mvw1SL*Rh}<OqJ9DOEK$s4g!QGSJ}dg zCMJ7TCuVAhfOr}VCe{(`cIyJ#gx7#9-dytI3*1VvN_S>nr72kzI4U;5oM6wdAl)kp zw8Dz5xr^Rpm>-5xpRp+?lRE4MIS-J?vPWKsTfGNl@F>gj)MYGqy|nA?^<W@T)v5-8 zx;JS{oBR;5xX~TH!OnFke$cp!`-|{$HGC?AG7wW6fRNn-vnaU$2tP{@AwCax(_E`k z^&IYWIqbHbG%3;>#=guXu9CObG01uL#_Z7(<;AgGXX~kFA&Ir&+f=V_NAqb44ucDd zmeY4_6t)Py!T{n!o+atPK_?i(8KS|ngQud(Dc#D|0ulHfmRVC}E08evF0YCrmTs~| z`|I&EfVYL00hT^)kXh1KcN&3;L9@XMs9^|&Yr^L|V<~<t1=0DfWH9UqMJ_dIJd2Li zvgCmMal4Q0<CZ77#ZV_^OauQ)Tzru_FlRP_x)&6_UgB3%tt_k+l3ET&hl)xH0dNY> zmSeC2b<yWXU~!mR;BYajnEEJ4+&89{$i9dxfYdz*=eIG<N2k7g_duLQCekV%{z|ma zJP|j^uWKtSvb@>!r_U^m9#!+W+|l7XWSV9o@kxd<|4PL4A2^pc2-xdKTr4&yNeUh# zF_=Jc*5zHeIto783o*Ai=PA)05Qk@HEx%4}JqLnVzt#~^Adc)r+>Nme({;C9Du@pU z03K$PtYrJX_7?vAl0ji$Sz*v%`6+QR0_g`KMcz4yHjiL)v<M_a+rYN(N>rNFWu)$) z|H)DW6Za?FSl`P0o0SFxAq}jiHN}S3VpuUONw@qTTguakaYxH<j%alRCA#N+g2{Fg z1;@fRx_^xWbko6bpc+P9V5(5x0Ht<OfC397UVB}fI6jCeQ8|Rdm5<V`OacPzTYoW@ zKot?u`b)5(FZ8Apxdh*F$t=$?2DX*8Q--juR^<GyLTmN><+K}I)AZIU@$7^E!^+f# z-#QmTTZA<b|CoPd-u@k>hZgURWeGhK@XQ=gZe_#Xlw~$$W};JuzR?<4fpq6Spq-vs zQgCVfshS&0|FbvL6i7QEv56p@qlt3H-`!QMa1eji8b!`jT!2Nr4nI3~<$ixy)>n^C z^Ib55*BLaAsrq|(%jtlO&=kJ6m0AU<9U%^WKx9?BNqLIJAukt0>34@F4Jw{}#DtU+ z=g0aCs9R(Xw!#8(EbNB)b+iL808QyWdxI$UGx&6X5kck;Ccn4w7#E2Q^J%B<O?<^7 zN}jG*;$`;;r1_P&^WT1(K=Zi6aos|)MF+mL;uD*xtVq6I`b6nioG)DA0dm>H58>yq zbF`I&$imK?uGJuu1m8LFI_Obf0&|$Dem%*4Fxyt_v9+8&u!Qi$<+SHaz;lB*v6&yh zfw9DUubIimdZB(Abuo4TE=eQKnt84?jWA2ph;i5jQoVIhI94@PtPxu$d0MhaJRtXx z()a8{@Sz+spf!lH0OIw}L4#m$M<#zZp<Yj<MdwrPtHt?li!{5|ACO<HQOJB33&gyP z;hcUfpMErYEvtRvML5+a0P0dQ&Vr7i!AQ4J7DG`34bVy11Oi;!2<s*WR;Pg;(g3m) z&%>!Y^HE4dhA9@$)EngmP=7vHZe~#6{Wvzxp!|nFbaBm5ld{e}$ev#@=~)~BVOPvV zb9J4RSdUkVIsQX!oOqhon!54#u$bWiy}S@V2-pjw&ig-4XqCZofbK3ZWL8o*e0E{w zjeOTd<h|WuB=Br_YX5$}#HgL{Qozzf0Tp1Ka@@F*(jQ_MDinfKI7THEX#D{70J4-5 zsi0i&YtQFS(>vy19}!IIgq;-SRPbJ8@roV&@7*Nv%=bXJaK9omr%s&%o@_m)*Bfyv z*8UPQZgSPQ*}p_HAn*caBrE-%c{tWAf<;2ZdqR>L0t2#lU5WITyv}G7em+#h8|6=o zZX)vyVxoBQOwr_%Q_Dms`f%HvPs*3#3%_^BrE{Nxq2xsuF*?rY6U?~oZ5W65;!BXq zZ`Xn`8B8Z^k2`UYwR%m;RYUuGAhR_KL{n0BB+R8ponUkz0HZyVk_()i`4OQDy!KZ9 ziHN_Y1F6N&W`*3_q`q9|8jRrO=2G68F#nHCo)9}T*K&I}hB>a5yGVbs>AEt(`$Za` zyP|#=Fm})6vEC^MW~~ikR5~Xw=`;RtKDTSUrw$Fhj2@nPMxXvh6vFsABlA@;SM>y` zs(eO_C1iL%;1eF01fai=_oXUfOmyarBxmO5@!Z@?EcRlsDe#BesN;*DzSH%ys2J3J zC6D5kM=SPq=3TC-TyzQJtIbo`55mlz_=3>$0(Y|RGs<{Hd7NCoZ8UTR`S#~^A(7AP zVCGLCOH(!YOGV}?(v*$cB~L0sUrtvlsFQD?AZ_3-;Cmt8Et&Q!x6!bGu1mi@pg{l& zL;oyeG_JS*8Slv+->YvKwEm}zdXkC-MrCmuL+}`G#cZs@h86ylwBZ;&z+E~)`%F&p z0eJ7Pjc4+7G4(6b)-4wllDK!aXX*(Z2;#9HJ#-Q76T<~u&Q?K@{aXHF%B}h&7Iqsr z;}TRD0N!o-<W7!fVf{4B%OB>}j>ZA@J<PB0zTKK~(m>nJ8H9B#a=J!T{!0{&Ebdyq z_lpn%A)_7r$mwL`{F8h2PB4)NGIKMvlD&AK16fY;`AHen&*7N{*vL1Xe*b_YK%gB_ zrbZ>mR7#-j61mXCJuo?~s7=|@HzHB^ug3B?U@~<UhvUhooy65KLqSosJVr=0m}5@b zf;0{v3S}LfdCI7tA`R3|a$`wzS}@%t2wV=UQxK{p%JIUf@(=ctM1Z*Vb&S;eL9~2J zVdDDuTHKtwvJqqE_tP-iXWbCn3bA40E$&ra1WHhtEFh3vo|32%29$(et_QuSY|;%0 z@H>MG6GPSowCS+HblqE;YVGVmyZlg6406ZAoH4>(fxgU{rL!ONu_>54W_niZX@mvn z>rZ`fd%PR$FCLz<-5%OjZRHZXT$5t9?j`=3XY>o;wCh84dKZ&a<NOb)KCR8nsF3PM zMit#Yo{xNS2e~hV=<GSr4U;aLB)6I|K+pOlF&UExwBRBYPF18Ow0Zko)z5ZHTeQco zHGJB7Y|Hcy-Dg*E_(5*d=ucjnuUy9O8r`v7#$%=e?wHE7fT**=6n@6XSwh6h3Ws>8 zx|6Ass3#xWRG@F1DD3{1mU!cbXRR$Zt`ILValQJ78XThGqbyA$Iu7Y{WYs<zK$xtI z?S!;O&k>f+^;qdGm$nWLkV;hd(OXEY_v}-=eP|_>8}K|wbmM5ce7T|)6yd<JguARI zC2IL{kb0w0b3vbJlf_2PMAD7MQ@|xh)!%uBZ$#)J{M6)v@+ZvaqdTy0V&Y$g$p+0N zncO?fDVAzVZw;6bk_+tH9C4XUfSMZ0DcbG>-5k(a?3?bRkQIFgJ?OtRXMZxZMSg=a zq)E7`KIF5xs7Rx!0nPRyZAU)Ly4N94qMwniFC`};#^U$-Pf8bMx@jETo0V@55StQx zAg|*SJHpTRYnD^6kr=RGM9BC^^1X)Sa|f*;yL`UgEjAY~IMD85a*5QS0J=eAlaw!c zBHYh|*(T|ntqR)|ciSLI*VNI0ahZB+>iNOlqP~L~hc|Sl-*nzDjkC(LNzGODzMdNF zid2*MzDZCwvIFCP-&ze-%y}RJ?!%54X@cduKiw$&RA+lfC;PyOuTfKe!_(soG<V~U zGu>6gGw`7GxClY3Sj~@%0;=FSRqzL%nJcolEX<W19}v<|o_<^in2tw|KdpDkwPl*x z@)@$bmR@N-+TfGl98Okv6|2lKFzlgDQY7nkSTh|mvbDXiF|*&b<?baEv&p5HoLq;Y zVRM|Pe=63tzN|oG;#+4pG{j-X;?axuK^LvYkaUSbN>nfUp1>4=05lhnDV#umnNl5D zrsO(qA|3+=mmn`j2q{MwdVJ{M))WGJA(&!pEJ!mo@6wsPdUZ9T3+)dbN6rZi*JHMf z{d{bf>^M~<44Q~3wh>an<i3$h!}<I;sDTP$s}Bng`+6KNG@%}TaWO;E6hU+=KTz*` znK+U-sOWjMKBz0y0q*b9(NgYc@ux=1(7SyGKE&r}MKrbStaK4-?|vg<)uRn@8{1%8 zzfFXk)HTmaWdIwaRoZK71x#JSyIK17SQvOtuy0@0s1@=t3m0p|52K&>wqbVvl!X^% zTsW2GKSk5ya-R*aNnNf!=g02}Io;9BJDcsyWzy0|Hcb3_2PnO4>=_`(lf(3Zh2)nn z6#l`ELke+bm`iJ;SSVmwr=0nV8b1iEq=pbG?Doi6x5h*}+L<sQ^yaG&aCd(C90PUD z$ktGrN0Y5EE9zO?b|RlNKR0r`-DV?CRkU*fMWb9ko8s&I@GQS8Crb2jY!C=n*7?hY zQkJgvV4MQs3lKiLD&ID15I;XIldKY%Sj^E#@_s&Q&WjMFdJi?8vyhZ-@`-~DyE4i@ zf2+{L>!5;%@YB#)ySTv4+LH_Ijq9nrdz5ZTiXEF)*7C&Z%KhwQ;DTq|ulOQ0p^{d( ze6;JxJoqB*#fN%!e`&tYb%Q^}?EuO+BYO$2=3BeB4=6_V`#IKesUo*ZNHg&-OXMMO z>W!nBvW7ZmZD8J6fWIIoj_!DbmlWfmXmOn6BKWLy5rIzmrfav;2jMy&w2P6|_EOwb zq8Pc3KlRI*yKMl88(!!!x}ZCMs$2BjC{6TXPvlI~0_T)D#9sLmlv4Zhv~Mx}z0Ge% zJzU6$2+%w)-!5!mIXbS-;BnV8fEdUwTk@tCHxXFxONW2m3LGZy;oY`<iQaOsf(zqA zX)~3J`?1KJPS}3wqEw4?Ucdg}TkG`R*t+}3chAd5wVY)XE_KTzzil}K8H{2cWpe#P z7(wthWs?#VU?3kiS|7wZ><WF2>|s+Y+e4}C2dE}u0qD8wP~&Q#tX97f;X7*GDJ$Q% z>qiah_8?UU*PHC@OJ<V@oK!Ej2?}1w&LuHP-jn1d^_958;(`ZiaK#ruCjCiJZZRtE z07n&c`W@%Bm3RqM*O(T@hQ?czzZ^=j<vRTQ`%`3o%80!D&e+N!GgXI#)6S#=ETUDg z9dMBRuS(UjKF`C?jrnTy%9T*WJ@=*;5K5$n<i7F=O6%6}h12T1<z0lVJkW-6J{hx7 zuo7&aDGJz1rd=6mmLw4=*c}Kk`-MpaToo1Q<at$ZNp}_~t-P6*<PfPz{cDr{2MMTC z85K?2o7CT}Rf^^f)2BFsRk(m`zK30|3-Ef`_GYSMA;%xxt2K~W+1afO!v7X3>@%MT z=vZY>_{5>6(}z;Hp(mwE*+#NqkWma*cx22@TSY|{IJ>B!xXWJ^qn=ly9^$toK20U3 zWX#Ybx8RT?u8r>R&UdeG9XkLaDXZf;!!PkW<8ZgpmN@U0HG|aVlpPwEx+MY;0d%j5 zIZ1(aM_SRWMdD)<;%uAXDoUL<vui*9kL&K8{V#lKaz`qdw1*<V8JvuR(1PdUFc?;d zuK7?GXLjqWR)BzDE)K~`urx|GBu@TN(!QVe=dX-hAQhfQh43G#fX-~xgPPcY!fzcE zhgRO33*&+&=AJjDe8$kDaP}x<K<_SFStP<V;`^`Q{48id6Gtm(QbfZ2%@YSfhoAcl zCizV8<g%rff0hijYjxY&Hs83%84ey5P2p)pSi$S>^3Wxg3inaFxe2s3#$$Dhd$;Op zxIFbwzXjPGyeYBsmb$#cqOCzf{0J|(5fob%Vz<yPJxcOt994%;H>ze?fNTzWyL%v* zFm@3QwKhdA4!w^8`ZtA2k)GVTlI^vK-W|=oVw+`N0@Pk=Me8pfsh$LJJinLZy|=k8 zY}6}kOU{0E_i||&7Iu|B9$w$wN<sYJXD<9kK1O=042=`^))T=Ultt8R!)nb3z6DAc zbcN0<U@<Tt=T?FYDb6&xfMK6Vjl<8wIq6t;Mx@kydZ}*exzB%<N=oqfd@O~ally-= zY97C;EMoCZa(CHjz0zyeZZI;|$-L7DvH&LyfK_C`(Bxgnr<;b3o7cQf;JL)GaT@)b z4rfI)Me>{!5DcBP3yyZ)^5k*NeAwm6l2|ifzIoK8LOaAvMAWGYa235en+X#z8XGNV z1ftVBl~)uKiFYC4SuakZXfqPoUL9T`c{FA$W7t1cHSTz)W%4+H)oMzAsUhscO0T~- zsRyDKGd}V#M(3$5^NVHT6>9@>GD2r&zZ*WqMGEcLVeL7O0z)t)zQ3$qUh)AdX|iV0 zMWu0NmZ6B6SKYv&9n#T~6Kpb`&7IFU6qltVsZTub8aXBrjCSv1up`-s6ApZubA+Vk z-_GW)@GMOW6?_-ROV%_Wvf>1>NQsbP^QzQ-hxD+KticrFTN$|i^+D#Q!oTqUhpB%I z?<?55$Km7~+qN3pwrw?b(%9T#W7}?H+cq29YSJW)**rP-IsfPSy`5|3<=$&%eHQ+o zkmXM$?@r&+@b6EdZZFw?9H;3ph4`qgiPPYB|8vqrE`igd{a4)Eb_`DVKU2->6*xKk ze^>uKgV#g;pNt5PDW2mT8VpPg`#%}}i6S8U^nxOM1fW^R;6Lg8{-5dnUEzGWbfk(a zNK)zyx(j4F3()!<f{;N9@@`XM*hooHCnmxHk2mru=09yZkI3q2Zvt{$ujVbV<!WS9 z8Socqv@k<!m(}pZ&h_wZ+p6xZ4_D-L2_DkX{D`%%6%Fud!;lf!0)FWW>J`{G8r;5c zt@@850qPp&hibAE@^JZb71LvX3_e3{ovw>)>25}x3U8yTvFPn-`ek%Xm;Ra2*G$$K z5m+1&?%z4w@Gdb32!zc#@_gI7h0ZniII8M7kz1zM%8uze3KkXmUj0vp4VuiTE>%PS zWB4ClY*a8jI2dvAIa*M@^UgJo-3rF7N&5*U13+Cfpms9J$SygI-GuhTSiJJdYxV|u z8gnTQmJ_3jV>l<xv#Q4U-jV}92t)0(^qYhxM-~>c5Y2bfVZ`dP!eoe>3r^9TpEEPK z;+*n7A`&5IP@fbm8@<?2sIAz^u>?NjUa_NW*2<D>bneg&DeSJ?<o$l&sl{R68@h6~ zZvt6VZ@d#ID817K1@w02rxK>21U0%!LN%<v9(GlwYSO-nM=qPsTzGrWrtE7D8(YZJ za7Z>{$BaR(%~j`h$mA#8LdhCaKWaGh;bizbdk5(Hz5S`;>1}(vK40p*UD)5h_<cA% zz61veF~bq^)>i)=w%iNOA2!lKAjKE`dk7FHotg+mddBVrMcXyzFG0c=VgB^WTeN?B zH*3l(bM(tlU7g9F!S^eKc~b>gv_9F1MuC#GxE+SFU;<myq&^)-_OuJkeblySL*BM^ z;p|#Td7*w2BhBhhRF2m$u@^+La2HF>iJ(@j%)qjI!}9Nfg4ah&;HOY2F_p`LoRPq8 zpoEyN&gQMk-v?%+f`3OUbol#VHhMBQFBuA8CxbXJ>Ll=FJCsUBB*pp-L3$O#X{VO< zM>Y{&#PRgdMFtpC12y(-x?kvWi;-XrlmkpQ*h@<$#;!CtrUJGxzE@`=viIG2E<2T} zBD&ACsAOX9LuQ6S<3fhJutb6c68V5fuG&$Ql;Nl2U1R2?TEc{I6muu*;$o2RFFNJ$ z;TXyzb_O&Koa8N)>_4Q;`pv~XSGm<ti7E10+fXXL_WOU`h~MOG#yTqJF*W9#Y-6$z zA1x0>zp?AHg4N-;x9tBo1=FBp-U?=p%BqjjRNC1QH&Yaj&&DA8CjFYrhsh1hKq0qj z>%4`l^wqIz1f(|%5t46sN5GQT{rh7~h1z46hQ=6ad>r|uMZ0=YzaJO#_bKbO_=tQ( zFXBBixi;z_73X;`jRwQiRgHoqI$dE|1b1<)7>2ABszKsICiUMM^`=zPL!Q*<19b%R ztPap=gPk2KtTX&Rt|RMs-;!{FpL)xq2l4os!;~EyP$fm*U2<2)M|DkY7_Du>-rAZS zskrc{K%Xv*V3^$!U;OPgMI_X0)IIU59|pfy*7>amyTkGVUxbN;flLxA8>eal4?izA zx2JCXp*(shs54%7aHRpli^+rc@!>TNh4Gbx3_OlMZ4k##BbUlBQ?xU{`bCGZXGiS# zQ09famh;FfEApMUtY~AEklRmDYLL)N8ALul;>Nls#8bh%l)wM5e*Ymy9quDhTWo%z z?xo0z{fV)(Zl?qPYp~&EW*Y<xk;(z-TjSMll?M2brMf|bZ(XK|4XW^!jP>U~G587F zNjYhZC#d(d$VI?5!w~)e45cG*OS7{r^WEnGoJ~K8!g=PW2;$hHmMz&Ay)r=QEUyJO zHK)s5q}Z?=iJUNk<>?yrL)ggGp=olqITT@YTX5rpTeZ~!a14*&%4)mneXKNsR3O`T z{E7GPZ)QeH`G-Ld^XY>o(zT*UIE06&m=NDZ+@RQhuegd{U+V4w?~r-AcCBxD!<E(U zhfnY@TCrSdXoU4RsE-Zq=opa-3=j_qDO+D4+vFX+ALxQE$>27w9xVUp{6=YrFNDKj z{I(gFsFd$yE$h{2&;K|j`!#QWO8|Wttx@!Z5@my1HeS|{ACla6enIIs-_5hvZ}MMp ze7eFRyO#wDE{HdH0J7K|L*)6WNY?j#mRM-LF12srkB^ZCS?C|}aBYQSSrFBdirN?K z1>0N1jAhG6QyToV&m=*}0gn*&C<-eY^Gl@fk80IryFkcMa}qFs7K|e16B*4+HAU<} zc{i^di6G<N-q^EG;Tv6vG`8VqH}KTC8!Sn;^$R(!asGe+_?@gVstfC%jJgE$*ZWp# z{c+{D-0&{?(g=>)$kfgs_%aR*P+BuPb>W5R+9uCj=PO<pfypfiR`NmYU!UZuyLfxZ z8U{Ry=Sj^a{aps49G#=9mR1Ut&fY5RFsA{VNU2ShH&{b{Qxqrr@U;Y|&R?-Y3T+>t z|KThQ7%Z6me=IX-&>;5zl^MvugADNhi=X9`AXLQv>UFzWKtKPxXw3@h`!A^SwMRMZ z!-0W`lY)WK{RdR3(ha#l@PL8iX4~Jb-rxih@;&oX8($GeaGZOCqyg(>c0D7Mvd)Bd z^rItPdOGsd>GgZyYDy(VLe`F2`98!`66xgCH(KhCP8N2x4ME}$g-hEd+>uH-Jcp8s zyK!01FCJgh#+9t6uR{5oNJ=;R5C&Zx+I-br8^c^5pGeQe()LS4f`Lpr`9!prP)9cD z`=9@KEYqbp{M4f^vsD$RgswZ%mJFu;(F!#eP4In_<A>B1xxKpS;pc`4^|%_qfClhZ zz6eDsF-S0;Q@%4)eC3p#ZuAX+!AXLyo0Phcu<)7sz@5qKT*uda@D!@B9F>12y@~gO z2{!LiOPxBUsop*1RRHoOi$&3pS9&iFxHrqZi=htI17tf}&H62%vZ}o@BbYPG#_=z2 zEbj4Er+_VZ)sc&IxmM0R1mA|x!A#*bC(`q?H)$Sk`evKMA5;eqKd<N(2x~A&>#fo? z3+KK+jH|(zF(yz*QuCMlo|NabsNZ!Rc>093M|%#GOc!}ma|5xfOEyddo`3Q4b_S{u zb8em9)`^F&tCB9O4GH>PUj^NN%hSp*G*C`2B8O($xz3@n<%c*dY`2d!UdYQM=BpbX z%|NM1AS+<Qht+SY%jB3-t*lr0f2(yOJ{*7YZz8u9Mg6LA@$dk3x}l$xofwgm_%se@ z(nd3_)$F-pK?%^@L88shIdk@To`nTEI95OX`tB%eS!zB~Vkt$7o$|JaS|`S7U9X*` zDR!6@<{KpXGEwJCW^36J{I0)zHBTPlh#<!RrQTe2O4kE3RUoc4A%@!?k`6_5LLrp7 z{syfV$46G%>N1iR7_*)NqZUJa;6Bi}t%leoPX<f25eei9E<o3eB(%C7qTH0uX$@LV zD(+vH@s3G?(;8|0Dk;CGeOoAE*#8HYTCUA>u<bT3&>l?ZT-x*|Mm)v^Q_x84kf@$Y zM^Ucz?HOBC`r=|Xft0@f23Kb6gOk4%@##w70&HJUfBv5s6QbTmMOS+2TeoY1=yug2 z!~x3igeFi{!v1`X4q-jqS1>qFY6d8vPn%jDokLIIHez~_90gq$Mnp<24i@dJz(KS= z;i#E2+of((bug8}g{+89ZA)-lIWDUnnuyd&iWoynC|%pZO??+iVaOP^^fJ3M1U-;@ zxxEQ@BST-0dEy#B&x<cbpsyJaDY{eo&8I<fSpm!4kwjk(bD9=HgcyBE&b(w*kx4H< z>)!1ln?$iXq-1V}gl4QGNZ@FRYrbQ0PEDLv;CKh0>!pcX2|f+ezP1O?omiAVgksBx zI<swjS0nop;*Bi>T^LQ_5{&Y_WD(Y#3%+J)NQ{yV9a&n0BG(BHiil#rnC(ha|L7`9 z${lEf2}=lyeKO@sB~24@lA*XB|HmKcP4gkdPkH>Dv1I}E9T{l4yxQRU{;o(EF(}EK z_~!h3OjzZoOLDZr5jmosdd|De;SEu$QNilKYb&JWUEK=0kdXe%EY2^SCNd;00yAq- z9nyO!Y!c_dp1BPY@%_XEq3YFQFR@7>K1DzvaR$Q3mTkKUK#2S`^+)xkzhJ1mPL%J# z#ZGF)W~c8UYic7Ku25=Q@qWhAJdM7vzU%U-8?02Lk07c<@3g$4u_nWP8tqWo<zIB= zBw9<2>O$49TwKfq{I5*eFaO|se9mmE>WDYEc?=!d?Ugz$kL_$Jv7eU)AXBEY4c>rk zOzD+TcUIQ#AwviuD*w>0=kcRr3JC^B52tUb+~E0-LYNd#jOe=jXW;Aik!iolAgW_j zrD<}_oMwRgC?r~;ncsLPQ*MSPcL_y8GJr+cC4ErG8z4$E+w{?U%`JpT514zGV4MXJ zZBBUT2H3UCB`wAdaIxoh`G^kwyr>0=iUJb%yh4uq+r;(>lcKf#6YA_)A7h6QuF+>d zE5Tt0K20a9Q}RmNZ<yPYbiycvtf$78a?Rk{{3ZN`qCif*5qI@xKlCMGlywdFZ}b@$ zWW&bY!OrvOR-6@_el|C;{gRSnryn2IC6)Qc5?qSec;_~pN;Mi)<rLwxrYJzFK!_SF zuT{AY&<(sG@=5dm{vp51dh{Kqd*Pt~GyBecX?km_c6-#oT1&Ft?BbZO=5>UB;04yd z1%^*;qRL&Vt7nUIoG5)<z@u~EV*QE4`!ie&A#4ph@^2c&-Mj};dv4*7^YFokc@kwB zK^ok~3=?V#OKoO|k!L-d2{s_K&Lg_fnlfLw_~JzIqkH)$|KJyA@L9^nX|KD$kNg;+ z!M!FWf6na2ipT1%m+Ug9sTa&gXYT7qUWnU&YR;Uc{^lf@$g%yZQCmLNq)UGY1*zJp z4t~E8w8)$N=5}0;j=F0}F?r;j^b%SO<T_&}M620*b^Ai9WBH?xcV-@#`+4%Gj{D9& zg)V=P2R}(_Arm^Dbve1~Iq7l^nRQ8*!cKqVz7G&KGsO_9Im4;y{Y_rzX<>NzEZSHQ zSR5E8gAd;=bBX@t^0JO(N0N2HHdJX}4WTBkRj-!A0{mEVe0C+q7b-t&b%f#ut>bSE zvW#rV4Dv9>-4-`-7heouUbk=O_?v2kQ&!u;l$Ljhn0`KK14$7phHOnM?U{w)74I*1 z**)fr=%@lJl>;1$sDA?J8kI}0o{%I7=j+(*cn!=M%zm*FhgM8yQ%*}>?lE=VhDnxJ zX<w<oT=(oE4tHtW-^Z6c{h3$d>mu;9wQcx{FAD{31~M79es1Fcdq3x~UDN4>KE-A` z@|#buB^AlVNygfv&T}pD9E;`!%6RsUKMwPCJiE`hGjVrOKGqP-WAJ%^tli&svR9Sp zA>`qNl9&mN$TZu282kmTIo^FXSpR%G723AvJiW`@MV}^AK`7jiPK(q1xh)PxpEbid zIs%x~s&6*lU|8UQFSAHcbK!Bg+FyYf^oPn>q6E#By?lnBaIAS7f?P+ab?7a}eG)%; z57~+L><U?#boE1Ip>|XQA=ah*Tg*Z*1Wim7=>l@sW*12QJq<qg=KGS<P<-Xk74QJJ zN*ga?k{n<Q??6ypxTdPTpZx8UHsshvll7j2gTDX#FDdR7NcrXYY7IAMg!@%@<Zg{0 zGPLl-Qjk<vc`~R>GBuhm*#IqqF#s1W+(@Y$d-pfb4HV&fV0#{A1~ZR&N4|!mOD3KK zx*1Om+S0U{h+8n$AQeWGBvpuYZ8zq6gfi_i?3??avN<C97sO6~`o{bcrNpMHetT~6 z14ha!$%RK;fTHC9#)@`EP9|aOv*{I1OE!)YRigiuu}?Jg51GazQmtJgbu#*2xRU%g z0bFGAFGT6Le|S^AfXRDwwc||t?bkzrNZ;;_)(o1XN)w3cC_$E~*uTVibSPl+iW~=! zH95bEt+k7qPZs!-+Ph!=Wk~%1|9^QK(C2+JR0JgUUzR3)LK@`ue@?H-9Xo#M&u*;0 zpwBW#%vNI=koo^y+GDbyx&LuGIjW$f|5j=3q?b<*d~Sz^Oz(>U5u^h@@L^kxG(l7U zJH)IFiiCvy&+XNOk#!=yPanh&%9dz-*9WbE13hy)al{`pe*A3q;ZIj|03rE^G6TP~ zNOrzCC$Qnhwq5*VccZ97Ov!jZ%p?K{UUfW!t?n06ty~S0dER7B8rs+BhGaUpZ6*XP z7KS7N+|DXzEXZp@T2JCCz9ncJkqldmDpVzm#pqqXra4!^De$aFcxe!qh%_pqgrWv7 zaCQ5<@I@bc<wml(Y&Ll}oN7#Jw+U(tLNo51tPSi`SjnhcK&63JLidf5sX(|Ei_0R+ z$SA)HIl%MD33~F>8B8srXrNT~FrIu+aFK1EW)V5KJb04oE<&JbL3ay`y(v|rkMRjM zP(!pOWep3tOIOa?x<IoDGNjKo)S5y9C@6T81N+%Dxhp`c5%sR7nsy&MRs19n<Zm{x z_cj`2i^nS#4E%B=4i{^(9y)^e;I^k^(WVgP)3PHaM&Jv02uTO{r2b8vgK_W-&;=$_ zQbdKSpw~@>O#ZlB7ESRZh7n=uCkiR;au+!oF4eFHk^j`Ay}=vMB>jH$r7g(?m}UW2 z{MDE${z5j~r^aJEOm!KhY9TWbCF5mWj^IQG7o1@y74}i8LQ^ZU`qj`aoyYU2?<+TF z8RsrZA0rGFO;*kWj+>iB`VSn)RobucB4a^`QF2$!*lsxLI>T})Z9nxs3$e8oqz^OQ zY1vTm*^A(I{s2DM{$pwGiL&lEz-+N6@OpP~%cFq$-TF0_++)w-_WFe7xt^&4)<Xky zX@@tTMxj466#GCuBb3y;Di*72FgnH4{x#4BW@95*3u-G*N-iy$hv!NBx}oN<p6I&= zrUt!`V#~|`%V*?tZQ$#94ndWEf;q@zZl((iO2~4Hh8{~7($r~Sn5J<TI04uD)HfHD zHB!N2G~%I)v#6IlU`5<CIEeIUb;E`{)TnMP8FXIiVSh>DfWT@2C!GmW*JPvBi!73* zu)d^4u#AQQF~8_EXhJ=f7s0d;v1ZxCvYqt9$>v7+)cKI^qMLlQvs)_xxz(6-)6rFb zxY0fbvIA2Id{(341tb?h0P*cIdLKUu>8%?|lEtr$U~WW3TyM!WENgPdG^n|dNAfcI z1c6PWl-MeWpbDJ=fpxnnT(v{;r<&30iy7TJ6?@axOzdH^5Oj8A#$6E4y!cMQ%DD)8 zR44CH@*=4+`qyv4u6g?cnW0bzv0SVD$U6~g<9qAgn`4AJYEvqJdTCYpv7qYZBV12% zbszFhplDq{e{>WUicjS}$Q)Gq6Z*FPS2u*4f-3l;ZV)j;HhqvBCgo+(U}AS6o7*F= z^1=rW9Z>-pV=Xl$eibWgvkOQ}Qr{iJInVpIh-3wsScQJ^Esg8oH9rm;+1EUWbr?gv zrIia@_f)7Ezac|FXq2?ij2>CPFN$eZEDe2=?Q7NdBb4+4qVRQdm{z3D-vPI~;_ifB z*>y$oNp%qqnq8pqVS}`io4VLt_dOV3UW|G2M;EhJZJ_0=WGdBg!Dh4w_VNvn27A*a z&vvYMAXk}&0?0Zt$)EmBw_-{|swR$6q9VAB(4yPSV}eNo;mTrj!bRW<D2DzyUZjs9 zB$O93uQkw*R$G{y-Y8EP(t|6hZ6J1J6XqJX^7pu|%el@HRqy?pc_a-CA7XDsA38l7 z!q+AdvPQ^f9zD`4hA1f$hOqi3X|$WJF?-eXEI|t|#JwyU-kjj)Md+@URQZ1Jv7MNL z*Zu{yCH5TP8pa9zH-X1Ioch2w&96-Q2ff8?wtbBd{r$VK({YKp7F(&j&cDNQDZDfb zmFKsL?BK@Qic>ve?!CPA3xs7_3Yfr2kmkUl?HuMRs~BN8ZUl!^pdo!qI7O{t?F@O8 zzO};)&68y9s;L?fiTZMQK?}!ExehAmk%Mc2Mp|tEEZZ|0zw2F5+{b~lGpa)@#N&D4 z^@5f7EI+CBaBhO-P#uCixvzLXPm7>ZPzfc5qxZ3`5X`hmcS+!@R~p9qqKK_$&v|eG z;)@_7DBCxD4wjpAd`T9QyTcRZxSmLH4PZRFx6AMT_;77uA=yi>T;eI*-1<8c$T?ub z{uCV8q}G|TEpVS_!k@os@76&C(|@r}gl@%2W1-qzR1f~MBRHa-;G31Jq)M2$7E#PG zO_+HB6(6{(RgrZU1U)x)F1FXOYWBnHNH{-tAJpG(qvB$>%DVdA44$8n(Iz>`x?LXX z2ATNP*OG(#j>T*{w|<>$%FDZv*tf5bvXC6eW>)~?3l)Gg!R|sBY~&%Hn^Z^$giF;V zzL$uZVsO52`jB^XpV+6y^K<bfaH1>?mic1rFU+e)s!SUFCvONHYO3K|kxs47tzQ`h zr(@ZXs&3(fysmqRUW=#o%T?6?<Pnl8>hDQO?SbvK-!}vW6<t{L3X0_V0ni^ycuzF| z7j{^87&RkxDN}+Y4a?HZ*QfFM!2}3##DqE|B2ld=7%5;SW%d-$zU(!VHFkuCZdYX6 zKQV}Gq8YC5B)uUimd3f=;Jb|l(q0Za`OQ;5gc75W$oCftnr_QR$AMbcVh&AhJ|A`c z)gR_cO^ItlzM95Pu;f(mTIU|@2Ztd*KQ<<Al#(N5ICPTL{UOm8>BfN!N<^bcI6*=> z$}O~B6pde-uNJ5PJ!?K~7RW>Y3dbsj@(Ta?D3J9w#Q+|owUks+9E8~iM>R0y{|kYG zC~+X+i&&t@D{do6-&%jRV;)qvk<g3nfP$_IL7R_rxfF+~#0l8|WUOLk$?!0M%pxem z{ClVFehX*R>f29^XjNF;pOZymhJ{Mwt6d3SBM~&y4d~S|(UU3>3T1X|G3xZE@}Ln4 z=iK`?Qzf`Cr2eAd#0=PrkzPY(ah>Z`t|Q|2H`uSBT*s3)^RW?vjWsW*%J>I8W$X=K zJuPW^c1QD?@c(#Ij;$z#NDMRuhJ=U~mKm?V#zr~{aG$Y{rtvvat_8xEX59Aio|8ox zN_-CFwD;9qX5QI{$A$OB3DAC&)17OT(&_~XX({317yZyg;YI-{7QB!MW!n^Y$z&v@ z(H-c0<e^COYq$$$=pN^#G_+aVDZ*|@GBVPuIRkEw`!BFuGn7y|m5+{q(rL7_2X^k= zrTQ8o5pVd{Nf%p3-;0V<@S*g}{_n`#K|h{mr%a$6mn=`sjp7mcedEqtE-WxwVhPk^ zx|vTSA|rc>BU|p#wrQ#!NR><AHy0;a6{xfnRG&h`stigyAcQfy_yhzCAi}NTqITRw zI1b1#J#oGnB0zij_N#^hL1>$>0tmk=x{OEyM1<=`QxU20?AK{e*qST8Ov3fe?wkg| z)RynG!)%YQjIqJ|uC~zX^Sr6KzB`!5iFa=yj1TC8JN31eHxUYslS4t&gNe~1)l+(s zInWdUUbkAi4z<3T{@M&l3IU+reIbd3{W$bq0%5$1uRG%;<);P!`abL!&d1!d%^dC( zH|YW%)_A4kOr<5O>J8?<q?rehgni|@)5K)+q2Zx|jIc!2lzYCMq$OGr#@Ke6fsz|{ z67n&2Y+-&*^UcI%6<ndB4<MZPjxp!h6xQD%YN4ZA2Df@nbpC4dN`C$l54K@nTAbJe z{pieeLX_*Jv$;wEWSV3P9n`Xrq<n&Kl-KqR)~}hr8!Uu7PFH^7X>p`)MZtB|$%>SC z#L>v*U(wuyr=&-G<5w_O6RDlemw-!D?H=KTzbj{**TRNyn%QF#m7<}DQlRB?qLXtT z4>3j}T32e?(9_@L#cCt0EV_9dB_zc6FP-b+4jbZ;Jwapzq$AW;9QmhF7Ja(C-v>gZ zL~}2HZe6P7zp|YKQb3ofko!nQF9r=+M@mTwbHd4EBKq>k^{}6mti_I0aaOJ6%w@$J zG@{j!k6u7Xu6p~cr#6qh$8=!ybeA&3sE#!jZXYTVvH!Fnfp;YGO4%>)Z(oI+sZTY{ zd_<v#vZ`(afFNo%C~VxjIhIy%%D#{Rm*h6DVys^$Tc3b=#C}b}TV9&RBxcsj@@DkZ z21oct+SINu+&>?8qC*+y>kcFg>EtC1#%=_MKzjQrI>(YP+65r~#^05!^Fdt>iU<w^ z#kuq~O-qIQjOl}lq6z#*xccUbxK{T<U71rt!IptQ1D$2kXVMB(G7F-#uOc*+O&-|t zv@Ywlrkq(~(x%C}t7Mtz)V@I^=$89e?Sg>{Erhy0DdwVk=y>Q0-q(A`+^ykdjdvx; zFULbUJKo_@opFg?NL;1DIcpF1gJF@0BtjpXIgA;Of*GCQj`!pd;Uy2@J2s@*n17xb zQ)+;_><Hp*Jlg)79LT`K_lHg;nGZiPDnxA#wVk#G)nef>_oqUB^+o%beMahix2M>} zceLv(3Xs54i!kBBIthnhHJ6otI;VtTISJ3|!A*Iw%j-KH&XzH>aknlGcoFzIHwBfA zSZDo7@U63<^c>*=b^e!mILdmvzM@K+p*dj2%zop3o5Gb_X<%`JC!Q-2#B!Ip7MWwd z>r<TR7<1Bh<?ckxwr^bEfgE08yg5HqSAc7}qEg`aaI}XrN@&<8Lstii9;9Uu=oxBU zgBYA+pH<vk;Tp&H^LLU7u@itQM2FS5Ku}r7LG3)3`+;@ZvtI;OTzAI3B!|QoaR6|7 zMLfO!j?dZL^bI`Ny8#z+vBF)A+%}Rx{f6XbPC+wKLdc#PQUHnPDOoWz+^q7YDq0}N zlEd2A$9q4K<ZKx>38j{3+|Osc#@40y)Y=X9kI5lREkn3Q500`i<{`usWikbOmBkAW z;@P!txbB*jGJ1JjPf}cT+$#%pEfqj=fMNJJdnqqm8|@a)9Cd>YN?RVcSubLzQ!x8b zyA5r97sB0CkeD%nuZ1|GgckUe^v2P3;TRx?`w&KYMfOfd#FJ26<!n=Ds0(u3k-Z;E zIF6iX*&{dATUDHB0W&Sd?2nP$Xj;?FE6yksQ=^LS3BJ`;hbfoX;s~8xRX}!%!KO)n zI9G9JEK|Un+ih**aExrh{JX-v19$GhH_q`#T=NO%X_)gx4_M;oiflQ)f_sv0QLhXf zpdHiiKUs3*B>u3B>5J+tyiHay#=ieixLiJZGmE`iw)ryc)cfFD#X^;&gA#7Pu*Zw4 zYqq7uOsHZ`TBniiM^WK6#0-EjZ}zl3e}(uLTeUN)x*>X^t=k*WQ{Vi~zlkP(f@e9= zW{h6;M@qhp5;1E7#BQ$2<H==o1%p^>kb%;&dvMBo<0*t&5{D$@SucE&0jm&^`~!TO z4^{#6_eVMF^D{Pwc=U}`>n1t{p6FgBIb3SIbn(w8I9YXTcNrT}CL&;moZ*Mc2gYNY zZIZ;Hb6m$zZ}5+);tv$UKqIT8X9gsW2z@TwS=Ya26;k2hbpK43aa}`2IanA+Nq&rM z5APw<QtAYpU6CrX*(l%?W+Gha*PnGEkVo|hBZ6%XlVsg*DN*8{!WPMMUK6w=Pn+KR zy6D1&bP9)FdQJH99v}eM7vTr>C_k59N+{#guI^Xyw37(UxXg;k_4&!-5R|s84)=!i z-;y4}c-uUia^x{ww2lQ_*$U*>{w-&`2Slu5B>!u&F2ZScJ^7~QP5_zC=n<0D0pi(Q zWc9<;qm#|~UhP^B`JK)d{|;hV^{J1Q{*Y<<b)x`|FRo)|USkB{(HH$;YK;DxMB8x~ z8t#vJZ=hOU?76Jj7A6WoDw&#Uz%=g&0o2udN-v|DZZ1yud7VZlEOHJrwatuACUr#R z_l&9|7T4pUo3n)|wgN9a-z#_5N|?nMpk9o<_l>gnr^-%<mi>YlIea-gyn<poxiaK4 zJzU~lGj9Wa>@@&+@q6E>)n`Lmg4}2Xq0gYD{0PuM%=*#-#))TMwgg(2%dzzx`3~=i zlC?Qg&FcZ?(Krd7n`iYDU(Bu59`>t!52ib@%=k^-F0a3ZKghZ3n&cDbf1l3gZxkE8 zchP`^^%I<GeZ0#z9vqizwmyM0u|C}^y^&rT^GihOSVf@ntEZjwXCyGVETBJ8BOQ%L z$D}jh-u7W{g!{nm_Vu|IMnUNjO4o4&jRNt>(ipP{rF^`?er``s@*jIVAm{Q@C<S*M zzpsYe*{_t-fA}%m;VRjnp3}$rxfcI91mQLzwaqg1Nr`2Mo}=xgDK>GlwBtrcJLJ&& zV&J@x_h|=aXNO`GPD*4v&$sIR(eZ~a8_c0aaI&I)!Np86px(pBpj0_DW$BRN{|%U2 zM$LFCpBpuHUuj+J;-4=$Uthk&umRdH4Y|Hxj-Mf2_7>IzTJ}dq=fc5MBlMVw+~B=S zWKjpYyOgj$PGqH(-k7s5dUzKbdfJ7d*N%JYAY?whV=(+)xV^q!miYgy@mrJ(Y1Y>Q z9E68%2??0wNAIvT>wUH3Xm_AJz5r01e_8wPx=H}MVI`fysokND<xbRIT{+!=Df2qU zh=*$^whb~PD_eri$j2HH$J-jLC%#{IOn(FC3+2ft*p9H9ArO?X{ii_hO}(zHUNA;K zdk_^tqf+_lAF`hlaYyE}oIv={1jVR_eD?#w3z4RyW!{R)C&S^eNBE})5kOA+bbD?% zt_(0!`{sz5am&;r88~Wi)d^Zmu4+K)rR($8jw#P(xNvXn=sIroyY>~ou+ws|t#TJQ zwaaoxj{p4+mb4?ws~l;vH#WA!)2)qrE%N5Q(?%n468fAADTz_L1YHEjHNZEfttR^G z-!tS~XO(BF5orxiUUo+bW8lf&U_n=Agc{rri4pU5P?`By+Qg|k7`4X_9?4{C4JK~) zjFHVE2xiP+SbDr;9~lExZs7{Aw<D0r`4^+UPna?Vm5~UPndBC7^6o4tqXg1iQ(J`6 zPQ#y_>R%Jzbg}T3;?yh!RiLm3%u?+`UK8)H^a`;H&P>V9mf(*4#euRB*RIxx>t_k* zNoKu}OlzyGqx!Bn5pls{jl!K1*R@C~Fdmx+{k2KkP)_2@JAF<w1|*t_ZyGTSESNp7 z#iK4ML`CT{VEx*59f!Vpot!6jU7+wg*wcbWW2(@Tx?R-T)#fh|ZCq0#a2eEgNGP>I z^>?fvs_0!Ku&KnVh=81NLi`h=s?m&zHh+I;QGjN=|1VA6ZmTg<X$5~OZYf(_hF`}= zm95802fZ+E2idENq@83T{mjSQtt!|TUv)bMjLS#ly;2~lTaAH6P$r|arQT8nr~55e zo@iQ@=vdmgJ3&sqFY)^`?~J3Ro1sMMBPy?We%-HnElmn5SU^!tokt;$$(aD7V#nL3 z8BGq{c(nQ5%gBoO#@@vXq+|W>i@*5N8rDMA08N+I7PXUN2LAl3ISp)VtW)+N^(#JU zk`pd+lB!7}XRhS37hX?;d!wE1!MM;RTCTDx9HX4Lq?>kint4^w@GdgnxTUIx`%fKO zbPRlJEn$_V3m|sr5q2pG0jln>yRFKt{HmfiQ_6@Z*X4p*`P7uU((Sjz+ab1GN+4+f z|ABnZ<;(+fbyh&(cfdQli0I9Air2lVxJHuKBVkCp!(@7p1**i6ea4+TH%%V@54n&D z&fnc@g`akL9j)qrCl0hxdS85&wb2<9(@mJqJSZi&L4cThM^z-sUGrR6=C@P!^(gX@ zBJOeQ$+-bj^-J@%F2#c0GheK;Oa)=zdlEc8N{RdA0JbY*cE6xbWm@!vzyKlK$di;A z&NA$lS=p{@do2IZp5QH-1dFZd7B;&Zls6{0Ye{sNxSn9hpjva)t_0ErFDoJr=+t@z zsU0QzR)F84H`ufAl~f>vC2*R<E+=Bf;Vj1s`%Tl~G&{K!Po#Mz>u36(vNA*P#^dJN zoCdeK1Rq;JNTe|Y*WbslYWAqeorm;asXDC|YP-Ql+FUY&ceaY&g=TfnG0caIU8k)c zq?lbVHq8}f;id-bX2%EO+h#$3Z*v(G406cwB;}m|#49)-O|uaG>bEG?McT13)G}Sq zK+H)pJ8a8a3hC*)%MP*`5h|h)n&rXeVy~+Yu>Y6s|EIUi56~?_eab*^|4*g)$?L<U z+yBIeYlZLtZT~mkeCIIjQSOt=7Y6;`7L~G3Dj&fQ^!0yKzMVfP4f?<85~xs+!v6wW zHN!y$|2=+)P1^e(^=2B@e?|e6!2iE+q`w^EOKV(R3;i6PYlpi`c)(h4i?uLid7n(v zv=%m}n$mvr`<3ryndhX<3Q`6bHJi6*SNe(JDJiUB*#O<on_qCWH7jx!rseEZ=6O*A zS@gMd7nG4MN-0t)c2qSbOP1#nWQ=W7N!Zlwn>1ZxYE?(6=mb=NYi@0kEGlI3_^+(k zh=Ze+O+BnpF-Lea`JBXTwY2p^2e$I%s++~gakkWJLrUi4tRwI8s;(&ws>V4<OrvrE zPA)q08^fB@IklRv)7Nq}4isBye0J9P4(@5eCQ?6mu^RbJU0`${hfN0}`)&n#lQNZd z5@M`UVaakMl<jH&)~Ge}bxy^TI_`Ac5(jk6I&OUB7^isW23u7*6Q$La=0G3W(&TZ< zaZ1qCz;Tt({)0RW3ZazV?4sP4&Nw^M${~<RG<qZL^_ISJJk-L*_4t``$$DzT=>6;8 zwGCHKH|MMIyNERF$=XeSwp^|Yld;Zp%#?%MyNl`_V4xBht~shW8j}~)p=Hx;k&ivR zJ@v`;7l>E;GeMEdJbkBM#%G76J%65zqx|9Jy}$Bf`%Z%q+%)%`El&Jr!;!+`Y6gP# zdN~gtc5n9AX#!aZtR)F2^^nr<`*B_tLsC7`6BkiLNk99Hw96!zJ&QK6<f=HSsDEma zyHY|^I_IPUQu}LhC9I3^X1q4i2Q7&f4RJ5)T*r6mW^v*%i&@;><U$SYb<zKtxl%n= zIs%CSI&b_YGrjNklTD*5G#nUXyyt=2Ybv?A=Z6^>z&x*mkscMa1`BIdY0Kz~#H&J@ z8gXS$q~}nrU1}Fj)}#rwYlb0({jCz=&@UaHUg|;sr*J~M>PF|39C-`8RTKtu*+0lX z7Vk`rpHojx3`@4t#cIb$L2&(r?BQ7jC&9_6E@|d4`W?w9tv|cRS7_03XI1%-{+AFd z_OHor;f~ju(2@%>kj0=7t4&=AEJX*ovfzC^D_skU=YYsO(;77O!YWRWfV}u<eP|N= zbKzXz#LRqQEn=L8J951sow$x)1fsd(cM7LeT}xPi%i^y1Lqkm6*FkN9_K-Q@R{RIY zXiTcl=!xu_1ms<!1Tl@%zgJFl_#U+V3_p#`&*6>1O0fGwq~`~X0VDoa&FEK@0KccU zUf$doYM#0tm=$Y_=@!_vRbzb3e@G!b8IijH4K6K~y$cB@bdsuWi1|OoBV3LNcy10< zXZt;c(-LRl&w8*nac_?8B4l+@GHvq?#1Ly^EWP#Wi&fz%g{`ZHK~bj+|Jtg7r(TUq zjrMp4j*tXuWDUqS1I$jmvz|Vy5g(Oq{kciEcpQBv?EWVAH5#-~G^}UyudqXnxDCDl zqPsA}MOed8jm?Q7&R)O_QkR2+eZ2%P(Opi5)P1&~APZo3OXlCYm9O?{hWM9j@3*|k zJtt8ScTm&JXcLTdjLe13_bDCw($olgy7{A;>Xl6<uZ(Y%lIJ=4sG|}@IrVV?es{n1 z6k<-!x@~Z`hE=%iN-xVVbw9d0SSzJ~r-y}v5B!!BB(q*r$a^Q6K)$1I$`@<z(MtC1 zNJNmMsdQ$-cm?GnikHp166!RwENx$#$`DSGM@_TyZgOvz<bLJOm@J12M<lP4XUvm+ zm4J9xVf=w;!$cnLs>Hf_E6R8}HRzx}X8{sbT2kzq_vXaKSlg_O{@P)DwAU;S*cLO= zOOGSa1}v7g|6=gvS~^*P^((*qy}#N=u4}i+mL+Oe>MU<J{oq44!;RBS<_%4;6uXIu z2X+|F&7l@KdjO7M6v=PPX;8?BSyF7l-=)dpC*|n<Xa9<ydX^P-Qt^nETv(77OIX0` z5M@v&;aX%(3EqXuT?w6Rd$oxjcv*0b-3VO8&56VB8VZDH*3G_56hPqpt7ob2KER&f zr8KzT*4X_;XkI|}N`jo^r5R&;a3yF0uf2m-e40f(-MF^guXC@5fNh3{)a<E}N^{|5 z@)zdYB$%kF6Sgu?-ERWZCBMt~C4D${vJK}>4M3UfLQRKOSl2PQJpK%3hr3d>o<IPs z-Vpcgj@Pe3#?Yo5CqtIoL;Rkky#vP^^RP3k=$q}xSX3|zauSi3e^yp2&fP(v8GMP= zl#YcWTI?=j)m$*)S$;f`a8I#`h6&HQY>jl=295Aig><p;*(VdJw9~T0h}f;B(c6R- zZ?KlgC7O6>$Bu`DeyleE(NdIms-$+@n(sMc*3dyx63;Ue0T)Iet3NsB2IEbq;y;uh zw8rI1yJvT}KM;4Aky{aG%zIglzlC0UV%bXNB31#fcKj2itZO>K{YV<4{Gy>oXOFap zu#iuBYtGrTi64YC6b>aOrB|6Uof@5<-!0*dHvd8Yf3OSvU(sgzWCG~he^HY^5d^~i zzajfETT36(r)8oA^Al+a0W%XbYYQeP?>{>B@tZAZ|9X0Zbs)+YPu--lUk0(6@JW6j z2NtoK!aO_iz@|v#o+itr9&Y;ty*&wtt2uW1Ivc*QB2c!{&mdamWqMm4{_%L74oA7t z8Lre~SwqP^4RhI)t-kr=(YeMU^;kud)xANWib+`<Yxb3vXz0Qc1IV}G4`}?eJ<fEh z)p0IyMCEFzRU8B$P$7D*$EZz2D%T_*(V*tvef<lAR%&}0n%s?RF|xC_NCZ<(JknUZ z#A@bPvVZLLYCq!Oweq(CgSAK$+50c0Mz!K)$j|eb%NsbjhQEf0gi2A|9YQ^OTAOow z%&f-Dx?c31Ck(iMdx7q{nd9ynNp2f}`T}fIOG&=Z6!Xd@c=Z@5X1&<GC|^w-IK)cQ zX$zn^<uQ{&B+3(XEKT>Ah*3#XGmvjw`(``OVuPT@;Ab8gDfH*dm72#rik9uxA1*`^ z3KhC2LD<D`_GG`2mc~^3k?kAginjfH>dYVXe*$m|tR;yK5df1IF0Q}YilwfPM%Bii z|33V$!NPnu>Ow?xD}})mDuQW`_0Y}<cQgbnNglE9Uua+esry0AY3O2E>Jbc=L7tmm z3Z^g)<83M$n(jh|7ax4Ch<S4-G?Ln{M*Hei(4#&6AIwpfX}CN!>)$Gnfi>Ulh;p^0 zXsnD&tyceO8ep<&$eD;jpsB$&k;l>`mUsF=i{-C`iTVX(*a0-HsSJCEdN#%R^L)0r zS)-c7B4x1IdR<6j9B7)`@K~HahV(TD4-=cyj<I~S_&tP}OoAQ^$t6euR_qb~A!e7e zeXm+KE=_7!ERO#w2N286Wu?brLWQM|+GV1rCF%H!1FXR&avyC3QB}K!Y#A5{%dA{Z zcKv$4H_kq{C^nS0OI$hc{dZK!VRZpxge~=HGHHIJ%{Xv}jrOcsKxqg@6u5Y%_-tN* z>y`hr?!~?j>7mi-gl#mwSwt`9A13k-*@AR+Oqm?>=jhOc-qYA=Km0oEyMBRmIJgq} zY+0HrAMh&la)5?;EKBOxaGs&HmDu1ovi%ht1^uWhQtLhWs`phHTVCdhL=UGJ9w}6e zd=HhW4N`O0e(9xvZ`v*tmS=}(XKNo#qAUkiIYo}S;`49d%Vv$@7wFwYy%cjgX4^-I z&)BY{%noEe4cTz?U)p1+-0mUw?3=d@W(p>JN^x%eT`<_D{eD1T$`we9@j$*6If|q1 z=7A1r3{)w#+!VRE$KrIY1i;ajrnV5YLv^{-+-T;0E&DcnV<*p9HKR;&q?R`pG)dyf z7mzujcuJa;GMt6Ri7U>hFhJgHo(IBWW@-+uE}jt{mS?wb{~nFX)uk$%r{Kg+=-7xU z0>;7ChqDY-P@v+_)Gi+ZC-sfbQreuv(u)#)ABQk};8z1Q^|J>U?r+=G^xP`!7N0#m zq6P8%v<>WXUw`kSV=>K)95|?KCa<NyFNTfCZi$c(o}Im0KQH7xa00fK8|vAcrr|o@ ziqq(HL-Oi{eM8P-c);Rmgrg+=Q>4QOfRDc=N`|O*QUS;c-^Kn`KM(Qu1+DEzLMFQI zBtvRN(y0D}HU4gjd95quwLQIXI9P}W+rTDQ+uUouk)w1%9!j8*J020*yZME_dGXbQ zoYf%dhwB`IHC*i3Gx*TkTZ+-SudX0$vj?c#V!~d|tX-F$fyv&*bjduiaR9?=4`|wn zHGLV&n>E7rS~A|#Tyj{)zq%Yzyb&s%zWE1236yYf+X+d0<N3m`v18)!gv)|Hzl-=` zveVFn=1W*VafcfD`Hb9o=2-_AHEu7tj!<^W>5~#Nr+FK!UQN-}{m@pR1Z0!*h<c4% z;SU+NIOac_dGw%(M)jL5M-W;>04$SUke!E;p-D8WDw0vHHSXG*LcE7HrjLt<2uYu5 zeMa7$M^re}nZe?7aQ9-ZVK;ev0d#CW+=Z*?{E2kK<BdgK_~VZd^kckatCw`r-hK^# zp1+}9l3XS~;V>fcY)Hn3p&iX%P}^3BUzI9GT~P}W$||r60)GpBqY>>Q0d_44gqWjY ztJ9V7g@sa-eoavyY+B8~=lEY4p&2yhcgIZde@&^!*jhQzA5r7vS}!@9d`}^ri42Oc z!fAUKdxSwZbd@(Qk9)L;MY4loga0wE=A$J0?_TEjZ8qb`H>$4btvk6_0)N=r<JE|8 zirn)Xvj!Fvu=Kb>zU0(<Z(z=Xqxpdxlw=}>ROa1T-2JJP=`Ihajqu1h{Rlqj^-5qQ zk4f}q#%4OW*Hu{4S3KJBI;t%+!YoDW4v4~h0qR&$PVd)M=Njcz<BtB0zOc5wUg-;u zOebI`oF%pSRp}k6DY22f-8TK@MPw#*+lFup=ugEE)9t+T=ITejVKofpy__`WY!-)l z29)<rcEFR3539M-cW^cj{}GmW@@>6A{(q?YKPSXTHOLR;|Dg01$)auh)824El&<3S znM_W441!G$tiVSCQgw~g4|vdl6ZNt#l<W&$rq$6+%X1!Rhq7#fAGkkSY}Hca>yIQt z$X^+JNiY6*_Fkra8;^uQMA;pBH`?mG#8$=h-<{=uxPQT*?~Tst?(KZVl$;-zRZZ7V z`ysi})GVX^rBzdxJI2m{t1GdTi<;VIA~o0(p(Q5aZ`ye?(1!J4A7;~Iy`VKlenVra z%A}ZFG<D}p5h}N#C;6x}DTn{b#X!H(Y8F}VUy3uXWRIX*m8xB1;U`>cE<3-=TdUsw zsN$#Fiufrtt|`6Sv*t3rGN8(7s*j}>8Fx&v6ZNLF3uAayvm0By@tI`JU*X!_Xe2xd z{wVDIPg83Qz+!*gC9E<}E!hd1ndB??dgxmrWwIeCS8ya=DNV!EzR@kgzu-BnvNu+2 zV07K%Wp)tSHVEjFXWj;$pRVq1?^h->w&2x81EF82o{a2jlB%t}W(Y3-aP1QjDl=*` zj&NFjplvj4zX!o!H8xvsW*&jhXBabPjWz_x#IQgEGJMbZ=J#s>x9AI`_*cxcI|kbU zak|rF;J=Eh)FE_N@SjH7(=4;KghkNv%%cbf4aa)NkJP`=HTm!fn_R1dL$Dkd8B=)k z_FvE9J>j!Io#y4J-}Ze5OuYx73RZ&4oob03S&!)Aa%t7{nAB`Et;4pYId#OiH8Qu7 zBw?5Wy&E!vdMdIGqL_Wm%elgKa$2^{W?|M1NV1~BvOc0DvV?F0RVQj@4<4h$jgHke z8FLn$+bpn*^*o&-cXFgS8Jm`<ouNZtai*FID!GGJg27Sighu|#!-%l;QSd2Jql-d+ z2u)ZHqJ|WhJAehLOhFE8OZ!c<naqa8(*Jt{hHCmw^{)M-OhhgQ_&EI6)~>C|TtXZi z<({JNoJj{^|DrB9_JELUTmvDXZ08vXhR}O@drUM9xTyM+ez|TO=!Lqoc{8C;W#cO3 z7Wwrd%8<7&TQGu}PoNI{VWg%C(J++J`{IU@QUbvTMha8zJOjVyyqQ-L4c^Rq9d4Ha z>&F{j?MlS<tR#JiRIhI9>GRvNcWW^wxwOx7RugI3sbj1rUI3^ktcx=q3rGu5kYW`k zARygZh49^!@j^(VDU4Zyx6xar_B<W`%Rxo!&t>|il8VNean5qQbT_?`<rA0|J9O<# z;{Q8c0wOT!hfLCs<bTF7D#P@j<WdM=orceo#!JjZxk6TxECI@mFU9U5<w{uZ+dcg9 zG_Qp!6uHhon4{!hbJ71BlJ?zB1`?bm#^_5L>2$$K-Ei=`j}M;{gRJ67@cXG84oSE2 zHXppi>17@V)g3;VNsuUMCHlz_TSpx)>#GRm{JUFxDw`AQuOVvR6fk?fgV+opl<u)~ z?~T<8-qsrK$L2wdYp=AJ`nW2-Zk=_rU1%h%T8U*LKiuLOCEsiv>om<3Vw|i>E_v2V z@gJ1);eXgj@Cfu4fz$Inj%!lpD6}!msCYj2D1B}4aega5S!ZxDNhY|*^c^uuUKOdn zDT7z6u25@#nv7IB<@nrC0%<D%?Ri%FPzmW~|NR6sl~bLj>hGuJM0BIK#OjBQ{iZgx zr4YWgHkxU+1rwb1BJwI)6vjt$WxRwK#%AFnEj#yyJ4=7ch@QH&^JqnwYQ1&(`}~8h zd|?U45C|L8;_~pjXN<0|P)>Ay!u)AfsFnDI?(JEZtHjU{R=~;M=)-yiJc1B#m$bZM zAYmo;MoCvR>Ja$1uKz5%qrLHlUkucMEhJ&`g;a&Skjt)`G?hzyL4eyuF7CgW;`Y%| z$*IA9DK$;!)?UP_!gk88r~fO1wzn?5nca58f5Ay4SRsj*^aY-#L;!B<BLDVAwKD01 z=*|9l0g?ozo|R_i`^x9{e}`(_5C1)BoP)T}tWQ--<dml);%!6;j{!E=zBDgE<)O7u zG&f0bYi4F)6+RCs^Fg)5m)F^YKd<o&!Nz>ydnl6H+2{n@)OOr!I0E{7pM{eyrW8lY zAV-*JfIFPO9CZI}-Dih!8<<A$pqM(o?7<_8zX_PXxu1pH5?2v50Ppg`t|KxiP!6L6 zB|hw_3~%ci85<>qZKXZ?j=-PWIP3O}<$+)QOBqhrLAC9VgbmWDOvSYd!sI8<>yxXX zz((Fq>vR09iWOc8;`DpHs_~M9vlZS>Na1d!$@wg7Tl<y9nDk*2sX5kFShso<mym|u z+30t(w)^^-hc_=MVCI`9l+QpSX=u|2n%aR5I1q;`Dk+UoiWFg|>CxPXTgsRP84d^{ z8#O0u#rZBZ?m#&{E~wzYyFyT4A6pZbbrJpeqKRtjd#?ISiiGe~pEkV#{}ctm5KDyA z4I6G9^a#Ns`A9B}gYgE3c=xRsmm}drLcBxp(r8GXYt%OvNL&`cbZk%PIq&y<l<S5D z8_sR*>CMwCy32Upc{Fq0hauPxL3M+%XnlvE;cexwtP6{br`NP}=(nKH(=7;!)U>UU zx}g=oU^o$dJlo;8ci_psEK%|PPz)MaB8D3;q=RV}U4~RYMNmikg<9jWHFE#A4$8?g z-AqRuraf#P&_R5Jw^rWP!^Qfx5pq)nMTtG5#35&f0C|~cDM;jeS%3jLcN39HCv-3v z=vqx?!(YEV39sRglfnHOOxXD%BbAwp>?;L>8SJneTsiAZaysxQ5JxT`#}CRwC?~Wz z6fE6C8+fhcl-zQ6gzfg#bJlU*YT>k+lgTbM=*`9lxH(%fUUNG-*@-HpeSP;}FLFnn zj*3K=zhHDhQ9wtYR)JL)s>*6<br!U}NTx4$!NxLNK({|7OMGy?Vo-h@9sL`+blBdS z0yoG??8NFA3qelgOd!IWt_bg7x_-s0S#~d%p}r_!FB19%-(d&+)i@-zx6{rEFk4B* z=5Y%HG<&h`Ut+1PwViz2D!Bw+A#O9)n^Mu`$%h2rkghu}z0jD>{(oG(Q*>rgw=J6F ziz-gVwr$&1#kOsJaZ<7ERBSsHJE_>VZQkttpSJHlXFjcmwPsrnV-CzQdhhhugah|a z($RS5e4UHd4eHM>izo)!kSdsqgqP6r+x~!=k(rK{6Eds&E|R~Y*-t;PU^GRlcgQk# z(VBt~JO$x4FA%Qa8O=nnvg`=ZfQ3nnVaZBAm#Q;9A%U%0tTNn`%I*YV?j$>~K!(81 zjb>2Tu>}70{s<#g+9UJcg_uEyb?KDr@L!Am2j@2UaYQM=$Q6Jvms?VPleYt`_bdlw z&^pnxB9Ae1=#yc|@q{19!>2Y9$;vnOZ@IrXTzPwPq-r*^FT`Dnoso&;U(nCni$jZ> zDEB#cT_7TS^A%E~KZ{CLK%43XB*>%mSAmIc@K;+VxE&X43CU<1=6jI+t}*MZ$eSA; zk4m->y5c)dBn|?zIJPa~UgTz>p*z>&fXLzgHsnLxA|gM@r#t?Pg&|a2?^hG~#!)<$ zNv9=b5H-TSkudID;uqlm$q}=M!BjE-bMx}J1qKcI-|6C9rz`9z7zhYC)PG61qPBde zi~Ija?1rZofS+1hfsLLTjRp7*pteQ!1T6UfU>@eL!HQu1dyl`*HSSt?ARvSG|4BK& zzkyAyti*<q+0gZj#Tici0`iN61QI%>&P801F3a{zj&TzcCD4wLcw>YLVT|t9R++hX zRB(}Kon3WbmHyWgk0z}f$he5x#?@<Ap%;1LXlCl`8yEqwgM!{2247XxS!Ge(Xyg2; z{4!*dEzF8UP1?n3OQ#|_?%K3Ly}(x5LvOKQI410Kx(qCIg<x$jq~~Pu!*gyIyk3hp zF<H%gt$HQ$AOVLVwP3wPZV6U%Nlrm##@zg{!?#WSqfT|zZ>DwY9^p~x#=u&mR!e0` zxO&lT0HX_dx?j%BVrRmzlsu0;)yL|f?-;4}*io*tQewYNBH&N9ZJBn(@O3shcd&P) zK=Ac3c=k6tz1mcpwnnwfvb`Yr?CBPU8?{xKV?}FGPPf`r^|LaiX}zMB2DjjJJ3L|` zzmi=>Re#M+e-}gH?5y6}>dzo-ar&uKmk3P9@26W}msY4kir`yO{ztuv0G@OnHJtBR zc*qI(eT#(6Uyq-S<I#oum8v*&)?Vb>M?WUIE;BEmt1EJ3y1doTL?Zt!n90LmkE<7L zj{9}pZFN=z5I5Q05pVC6=q|auKhP}5klsXk!Qb_P8OFZvH=WvNTz>394rx$yb!(pW z3Y)b7>5?DBeHrJ~#_9add9S8h{vD6&9Cc}Ck~GXy^Y*v3Du2v2M~tN6Rj-sv1DdW` zQ^Tt3_xMj2J8reQN?Rs>`>AUFZQ;}FyQ0Y0JWp~e1V$1#MO*W48_liHtCHaT<z>pf z3p4Uoy*=7v&InIGcsV%8Y^m>7whypOzJ!_p1`an41hjGB4?y3X|H^}BEf<k#ssG6M zJL+nsrASHpgS13k>kXmw!cyU<_vqGvIOy;GuS#(8g8Rot-UMl?6xF?X<m=J#!&7Pj z42~z8y7e*`_IPN+Yh2Z!)g->t$(L=S9NOElk^Ea5o;ds(<J`HDHV~}EJd~F`5A)?^ zplY7AlHVzQ3(uyh{;6v_!G_vxt7ynnW)E9H_$uGUShUe$k3#mU<Z?dmJ<q059*V+c zqxu`V=$z8$Pkx>d_RR~$!dYmyQT^#xr>^RyOV%BA#+bEbNZo0AX1pa;%|=~kh(mn_ z_1VM^nHAkYO+KEAaOZ(O=NT8NA<MQbV0L!4?YtVT5^_@%F?&35M#kLR32}d?Ph-5l zj%WcgJBYXWU@zPFN@uM?8P4J}hS36RXwq4BV9-7!&VtpO4f)iUZ=UhXIynN#24p<U zfaJ1RHLG1&$((<GM?*+<-xUjXAMR-Vy5N^Zr$(6|zzUHSu{}u(MB`^orq&HP@cO9u z-2Em1;~tphf`6a$LLW4V8ajPrSBaG&jwdm~p>V=Nph$*oX4VQlZIDfCHV-aO=ZrlH zT1VK$ItZj637!;NsDuX!4!5<cZIsWLBDSJ}2V?f-5+Ky}0m|d?uYVu1Zy~X<*%;MW z1O@_+LP+vVdR+kE^HNY8^#V;m>rB{bVc3co50__CbXxZvzka!|u;I%48V^Y6peO&; zn8&e%>G^Q?6zJR&?VGJe;$?igC^WLMr+V8Ss}$E2hE$cRa$Bcw4DYoRa!kZql=co{ zE7FG2eRL}rY}E>#KN<h@?n0!x0<v_4aOd(Yz!zM$nFDcp_t=E8X95xE<-!BaVgrH- znt~kG9KQOnabwhz-hV*f$)oiBj`gZzfH}w8<<pi#Kcg9%zOUS|1E*NzF9Uv2yOjrD znO}cGK`CyPaQ2;%4f39UXeGcEb%LtoO)dKs(}f%5pk(Q44Uk*Hh?_U0F(S%uvsRcv zFnjA<n!;PSHng52gjRuF5v3x70ti59Qkg&joWLj1BCj|Q_Y6XO<@-1ESXa&~UcALe zmbZp|(`tCrc?YS1vUlCy60#anYO4&hQ*zcYV;9ET85a|#2LG-2TTQ>e$%P6|&SMh> zE?>o7{pNYPwygQw^}5KWHI{<rIY7LtpZ&9NOWTF^pZX1U6FG(q^@5_BR_Fij-q*L& zoPm*7O%}~-ZO;J$U;g@~KNY?i1|NsHa!OBP3&FlBKBR3GxeQX<Ks?YqA%LLDyOd_c zs3>!}onJ;SSO((u{w{+2@ZaMc(SxVZjl3D3%jiMB?5bD^Wqm+>ZZ9v!@6ZJM;prt8 zLwlcT<_Ii4FltW%<C{+ZxjG*&nvsBX_5dRRBxtgV8VN-|Jt-`CiK0f_emrBbaWR3k z55f#y9~0Q8PXH+9t}Jc>Nf6m6MG=3WH|`VJ;_;wPL}U0X9Bj(HjmWQ8`hnAMNN9l? zM`Y?2s!ljgTL7oKppB%S0lyG~WMi_jX#)Vb8lIQWleHYz0N0*ntCDpGw>IZlGNLeS zb8#DyX(&^1aD)JK!sn~x=fj(&R)_0Ed$)ttwN#u`C;&hV6eLv+08j#5O3eiT)JaFG z^#Vm?cJjKAJz|gv)gbS!SyPr=<qi4BYmuj<K*1c+YQ|Hs!2ztGv8f8+07_JK<50%x zUr7(m>GC6}>yp{dSeIw1k>CI-WZc}W5+A4BDcMTW%rfCl)uYrw@Ncuo-4F4&yS=#j zdSAgXTII0NIx8epsR41e{Hu7s^kMfk_+_;#6HgyLMJ2@49dfyt?nluVl|Gz{^mib) zPSBM1^>+^y0YnisePCk<5EIgm!De7d;LvA5EUPR7%1OaQTgw!?<}$DMGEh@L@Iw7W zB)3Z{cb}2`K~dewSEmnXm3fMdf#x1`h3E|t`&K>B)Y_dch-N?xkyhYvZrYf0kkvPh zeP8_Y9YoTgv0JyC<!olnkrS!y+8a-bLW)#x*I!B_LE;qT);vY~J)IEhL{Z7VS{Z{M zI=aI&xm}{YdYX=gPPGk?Odjpq>!J4G{QU_Tcuv7_gk8L2qr!lYgoYsW#Q&np1tpnA zTcG|>c|!~-+6mc&$he~3Sdp-_hl{f6yujKz#G<f7I(=mn{<d$D9AsJIh0#XrcsAcj z=spDOO$ULRf;verCP#2hFhhqTLP_NFl(-#(DJ?*}p^hHc#q}-UV5G<EPSuHHU4oBY zwMiM4SjPtALdF95&4<h?!^h#_a47)}+RP+ST$^Hf%zciz930}Nl;b5(`xmF-!(ieC zyWiBa?plx}FON?S>k>Wn93TN~@POrvhuGRz3M_^h`PTL4OowxItF0t~{vi|+#5io( zcfICcQeg6goER9vw-Lz6c|B5FNoSutV|qOTTm2IRz#l-0hBRSWHyE^Ga8$@pO7>t` zvR(m{)e)1_JxjV6_&HS>eDuR?d5{b_@`fHiz$ufZ(EUENR-7fi476Di8&Sw%`#L28 zv){4hj%0!XnxG*sOJ<;pA^-c%wIH%r#GhnEsU5&sse_(7olvRKdo$DpOedoDKv5-H zNFmbiAq^l>t#wOFIfMlhyvl@13pfU6CWIi%EmI;FLFQPI_7QJ#VNOOwU^*YdbF43v z4pkAZNOrsFp*qf*8-lapIVrbGjDynrjaCYp7-I29^?a`)Zg#C?4za=6sXwj0cw!oM z;3=ofk1#~jNg7QK2gb%WP$UL6s%o#QVn>;uQ7f8A2~fs^juoxm^Y)UZ9)E<c`UVe* z8(apBGo`5BY7A;A0*QTg3$O0SZJf|^7Ur)fc+YuJ{%Wt&cip_Z|9m|ErYt4BKDn(@ zk=~;Iy-)Y%6>Aw<9#8QSld1>}paZ2!^@9dbgGQ#7K?C^Fa-Xu5N39AxF^o#<ohuN9 zSRpm+_fy}Y0SZ7KjkhDI`NN#&gj<^ehNo*pOq6|pHng}Ym4+`tXD2~zSsy$vA{vNl z7&>&S>AWUQ83#R7|Fz8QbBN-0s9rZ#j(7-?H4!4$a<fg2>|3jItsf!jKv{GnJ!ofb zj_GD-$TWTt&3D2l+oD4^G~V6XxcP8o$hWO7U#zHEsWFDk8CG)QkkzX2b22VHk!4`K zp-w?9k;)ccX=f0NI{6?0%Okggr2!E(OJVlq;8E(!4*)F~KU69O3;-V!<jj$ic7=I; zJ&Jma5?L6ETX_5gWPDvLRTl<82E-p*&NodhK-x_hM0nu0aB1RqIi^OwNtY&jd&=M) zkkkM#h5Loc$s^%uJ4M?Dvi+ubdAQRbHgetQm4(3}22o7eW)nzz5Uj&CLG@;UYT~;G zG3|+3rh^&WYex70(y9=HWJvYGDmHp$eLXN$lQCq9cys+kn=zzxvtp%x2OI$$>$VLS zl4_6)s#Wj2%OxauCf-Jk#7%l1%mLPMV)v#q!zH>5M|ar96-A)c)d}P#TR7r~_Ybx( zl?)2n;uI_j?PjoDoQ>H>mL=4TD*RD=y6_8<!F59*EjrCuwJWepjp25jH$7!d0hk<C zt|J2n6pw*DTtqZl0tZ=bK)={djQ(Y*V8Y)cxiJM{6?{eQz&E2JsqWTkB}LhrQJF*G zZVrX5aDk7Tt>BIt!4$(OxVAyX*@&Mh@PiJ)RI9_Q;79BT%Tc~@!Mc>#{nuIi<J}5K zKhFn@u}lV4_FDZDoTn6OPfyyub}C^dzp&^DIe)dAg8PVri}QNM04ev^*?Q1+9F9$G z#n$}cc1MT!`|lgldz7h+G=k;HU2&(YnE{wA;i}9@I_wK&c8u<<tnwhKg-~_yQEw)Z zBF;YO9*}VVc6V0<nPq7i4<km^D>gNJFv9r6>DjGL-!lGTehdT{`xC{H3Zk5oLgnpL z3goth^Wo)7*{TFU;rsbL+~3Y5N~Fe4<Xgk(Njc!~mesj+p8x(GcI0QBngs{o0K%HA zYR&98fObD4SGY_Fv8~RZtQazfnT}l#j}Iy^*o+`6p%RTVioKJr&JdXOd3;G8Elpp8 zHRtsla_>s4?~H|C|A;wTg(%#OjoyTcBk9qS+FR1s12JmNU&D=JBX;FWqv7BVcV)I} zCn2e((-4PAWb`K;*zmqPS*){M1WKZ-<o!gTfv09Z-hY_0&*_G+v(*Ow#)t7<LEppM zsmPOzVYrD$#KYr+{hAQIU*G7>S1R5<0sq$@oQrE7{_Z4hQ#%u>l&d|N%;88<OkI`w z(zN&+g>17sw_fPzxF_2_SyaNHF4<s|@=7ekohI46C(9g%BGj%0PbwZW5EzKUChbCA zBD6RTGBD6{>Nku=KXBo4k{;1|!|zhlDpM?PT$(i;kh;`DP9xqzQz9ig$4=}xR>%H8 zzdK~OeXmkQkvW-vfLkt}v+|tFwGsZLEcct~UF*1+`t4*Ne`Lyh<{(nYHI_~kOYbVH zzHvvloAZFb@*e9iuUDWLIq<2eq~ipH8Z4BE@5i3OK32_nRxu4mbADolR}_bvFI8cM zp_gog+!}THerU{VpRU7~UAV;GSV&Cq>HviOxoC7{e+?d=pdwBs+~|m3gYYS69`?aI ze-^NR>s@R7uI8ug45RG*dp%?9^Wd*m{pz;Ss4=u*Fr*r)1RW)jsR+czhqhoQd)f`) z%Yt&g@o(J&U83M+G`j%pngHqk0Wx6UL80;t<EghovjRi5V<3Eur=`N|&}zmQ4*_LV znguFCx2#vek9i3tOpLjy*G~9==&-4Sw;D)p%R}27Ud*UuAtmpH*%6@9<QFj1!oixR zpVxQBfb9ExiUQ=FcLT0)j9(C$2qdN)R^Vez?XfoDfuD%BcJud+#-D>DfL0TxOui>{ zH$?w6*B?|d4atNZw=r_inZ7$2sU7ro6_q{5G)1?KtcWRFPtLz~3SEmT;Vsh6c_tP( zOn)1IOq$s3y;v@Ddl@~RS=XHA61}+k-D0)ja$fppA?%k}1TxU2o?8+j!mt9-1sNrn z&8ta-m-ak`Ub?_2XKEIF+TXj3`!@$`Ge1gcy=z^s5!cASnLfFod^YO4U^Y_YmGLp5 zu|CtWM7ClyjF{MIeqRE5j`W~QaEDj0sR(94)DHz_%ObRqysWv9zC@z__jx(vOFpCK ztLZ)mU(FRB`dZ+T!tyTPjRgy#kFs`-$fqAlV~~lg`g89SXPX*G%^$Ig-EAAlXiffJ z<2jj3CGCs@xvl9gOd2pHpOhquyxSCqjJ`hj(I<JXIx(1kB5E-k%{n{zed|H|l{Y*I zL^ud9szdv8XxZa~vGED(kpn(ZhLtFM#<VxTHEko|hq8g8e}acMa}=J<LvHMXQ^!2I z<=cgA7>h4oT$okGFA?}Y*Ol2E6dYFk+SrC#m6z%tU7woW1|ev|VCYft5Y-FZ@&hB^ zQAoi16NU-L4o>yuJ?NhIBHo`3Zn;*DZJfq&x^&_{m(dQa|3UGLamZY?4TQbXQB;jc z=3RrrX5auRbP!{y^tv%YP4a|e+c-cqYz3I<OXAu~!*f_ng0n&~5ZAN58GK0kiF+r+ zL=#1V$g0UW!}(i`Hgq!6**TCQPd8&%T@F+=$Hy>zvV!}5>nsWLf^z24?P+DSP2h`% z;%<sCVXU5abcF9r(ODd+TkQB4VQ=wAhSBwUQk4OVw<xgs;4_#6e|GEvY}wV`OM5YB z>P5_I)M@3DE)VFr{?s3)^Svd&{mmW+F@u{2F(rShH%7tVjq8<k??dW`N%U#b?4;H4 zt0i3_K_FX@ZZzH+=hg|To4vMR*A>=muUv#CdBv^Zcg=a;qvKjs!fdjI#hfw4K4C+D zyA1>0uIikM()XXWz1^DoAtv^uavI)1uh|<Ts|?c|WqjR5G1v<j2io5)Us*-7h&6WF zs_3A%owlOGEZ1$}rt5*d!3N;s^kKZNqO$FuoNPq?(YEr<-9E_XBLtx)t%?l|=l+K1 zOZF);Ax`gqIf|Uxm<{o_kH4Sfxi4v^r27FwntxF8(-%ybfwDLIBu|fF9v~MW%NVe! zG9oq3co?E3<0LD!J#s@oJ2=?vIhm>=-3lN>R0taG@BiR>i;40|SaD|X!_t%$dWo2f z^S)jEY&j<6UmmZ?@p0WrjNDJ$Jq0HwWIpfxy=?7;5337AM;_I25PaI|57{CMe~$qQ zO(Y)OSI}Vk(mv!Kysr<QU^#0hn6y#MZ>Y+rt=6hah*xcB3E|O>m*K-5m^+^eg-Pn? zSP7;!_8<M?voOZ*;XZh@U=pBv6TiuJR>QR9oaa<;Of=D8M8RqiM1<>`c9*J#5rq=^ z5$E|VKatPfT*7y5AzfI{H}POhdmRE4(PLkWAC1gH8~N3~g%ayUGMQu@7&PG2*o(X? zXT?wW(=GJy7y7q6>|uZsdCSkOBtuG37aCP0-^c*od|%k^s1_$9ozzJL#>^d)G+AGZ z-T-=^3(o<zHbWICW8=iQ{5!Ow^S%%SK17O0fa3_w3D3r5_kAX-Haim&j%6h9LxW$N zX%uk?s^6Xp`9O8UrB^O&S_c2b{=H-R?O*}5A3+4Pc6D6OJ>N3=kcntaNF!&;Qud{8 z3a10;82KBPxwGWd$~TXTtg`$ZDeW6FOnC+x5drm`r_g5*`QFK2(%ve?(zk~@5VtZb z|3ztXY{B`W63PJYbwFqPIy3>g#ODi<x5^Bn20gJHDcJIO$~F<m{xOt%CGmM4{LMFl zRqP@WY{0Vva~oS*sSo|$<^87YBii@wBn=bFO$h!0xW9;Vw@Pz@$oZ^oxB@!4Lk)Ai zrTcbCA1>Iy-dRzu#V4QwaP!b8sH?H5@7)FkDitM{p_*=jgq-YseXM|&B$kCsHH=Nu zY+)66Yjqd-<bKF1Rm#@(by;0=s0D4LZm6Ad(5YtLr1*aAhhmR(Lx)X5b&^vFO<f6B z$$3$M5G=D@^0duYhh{}wnwA02?1^WfdRfkn9;!EasOK&z%Nl0wGJ&ZlHxLU3tbgk{ z6t@kTi7}!o!i%MF87G0TeBL*?H930IIPv#`{bA~+e|ajez4rPam&d|vSNLh0_$fcG z8~%O1hcSG*=cSUJm-I3)khXx*5O4JTMf5uDXixP==Xo$x(W9EMG(#F~c~(bU6$wE| z)y6EttgHfL)e>3xH)|)OQ63S2Go8>B@NZu9DdRwP^amv4KMDXH1u;R>K)~PMV}V*e z0}OuY!PKDkH2JsShl7^fnLqnGO{Sy9UfQh>oa_F8$Xy6Ezt^rq{*tV4cS_&h-~OPM zYrSoLl7}iZAJZ>Pb&BGT)z{%&9Q!-!u}tinDe~E#U7>f2K&w|b%2ku=6}Cw^kQiUb zG|Z-NlfB&<*YgQXKuuYdyBnEkQ&i(ZbaMKmQbg%TzlKTge7h!*ym<YWKN_$*%`U>b z>{bnTrk*#NC>B_Q(^)f*ue$=t`4=ao(2;bK><7!TNx_K`!{1}&*J6x*FdmtxwKFJ{ zplq16@KL{wfh(TBzklMb;K;A=bn7|M5uICa&9)?(_7#u-529N-izejbkYc_lWdjD% z%@B<Ebqsy7PAV1(T6ouIIoh*6*}4fS-h^37K;#2*fa?c|KE>TrdISVA;~jsYh{&y9 zY53^>5%0Cg82O;_fqpAO@XqW!XQ+7F3iwyGkltT8#Yf1W&fy~7Z#hdC`5-i0B-14C zP>S*$2HZgdA2UeB`vVQc%$>Z!s_ab9X-ebb>z)^eT0G1*j>+z>i`4u1!`uKci#G;= z?)9mU0za&vfO5g$v&qlTHyU4Lzt<H8K+LE_K0I;IC0Db008-<~vfEnGdtmQUZZeMO zr;2u|;tTNly{Il@#4r_#xRIY`(ZZ8ksTT=%9KCPA8-A%JOl!YO(<21w;jU57Hts2@ zf<6w=+P|os7g#mf+u4V`8ojR~tt<-fsiVavM*z;4*R(9OkIf9MSywXRj_lf?=)QL* zTO3imOXXC8X|P7p0svE2jd>n@5d7o|m^8O)ydLOZ=eBv>rZY=_QJiP)0WQcXE=Y%~ zvtnz@Asun(3vt%Gws-l+;m(x5n^s%z^z^<PTg#N}_8|;PYINJuvkzl`iyZMg@!%`w zC7O7;zBC2@eiDMV=xhn`B(|D;X<!%m%6}Fm{@-L>pIRW_>$`3B--|kR=Nb$*wWk^z zGBpqjfbpLmS2h-4>HolUJO}~#|7mKq;E@1E!2X+DomfBfg%SV)5)BOk!t}p(K&r<6 z0kknSH#GMA9zl*)-HM!NMDqKdJR=r{^%m2|I1Tck2#hGTCvLN0xfrxgO!#w3{COom zOM21nUE(`x=H};jHyNz_ihzr;?l>C+)}K8e0oR^NchX1%xbNiqSz$g0hCItng4m#5 z&sNk(xzB02*D1!Jnp?e!E@pl&NzSz(3#5h;zscvHa6*EccmMgI=hfTvN*}F8Lg_wQ zif3X=QO3xVd43NbA+|$?^SsiBS}%)kDs=5#Sh?4aRNl!hp1G0!Jm-@ZW!JkJEev+z zI&>U54mwa>N*^NO52>x~aJS!xPYWAT@=M$g(dLfgo-e~K!*Om+V*Owsp%C5K3|R5} z9b@(ef>}qeSvK{h09A8`8mQf#29k;G8c*AskeZ`uyCjTiXktWpB$bKM`%yCSS?52P z3yIYk-rEq<i1_Rj(;dXDSX*x)!5b#oMX8-K?OOsAHfu#pe1An^`JRBm^VC$>@)3g0 z7)5I%|HfvcGz3bztIy&_sOd*;0};@Y+$xsHc)sgix-92{^JDLC`;}{*V?hrtdmpRc zY8*3U6in~`wGZL>d8MIA^h<ur5-_kqWTw^obrjb%U;<Y^4nFYBMR;njRgTuM5iyu* zuCHRg_YA-xSxTM19l87$>WURDXu^({9*b&$bYN)ev1l7SYekDckb@fse2tVf;3c)3 z=iXDq<szhFVAkG0{@IcfqYh;!l$RP~>{_}&>@^$Hte5wUnO6%<y^lS3N?WL0;=iLK zUV!e9_SPhEzgIcW&PgS*pisAFNe-q@vAKDNKcXVzzVZrQriy!}4_*iKBzw-cl|O3g z94@n>oO)3`e2uve;wNkX#@Ot`kVGCfr02t2=<B>51Xkng@*n(l+V~EA3>6WxHq8ba z6{-|(<gV|3cm>LF%uLq^w=N$O{1_P>_n7|C(V)DdRYA9qo?a@5kM78GBR^l&glOha zk><H!d|3j+&59lX++G6+(BwVM%YO<x`kz?m$b`fhaE7HbJaTB1RVfk<lD*F#Zm<2n z9AgIS<M6$f$3_g$S5^iZh<D<D@TcnW|3*QFJn!o(Z-G6jIWiv4D}VlfI?nu>%aRx0 z_&H8+5D<$0IbpEC?_=ewycU7Vgyj2A9Ti@fBMOv26jOshO&wJ<^&=oIz4~(0j{CRN zprhi~hEB1!H|Am|Y4R`CDK1w?dkQ3@a8y1RP%EcJ6iH)RDdk1*p(*#(m_p_~+|_&L zeX9~&41t;ZMQ3-XP5#}Sf!@^;#+Hs~>8BQ_ucUhZk}yP|vi1F$mnn2te1?QF_cfAx z*$jUFY@dYjbYx*c5VU-<f%KJ==nymOy#tH!KFREo(-Mr~eo~riR2@FlvMJ@Rhv0l) zzW3Xq?l@spR#S+epGu!DM29b^mt01i34>IG-pjzs>s&ctXaCiCz4z?t1su4Y$<|8u zLvAkVFwPFR9r+`PTVOjj%}dS#+DKB?9|8Kw{VC30?c4!9v|;uDbZ%c3gyICmB%cw$ zCK&#f#V%T<P?W&wqp<O=1hMWfuw=^XGo<2%1xDUmGa3LBXQL!dJt*3Wdt1)3&3C!G z%uc`*$2=%ht9+zt!LFJfLznE@XzBHp?$}{+#P$n#P|u%yWx)47Me+`Nu`49+Hmlq4 z%Wa(Mi!s*YfK3Us(e<nu1E@B>FYt4=&F!FACO8tnuD-O?x2dDAo1(h^Y#kf3U2P+^ zOmi{geYSR(_zU~(G)3lj#Cv*Q1L^|QKNh}W_Um)XPm5$rn)qZeyf>)-J&J(%sYQ7V zmoM<`zGJ=tn2i5p85ls{U*mS!j7S@IXe56m2{*KKO&yC|;#pN`9*_^9XaN9DCiVxC z7Ns>{4S`^5z&y^{9cK=gbKZg#h_tqOwzzZvV&N=$TGANvcrvRd`%3UBH+{!Td1ndy z3JJjvU*Q!>xuGMa3aCoDi)1fWmi4B+f;O{<!r0eyJPujtc_<+Ck^OoaNC(*7Y(a`F zl6f2BK<ilHq6Iqd#UD{Qh4shzC|*6tD|0+E%~BLLok@s9go`B$8p>pwYugmf6t-ny zw9|qTd?iJjR@3AjV!&VFb>ygpFpnzbi{TdT-$DSbMaFT9{-zSalOwdiK|`Bj?;ltE z!6@$mSJJyoko!PS{N7&e?MT)|lC3?3qdLeWbRB~qHn9%w-cYHjS1J#TN+R{)PO*M* zvppj=h-yOT@E)*33q+N!!o0(>GTUJia};*hAYNmz4{>cdtt`gVD&4jSt)MjtX$e~Q z@WUb+mN0*M$6Uoek)scoVPMqGb<*DB(x@89Sm2lbT?qhH=u?hsR|j;q&2xwwrTecK z2mdP4mXoT%7fht?J8^|b98%SQOQ^t$t&Z)jG?;(T3o6O(7;g2#rMb9O*Dle_IxM@| zw*6hrXf8WQhX5{cu0Bv4Nx-&V7-ZmDRo(lQy~w=_$b#um{$*4&oXqN_oZFv#LMAuM zQ%d_a-%z5Lbg0z#mt?Di7^&lGg(($BCgCO0ghJVF?{Khf#@>kNYoN7<ys0r>^Oqy# z`9asxm8`p|<TK-F&~CjXK>tVa#OMFT>G`QOsd_xR`|W-R5kNrb|Hsha{{Q&iq$Xp( z$Bxwfrd~@8BdMW%%)WsRoyTXxaYAMhfX-(M2uTrVcc)S&Ec58H{PYe(<fG(~)6V19 z_&JI-<#RV#s8mm8x^iEvu9y<sl>IAIk?2o;s8qgsMeWdm-SB|ja}xJe=}L`SwCYS9 zJwrNU9{7RhpeC@Iha8-euQUlSoS8N!@dSR4yCz>nu`Ls-*YL;b(I9JDH;b~9uATSL znNV|4ZeEuCqHN}EEz~xdD$fT8_M2^q351tRUO`ss+-(M{a?^ZLvW+&iRgJx=5|~&7 zXtfX2;ZRkPDV0Sep?SqZd8ipxjo{PzADY0wZBsIRGsi$}+xZRd)ugTyFJ~zS^RBUx z!z$kn7l{PYHn-KV{@@?LYzXu%@w;o2KaG&O`48qkGDigID9qO@%l)GYJLv1D+DMSm zep%K&uY?K7D2DJtR&>4&+J!^v5Lj#o@}I;I9x?%i2#5_3Pvw?*%;%tX+n^p9@hye+ zS2dd-H+MjRY2P(#!puoPY1IG+$?wha{tbuhP^n?fJ7=UOe#Qm`lzWy;`CRew4f|dV z>;>AYS3-Q_nG*jzVQ!ioa6mj#NJ4*%n#8|PL5Iwo9QhRY5?<zMH`B>q2Gb4>k1t!6 zlAkWfI4jTEj9!QX8Kz9lS5u1n#c#Ebl41+-&$vMGImUWrJc$H9Ilq>4ix+J`vGq9k zoGt%LxXHW+*y+5lne&(@lK8fEm-Bp=V^0W;DT-{pI<MC6nt7Bm)aBbCEy;e)V;_9M zilYUozF?CaK&u{MN;EZCI^wS%e{3VLOS+J3VGq*;aYe5YM?bwzl(A>7^tqO$LkyQy zgsy?yN7no0OYYI*_~(tQGG#L+yycCf^j+c~tQcdT_J0ngkTaA&m6+oZ-u2$5xBR?0 zer<ZDJP%0yo7rNEspn~z&8QjIQ>~TE#!J;6lUZr2_DrDr96NHWDtmyXx}SnV0?b9m zsO2*1Jqo$EZU-7TQ{G&X60PT}AK;Vb273V~dg6THXM9a#+4zofD9I`Q7I3>7U@wB| zTuh8YIj=jPPM$>7{gP3lwLG3^F^j3ptb7#r?z}^gOIu~N%oi3m%BYc=7}&2hU&z75 zJTigxu%#jBVdob9oof!Q<`V63#KK-}?&g-IX0W!<w+voaN7tKC41XI}p5ZIBVb~0; zO%x6)F!9z|4W>`)t$3S+Y_j1#-t7Fm$$4JR^@w*<wr&>Nj=$F?zs6pZu566Mao-EI z9MwJR{+Dtq7x-J&I*gX=X#-j06M5Xs&Sd;b7Xfi_aV`ZD!r-h2DXd}poN=D>C)%jH z{cWU)mbuoG;=6<6O_x6G35VP-4Szi#b~|}(A$X=PevvXVP=2@|iMpWX1WBF%6A3Cu zZn@7?%cLV)N~RKV)l8Ym{Io!sKi<W_;!wyrAOo@aL`PG+l-$smdNx0Jq4vVv^45>p z{?t8nA1AxgR|#5tHMcjL^3m`QWlEvt#33jDSz^~qmKy2C%=t`;b6HweJl{NU@E5$o zQ_z!`#}HI;Jwh(cF<hGyVlc5A3-nR+#UrP2fU+`~rVDxAY4hxU2JwYR`%r*O&#J-D zE`{#mXXtOR%m})rV*_mApZ%v}mN&Pk=qGgRK)8|V+C$Q8Sgv#r&rSBoqXJy_xjunn z5{mvIljl>0{}qD)-~7X=b<_Z_|E`4KcZ7y;zoRk1cQj`CkCVi&!G;E!npnCR8rhiY z|4%jK`)e&?jS<!7NgY~*h)NSE*yBripI>_+@}?1?mJ(}QS07U@(;VRE<65`NxuPpl zV8MDMnS)hSeI|j4C4h1qVw??5oa8DieF#on0Pj@$C@2bm_pIOUw7XBgcL4kST2^+} zUJe<=lJ9ao1;oiq3fC-j45kX9f2dP!GWa>HRLYd<ko41on6+}R=#t|7CXbUgx@j67 zZ7e`ZmOUHeTbfZm<bw-Le_jgn7YpT+R=m6Ls@X~hD$D{DH~jG$*+~dqK_jcJj+OZ% z+`X0KOb|26s(bs39dhISj*w-0pDNlv=+H*7QZw+UG+-@WQN*Osrmw0hAKW0NR@Ry- zDK7z{B_^4htn$3n#-7-m{$l!8dI3D#?o~W^@H@$tlF$%E)(-vyy2Uf5RS4|3uE#|u z5@PbPXNGA0t1O%I92z}ssH_lB*X!oi<7l_OY&J?jM1jt$Kj5)1xHFH7A^K^5M}>Qj zX+{L|FVIX0nxrlvn(3c)v-!D5)Zcvg9oE8FoRw=>u$5IP4y^mVBrPt|iY~d6oFN+} zl6n7**D!Tzv(rbjI8gW(gENs?3Jr7`WA4DFs}PDz4~qRAF#BlRmbv6&#*RTHcq5z0 zR~n*@twY0*!WY|jt^DY_3$qld@wF~!4qe^y4Z%+j%lH(v76uO=yXqdh28~}l>-o4x z>1F)k=APPT4Visg3stZWG_DN3Z)TOv_r4DV|I5VwOP_^-2JlJr-y@<Ksl4+L-*K23 z?!UM&P*du#!GZevmUfmd`ugAHK$W^~<QnIH37R2kqT7sMKC*4s>o)7c$iUieg$hwV zx)xLsdy;a^oeu-IXfzWJR`IkfzIm!nk2edQ1qSZ5bfJqrEF|EQe)?f0H3v(T(KhHv zsi5{QQWNE6+w_vop9~z79cQQ7beRpax4!~NEzFWPfr*IMoPjomeeM(WYK+D`qBhKE zC??w?a&3)yWGJpQ9()$Avj%W{%-PK!L-)gLAl^=3kq*Y`NUTI`BM7X=bg6|0ee*k* zGk6l6ji^-t;#E8Gk2->)aG1E$H+v&&I!HFm282j;_Qgw2ES&w=waBTU{Qh(>(i>Ei zmLtAG!0QHWFt@kSb~gg`MbbU#q~n1aLcG2zUeu5|P*!oc!1>an+)XuQ>J<psKhzPs zOG(8)6>d334h!+~sXM81mN5Q#GZxlD5iDE?AQ`iH9p_QpkAX`l$V3zkAEp41*O%WO zxCkD_#4hzFAl&SSg?jixm+ebGYborLgHPTJQ%B?hxWEpQbqK2baXj1+f#=69`nEPl zB;7-Z+(4MKUp~2D2;M(WN5EBsuDV35SV~0wEs82Ja^HE%wyVt1l<R8|u)RJ#ZGlEw z%5zryt(z9Ufr$I1Nok7`mF(|x^A4~teGb+Orah0Em<{6me-Lt*GYB{;3ga3#?0&L? zK<fx?oqzY}YDpjA1|m<o=0n0#)Z$6v+{RGP>fT1vbvIJv`K0nSj?U{z3UM{we&}Zr zJdMa3l#A(9C`WQc=0C}IaLs*8X>0oxwcn+dXN=tNJ9DLO&PTDOc2OrX*?SK22TOFv zp9Q?*zVX^FC+dEr?$&4R&F5~Ljg@D_$IDS$-~xFO6DPA4R5D5oc{!~PlT9RoyVcsG zNcJYzmaeE7iu`#E#Ps6yg`r_Fjv#@q@;}IMdgCMNX?Lf_1_EN}bMT&JR%)k82eGYh z$JPq(Tiad&t;wZ*BK{;g+d~)dIeJf3N7rYJh9aXhTuRSh@zRd8b@W6-5)cM_G-_-g zdIDK{+lV}r-pF;g6+71vVYng?LNrxZ)S52zv`oV1z+r8Y93yKgP}{UTJ;a0F6LEzl zpv?YVLh9gi6G^ih$!n2$S2OIyjOqq3)*{s7_0Tl-X!&Z}PkD->XAWyjt$&aY!EX%t z+A?&UthLr$99Y6}%>83Gu_orw9=f=Q3;=G>ZgvY(A>5MlRR!qpA3MVak=4A%b}*_v z=jzlA1+4B2p2Dm~*}_g6cJ27{>{>WG>9k=_W^E-?+&3iqa71SPi(a2af@-&?U*u+{ zUZTsx;<$z<dxe`J5dGQUywhBnLYC{mh69;peDE4WrxFy_g?%jm;IgOt{7+4Y`hA1< z9}J`Ua`(58ItWOd#D5X(|CfhIbyET$0ylWCocEgRwjR{CXKK(qDee*)vzL@4E{amu z=UJq@6FVM#Yjs13iGG_$82p%x*?k3GU4jvUBqX?|r#>C3I-5uC8`Ujub%5Z3_Y%yV zSC=+dE-#U7-IdF&3IDPo!FE#}Wv3gzdKPFe$TxQ$y__pX$~H;&FOf@aDu{1b0fyn3 zi%qFC@4@>dR&Es0y>BOwl=J=59D6*G=*hTfKaKf4$?B!~K4Pv+yR5fPqUmq+Z<gA; zzQTH@k<oribLx&*M!RPf27<U<4Gc6qwRmdm*Vsx-DqGyDgMV^kF`K>iW^MJDCtyz9 zvz>mdfNxr)X8@aCJ0xUIhlvg$1sLDg_sCnNmE}CCNnz-vC;cgC?MJz)t5nxsdefGX zfYv%d9n>~x#;#}23ma&_zQ$_wFTrH4Nw+@{Tr7Y_u0op95+vxMOLiXWt_yXIu69HR z#<H7DCx1dz^EL`;;DnpSW9z;JO(Un7st$H!p74WC4!4q8{?ao!lDt~T5U8BRxmhxa z=P(2JnnJGtZ*e~Rrlh{$tA*aypA6wlFQ1>t_Af$iC+>Q4-SLE;I?f|Gh3SiEkFBm> zUY?kU9Js++kdEC9MMr6><3ILp0NKsc*`Za7m`C$`F;H3$V!9O69dJb<C=}~qY4C-^ zQ0yNN_AIc1N_JHyg`+rR39OEWM7Np*p!2e{TEpfc9oZl@EhGw)TP9vac+21860|Xx z9Ky}A{y9sa@MIcwWvj+$#_Bw<oy`)lUP|IicU`?qP$PA)x9agdq=)uLhqIeBlLBK6 z<f~qOi0Y-7HlY{*_9Xh_SUDp1?m~lv{{?CkCan{-G+?bVev^wc0S_@3pWruO6aPp* zZ6g$<mwF}^<U+LS_3!=pi??+X1Xs9XlX3?y(?f^h+6T1A{1w`IPZp$&-0{w5&Es&p z<j~ZkeV%*%uVA-DrobL?83#dp!>ptB$0z;}L*ez|>+2OqOACX&UEvoT`oKtd9-0?s zAm*`rouPNg4}pPOX&@=zI{zknK}G++LVUCPbJ%E{tkWN|9>(IaI0*O+*n+gIdXos@ zDo>r3@B;HSMGZbiQTufDVhvy^VAbk-bxFr3AnZ}=FHqcAkgn-wJ`~d7OrYLnHv>f; zAa&wO=QF92z(yieJG%5EG@8%2`+aM#-yTrX$dO7AK%m6E6cD<LF@-C!)-_@Vc9%D_ zjc(Z<0>ej0DeBU&l@2z_A;QB=j3|DZ+5<yZOSp3!(FO`^*2&6U7-}*UMZgJNID8UE zY3Vv!L)OI7j?gHWd|&CjD#*p@_v-ibXJNAS302XCi)k5Z3&tYEb$EmB+M?6Tsp_P) z13467d4yQQAn<JbpWoezy}qsq7)ox6zFnpZBJ!j(4|I(^zMU4j{-&0U2WZN57^5Gu zWyKI;T+nXBdA3}--2n{r+wDhY1&ynI*#rblBgqggl_}~wuby=NT^wC7P8*8uk2aN; zIQ&1mLA$7;)(+}!ZTHYh1-p;zSai$eifO3+*9VW#7(fM^#K5a53`V-BB^EW@O33{m zedS3=r=cBYL1vMZz75qOHVT3t)EULyYEp&y*+2P3U9E(Ik%KFpv81>CPFHnmYK?jy z_U3Gnj5jj^*%$(!3n|MLh5ffZEhZTwB%|M~opkaVDYE;?J%Shb`O1kDVp}IldN}O` z#DfoIgn^p|`-5zWqv;G@xejYQpkuIt<J4wIp16S^l$Gus;I=NgM)!^cYUeduzI*Eq z=W1Jw&d|vwGldK+6K$;}yD>7!PYW`QN{lnE>6h1EXQ5mA*B5RNYT>Tme>O*852Wf~ z+y8-E5#0~-hr%#WbmQD{a7kIPhqT?!`%6fl5Lg_fqiZ+S$QGzAgLR;cDW<q3tCmh< zZV?D0ruxlnZJO6rAjBWT&zMaR4oaPoeoV~RVCn8iWC)rcMySP2e-21NPWteVO)h`T zzlFrBa%{Io*yLQhIY-d-T6)~$>*RJgWwl^5S4eYT6RpHd$S6)8iD4AfqYD~|{+CwZ z2pm&0_$liIwQL+m{zv2_g_)fN!3+mdW7tOc>d+x)@VJa2cxEpby}*}Oes`CQ5@7*9 zT4Yes696xwZ`z3YGpQLlE>=Cwm0tG~B#;Wr9~KMF@0Qev%@6GMfP~izhIgBdayGjl z>J*JetODye^oHozW_PA7lug7m6b2Hj0Qi^;f^1AoE5z`mqqWFp`ArJ^%l%~+vR+r) zm)Wo<c$~@}y}`hLubd4b7qG;+_~{=oflGals(eXHM|2+^yRD8XNoor8zQgZYhgNMp zjLTNH_`Q#cKUt7lu-s>51`ijQ#$FFHsf6M7QYPzFSb&u`N)#}<8vjPlm-*J21!RD% z0H|!o!CU=x@8MOXOUSwwa*^9rKB0d~I(&Mqu6J-zP$XfWRd9=pfEh*tA|bvKh6Pk1 zn%FyV-pZ<WCG3(~g1ibbG$Tn?lHPy@@xQO#+;>wVvY&l?zP;MP21{z_4$WX0FT2k8 ze0>B~H8CVF<ERjUq8=GTdH$#h0}_K){l*r_!;1?m=$w3)`S~a7*pb$ipN}ugbpA~! zglz0K=ZH$oS^c6*G<_e^)o_6Ybyo?(5D-@rSiPb1iEfir$ULg5XhSn|O(<N6!*SlJ zv{8W?+<TVstkYP&tYhH!Du^nKRPN;A;{3b2sFZu5BEnm=71af{dvjM;9~dtrWx22o zDzb|YF~xYBV?%6@bPUGVE34E&emPhMtARm#@PQ|4D@oH+fy`AyxJ3avEYg6NAiUkJ z(q*78^wI|h<Mx{iC3qK)3166@9*#CZWH!<fYv#|f;x5qzXb1EJ=_hWe*qHP6S&`Oi zap^6Rgjfq8A&IyQoO|JP4>)2hM20b9FVtD^hl66Ff{@YDHhbG9>Qb)m_RtJdS6o!j z&v8xWg3ORyN_d{-r1k}lgemX^X{^v<IR374+#@hrr#8lfp(Q^Rw*P8VCe2A5zyczU zZ+^TXP9Jy>D5HCbQLmbhzcvS$V=0DHKhdi<)TFn)J=QFEyds||1ag&@#)^Lg%k^v$ z$8f3A-hmo&cW-ME_AuKtsz8%aLgeFd7e_+fD~fVT@v!(cSR{MNdPw80vO~!%I(K7Z z<X4bST$FTXx`NJ+M>uz1HXB}x->A)Se1&8c9%03U<B!Sjm}-p#r-}gUf5cH(He66% z3ib4*`RV%~M(uId0Xd0jK!{x^1n>goYNgUOSIfKL`LP3}n|t^`nX6t*SxU%3rt&fa z3yJ7{C-fcGo`O`-tv*YZLeTp=c3yhl?-U~o+*&>4pY$Xgr&Q!$O;gabfl+{P5w^#i zqx6s+055enPW4a8|Grr)C)`r6D3#nxO7Y0mB+8a*{scX-0S3MCR;U!!CpH<O@3SIT z(tA?Zq_Yg7rdb}>BbBWU@)|)zWf-m)q>z;1{i#?e#b`8%<y-=?DqO=W@<FJm1$2>a zgz?4qSs!3AV^?_!iJ-TqGH$b)Iba~25H5aeir7MFHgvo}DK!LBhZr<o$Fh$lpTxKb zdg_N)==^+l0vc(9?e9D!hihR!34eg?$yn)q9o~Iu)i?P^Sb;Uh6%6=_z}Fest85o} zHPa1p)1hp1^|aEg302Gx_m$cU-4R=>YIhN^a!J?G(J3|>gBgIz8DZC`fUwy=imkD* z<l~WWLHt0mji7r>sq@`dDoyjqlCK|TdP{9@2F*T+23|F~zC~~h9XEa<Rdn!Jz}i=! z(7Y}1KWa4JR@252MezRV;+vdYxVPnjvo$TEByVt(Wb4yislhMP;!}UD@U>xeHtk-P z=r*c;mw`Pj-yiF`mULM#VrzBN5SJHZn6nMN?~pA*a%ZNB<k5iHC&}0E$`^lK$Taz8 zQ}4I*27)??Q5FOG`jBJ{tem^30+iJ8^FUTQ26b|A$|i=yezXa2T3zJjEvry1fnE0w z4%2Ue$*;e$bYN0L^=x?Z1xOL});LDtS-LVpfhL8CL<AV;VOk+-29%q2Gw#d8e2dYD z`E(=WnHc6@3Z$7J2{O*HrdqD_Wgolq<>)j{0k^|+<1ZXgAjVY3B#uplmspQc-inV6 zh6hfVc5UJm;JD$&C}#*muOqUQc>JE9;i90o7fc)(r~KYPUo*1Ol$fJBm&HgZM64FC z%<0o=9NF2Qy$$S{Vs#C`vR<sd?=xo_{ql=l-HPd)sorQ@_QdH~%aqL>dQgi5K^loL zfHrq_fc(0%Nhej7;Ceu>GJwQyIaw<Z(}Y8}=#iCmR78}WY~ijAgr^~0t!}CDGOU1B zNQH~MrYhrVW9e$S2Ka&X(BA3WNzLI@YmIZf@aI4xC#JNS-4@;ZW8!A&3u#0NyhYpg zDf<QxGNs@BI-a1xCO)dg+be>0)4hBN)G8iti!j$2Yvkhem@f(37dv<~ac`T32Z=wF z@|}hkCNxE~LG4o;H*Y@aX11C)cOGiR9W#D3zaTr@j_%;DM!YjDtnRbZQ&5ZopG;20 zOonNH$IbeO>7?zvy3yA{%*$k;#p)39BR~o{=|P0ju78_t-X-@bDY+9ycRTz8=r`DX ztj$FN;cQqV(DC3VS&ch6+zd6KGkREa6fDPQ`HfKyz)M(!M9LcI4QjB&=<ZqdM@Y%8 z$NGt{aQ95f;d=2F$G*JQpoQEjb-{pbW}Me-qxx9c(To@zl;hv<WaBOZd9j=yOkTlk z)4e<*=L(|mOU1=S)TmulMJ;*`7>DCv<w}i;5`{*C{w{xZo0s!s=!LPjPB4fyh^re* zu2_2ft51b9GH;28Cs)AA<==gs&TvQ;0qCpVIBux3{6G}OIiOsN8<!}@#y4u_>;nSN zlx~tz*FEF|ggHUges^2`(L~?ZLSN==nxrKK7xm5fzQnb+O49a*m-Dazm?q56KVfoE z(A-+=1fGoJ@l$krl)oqLd$dm9|Ky+PU!`ET)}AbbfKeQLvboYS<K5N-fwE?gekSrX z14KQM_WSATxK%u38bv=VpOa@;Djcti+8=hina`?ur_Q`H=y)Y-P0!|Jvd`g}O8Y~q zuHf-8xA2w_qq8cxU~C3Oz!)0p#-SjkH@ub~vT1)FAwjzr2>U-HDPHBSC6|qZeYC3X zlQ;jlxG@fshnfSmd?OACUc(vp*63KY9hj8#s6Y$l6`^#qe_|OFoLf%TJr5*VZK_Ht z@Vnm?C!S$meOX@I#%kY(X_oNtk|=OEpCjAJ;X6C)!UPyY_x#d%13uS6wEos>Pw7}u zXF@ilmJxyLhlWn*UsIs{6L>b$dxHv*k}x1nB4&C_=Tgt(Q6IqJxjoeXqO#_;=QT#L z02orpIQo6LqTl2fPC?r{gUp~V!OmB#mhS?=k)+leUd~?_DDCExBOH|~^rnC62W*|k z;i!Y(r*&alFX50h0zRVY7@FgAaL;Ug5Mk09TJ!<t`CHphsBh~Y%UU{>2o~b2bF5{9 z(MQQ!%RWVYJ0#$_IYk^RF+n8PVjkQ0uwr9c@^hI|owfSSSp!@P3=14xDhp-GjB~?q zPj<^uo$6EH?AHeQ5=6W)b1v>bbw0GuhfZ1yXUlRbt>TNgf#Ygb6slr-{OM8*7wP-m zVnz`R^VZtE^jThzI<ObPC_kIBPw!60rE_tEO=Pqsr%V@7t-0Mr(D)#onuHkAw|*2? zdAo+^Ea<AhdS#1UeMZ3J1&RjUzhOzduK4QiCyRdd!(Xsyjjdunuo!2s13tgzmHmqO z{2KRo>r>0vfnQ1-2Oi_yoKCZmEF3xfqk!zaUU_c+MD+6_zOlawro4KxoIDFJ^BYn; zi5S|dp%jnHQBs5hJKn6j0Y@L1tk1m|CI25)=M<z_u&vudmu**<ZQHhO+ty#UZQJVV zvTfV8&C~nrxN+}&TamFcBA+rN=KSUu<r+Y+g?&xNR>3Llo?Yi0i#1Ty1rv#@sOGN) z#)w@$ahqgK+)p&M9x$c^T*(oxlxQy2y>$SfF*8wsHh!<D6o~Ot0o`RNKP#@YT$!Ut z$hkZ{v7sSlUP@S+=dT-BxtgzD3#WroZs**g=|yFRQ#va1xH<y`XmzPRH=&mEUfhVi zQtVtq50_OD)687)eN0y%fD6w(>`TPisAQODe-rNk3ZR`_aU_68n3+&yM?Y47@I(I= zJyIONFW;_UlYL|e(|Z{SuRF_(^L>0kkOdpt-Kh@$nv9-JXg7t6;=vPqfcY40l~ck$ zU41enYKyKM@ext{@snIMb88W=BZGHzQ!y*>x^0a43K|5~(H$ce<eTHN!|KTYNHzTj zpvH7R_L!LU5HV<o8{zE}SsJ03#Lt(L`ML1Vizz9j>u~ZsQ&;k;{BaGwlgFurzD<fK zfZ{Vn%L10br`VF8wo~d<y<m6R8Q;KUzbu=JPZ@jW1K<iKm|P;@%#)k29IU9$29hf~ z9&g}F%0pSyl;jf3O{=hu#YJkZ6EY(_5s~Jxw|S2rIsDQ|km~u6O4&wnZ6aU~yDA2J zb0fWXEvDXBAClSTpE<7u!_PRU|JB@a_Kg=}0ZWdKcW3p}s)m=~-`zlT_S))g_7tuX z4r0bIx2m|YjP3f~xM=i>3FL|SIJN+=1^LAMJwnA>`jJ<87y3vCKuobuFH?Lqyg$4@ zpRi(n;&OV33Crd1Wihc;<MeJ=)hGfaH;}TlCoj3uruBgfI&a^)k-l2&lMnQX=6qB7 z8)Rl;PhC+ZxyjqOSiwS%ySd(My~Lks-77_yB<9{7uw<&w$_IL<P1IYOPZWO}eF`ok zb0Qr0=^x&O%sYI+Z^L}8*T7Uc(ORd@WEHbV{GH-3EmRxgQN|Nd8RYoDP>_ZN{q=uq zgnxgTfmZ)lv>DO-uv>}&1k@+@e>w_~X<hZ$5P&Y85ATiPgr0}G)i^1>Bq%UVx0#wX zCUyQ(74;1zT2k9MCBt}~$Z|oTFffq^)UTVI3|~-b&1v3vAM}Q}wVbsz&nueuFTWem zpE|Od*U*zc230Hv)iP8G$17cYi_dDR4Tg@LR_CSrj+67UnkuZ)_nqZQT(nMe7Zr`7 z4S++Kf}KXA1e48fU43GiC#_`GSffgqg&HPPqnx{$V^ec27S+^}2Z`shGulX+1DH&i z<Vg&#SpNHLbxB<nVmGCMI?vK|`Xnw*lL$ne)0R?+mf44eC4O?v0ae{DYG4#y=2J!w zx^F+W82vBH_wyO@8vw52T$B$vpi`<57BG@(e-nj(E0GcBB`mthL|eo$Wf<z2C>lkx zl#h``IvFH^Yca}C-&(zNCGVtoprOH0Lp8XeyUAYPVjhmV6e3_}0L+X8m8%lvN`o$H z3F#nbdZO{`qU#<BAH@vV-?IBK&#(kNFVPym?)J|-;*7}|4%H?E!O2rgIbUCLFaW4p zW~Z4ajkHS-7j(Ke9fj>;A#*4t$mORp3^%s5N7h;6CSor{b1V6lKAP32u(mdzl#LKs zbylu!!zT0(F<I8YO@a}`r6W9x2YU>^Oo8xFk!S;@VY%Sa&xnEU$`So*+$-4MC5O%5 zhDk|=H%n(7?h7v%0eV4zPbbRm-vE4(9p43rZw`Z-M6ZTW%ZE?0nn+wnBX-G@{Xuxq zuAnO~;Uq)LX?$PjkL#Pe8G63#PLDS??B6@QMjOp~O<q&L$=Ad8CIf~uheT+sPEHL* zqPbTZ%Bh`NB)v~qbpaAagQ;h(&_)I2h~VC+{2&}b;OelgMf(Hcy4A{vIDmTKsG58+ z^Rg3i0B9iD8&kzUik{)x8*-UDQ;yu4XP3!H+;9GvSaG0>v4ev$n|7H5)1vLiuNjUH zMBVSNO9Y0W-*i1~okKg}{RmAMVLxxLx1K&vU$^&m;g4KDXP1|c14EClK3;B)PVe77 zlS~3mW~oe(g$Zk$bCg$kRY=r_c>4k!2P7B61lD3#*xzkwmo}hSfItqke}|-*;&=V% zr8&0DRq2p}t9|NjZ``NWyQ)%6ie8BT%7t~kPF_+aw(5>v2`T4JfwNi8wm(3VSLlfL zQXjpqOkD!YDq;d*>>9*K6WK}uFMXS+)4#f?PAAu_*5gJI4Hlkgld3ZY_6BoH$X|0W zWA*iQ670AGu=LCVGz6&Dlcnj-#VPt6=%o!2$>cx1y!--Y3$F4pVTms}gQv6`Kwix2 z7nU#_m;@}x5kfBm6(;lwoPabR?;p1~rT-;$?!BsMq9CpI^XRs5`J;B^8btf!^%rJV zC+zPZp;&<P3kjz112`(J0{R_xL9iNa=S-vznGICrP?oy@6~+kUMX9pmKX0|*%W2R9 z`MkH{KU?8XSzfNM9p4V9m4J5<R*!cR`>5BWMHa5?hp($`uB%4htJu5>v5}eQmU?1* z{tbSjxv7>U;(m}^*_m7(Z*2LU9xh)vGC}pwnGYaH4Hvdt9o@(q@TP$Y+h2t^htC&h zSs46XT?{#Zg0e#Wy0B2<3S(12v6W6gY11GMF;>J8f>&<<3-~6`WcbhV+aU<L%W4sk z7?<xErtrKGrvXM;o%{&H6q7W1FKGFMgmGkbIaq+f2J6gPBb8L+`?T8=_GNt8oHLn# zK1h(*FRIuIm^H$AlEJc@(dzF}^fIDlrY%}<Ek`7cBCNwSt3P|GRAam70YtkE6q!~9 z1t6yXbk<RfPIPEo&X`S}iY7|O>6ELfZXj~PtlyY2!J@P^MD-qZtMmqY$Jv=24}=Eo z*M?OgEvv248+_|Zt=9&!Y4Y}<m;lJ~%x$F58c_*j+gFG&ff4{s0IP@U%_&?pURph9 zU(-@N-nRhWuvrC_6L5KarU*y6;)78uLUU6*dUHuN9uSR7pfp8>lDUPlHI-*6cU$(} z6R!c_$VT%p&T8t@X2HZ2+9?6giGgSjAZN10T^x6Md8e917AcIlQOnAI3aBkMaXI=O zjW=UZyk6MA_1py~N@qa+$$bB|c52-ASkPiC=ReLaNtU0AKKPo`6uXHfN%44-LPF=U zwg}s-x#T$17jD*(929x(57Ht^?A<3yvStdqAjbypy2~jokRG4}%8iy{V(veBWf?BP z3r9F5kiZ{JLc>d52^Jdw56FJ};K}sPfoq7!+`Y5n9<e=C%E+mf5RP+k04X=UAg%E~ zw2qCHO5~P76Tu|L5fe&T-vgaO<|YM=*pJuB6cdbV$EH}1(TUf(1^TM+mvN1Kj;~H5 zj1JJNPY9Zuwc}YrV+fc7{%bl6HQl>mJPkxFI~BoZzGNhOn3te;N*(cT4FsqAwigYf z#Ylgi8r~<~SZZ6rPDim=2{esrz~fI{w+L?wzk<b%lZNOBib_O^k4$E%F@d*i%@~af z^Up;Uh#OIdCw6)Z4B6EQT(c61o+jo9N(68yF`n&I8s6GS2hasz-@2h#1G+ba=f=^i zTopGDX8ZR@K3UydZdM{hwKlrU&>4Q+e#EfUJ8~Kr?V=^l!CxxZUD-8*Luv*5b%*^X z|Ft(#{~Op}R*4&Zd9N^TGm;h;0;d^EV)5JXXjOc1(1xu?BNHTdF4&J8oUWnWX$Vle z1<M<P4+-Bt?ooSDY$mB`))Bi?$%Y{acdl8YVaQ*A0LJ3s{~?-#bWjI!@DwU(b%TMu zvyC+-x@AcH4#X=h%uZM=(Ko`V6=G1oH+_XZQ$M1*LC1&^zhrTLbGBxkl{V|PlqR!J zbAo60t!=+{^L`C}c3X8sZ9Ye4iw>yoJgD9+?ka9cF+G!v8rovZbS0-%Yq4$CaF;l0 zYA75Ye6!V9M+0hXYI@PtR^O1t!<k@A`$4d+@+ZYeW>v!ihCq%REl)n8I^z`EC?4-K zxLq>+(I!NL`V8^AyhZeG@>Cu;cEix8%CpTh#UzO@^upp)8X`^AKOaZ^0|{_z7eP=4 zLrzqWA_8SK4wg{vpG>>vBc}Pn%<c*6cjU^~&Nqjshwd-v7kJhU75uAh$dY^_o!2gc zl88^83?q2UaUDrgVS^NWEr@63NbOPMA@9Bztcq*dzV=EX1!)<!&Lj<jH{qJO#<U;< z;bu$xQam{5%bm>nCE%K$cL7jd4v0+A%VBR=Q-x@(Ag2>Gva&ev-hI{WP|yapyEE9$ z#0pdh)s1DQo^xZ~$^N|BLM4&4J#hAn1kMcf-S7OR4kQxGc2%5jEvVo+jW}#7TmGof z2$rLyg)#_I&VgUNHJc7RLvx?0yr_D^%w2l1y&n|lol%IP7!ElSy$gT?8xiOscMbr= z$TQ%!0F6oxxdy1=QXskb&~UJDLRcjyWTzkiN!G33&r|?eGebIYTwj{U7}fhqV_SU^ z*9XM^UKIH}4Mu&~dKH>BZSA2<qkwlrO9JoI*cV|D(UODVtE5Tv#Vy5Y{WOmue(`tS z&$WerHej;MYsOT8{RBjOaYEbOMgSOsXJ(UBXahi)o%OwQzChKUTAB4hlnyd18cLEB zEx@dYQ3AfD5W{)z)?lWsSv`7{3U5c4e-r;~1rcy5Wb3+S&{#}a3fGzrge>!G(iyww z1LJX1)}y?YnZkV8a|)yyATWgETOGOt@!91gbgu9tArs^?kO2yn*k9){)WcV9jySJ~ z#tP(Euxg`VO;z)q3T}|v?lCp)<cKKUg0rBln+1jpVlUjK`zub2Y|R9v%jND&IDjrg zx(xHx?d1av3Fv2C3`=|AQ-Xo6>VtSwf49qFw{?F6X+w&Yq4NBjhfegw@i<@9O*Q(K z?=E*_CrB^}7XrL`1xcaF8|GVOLD%9U2}V_hdV-Ra8J6HgC_*@ZYE1}jljW{?)17d& z^~Dn&RH03Zc{QM=d7=sa$ZP!tvJr=qoQ}U8iUz*Cj>c|Kj;uRF8c<jxTN?v|i2FTd z&Qbx~(mb2|hAZNt86R^;eVcMkzvqL{&G6%G9{bq4h5~2?0ylde<qfQ=Aa3^>Cquv1 zDhVQ~S`l^bG9&N>8Uw?#AtDQoH9<YlK{$%^-z+`0A{I>0k%=hkKuE!5s2%d%i~KN- z%qfq@kZ#VDdYhJ;jT^lY{r&uw@y;0QyJ8G2Vj-itQPjw^LVv+A@(TH)0h9+Xb$%*{ zFBEdni3|```OSEG-yCZkg_&y~?NzhaPL7O?gGPeSoaIuS?Yh&olwYJFT`f5Rp7`X) zvf+7=p*a!W#DA1p_nYq;2Hz$=^)lCD_aNI!C+JW50s*mEb951rNbx}Z>u5Zatztw9 z4_JS``4C17yKmd~*6CaLl--NgqH*T=R>}Z`n<C%_Yf^t(z~@&U+m+ZAZP=Fxja5CO zidFzG{~riXb@d{B(WeyTig~$~#R0`0mudeQd|;tasPU58EPG*>+^K?NLhI~K@|}Vk zY=4MExn5Q(A>7!Fu;a}P5ezQAQ#?Xpk3G@9+8dS&aoGx&gXv2Z`elFUVR|@Pg>&n$ zvH}5Mz#!<VUT|h!WC%kBPmwtB5b0r<L^)&nDz#xv@`22p0=zl1o<upb;r(9Y;TZ<$ zb#J7HTd3QRdZYG#J}b20d&2QW$w<mZU!-J->nSQ=*<BgfingNeR*by!Wvj9&k%GI% zY}TBZ1{X-{wcFPjDXkF6JT-A=TzW+MT08(SEh8f^aF@16EsKm$15icY9cPkP_Dj0M z4i(fyz(cizu(ok7relf6dS$m>h3j%jGGTI?@XT<OoiKf)6P=H_&rRpj7gyLdg0DZz zc+|TsTpS)vPI2kaPYDn@hUtcZLA(BRAI$wX0~cz_$J0*MEzYkgC9G7q{p%d(e<c77 zydGiC&v~5mM{C`%x3+ydyEa*n!%0J0*SD%#iYou~VbW66*S&qw^WGXzhqe)f(e$oO zlLB{o5lUq%E1A@0jE}Wv<8JkO>&{pWH|-q-6UBT=hO8vyGMrqtO<~o+KM)%X_!$eH z87=`qgO#I-pt(yIgV~*+cXqV_3rc_uVaDbhW3vFZ*lbWhoRBLwcql@t<WMl*xiwRd z_A%R=6z%!K6YP<5?pQI}x*6;mr^y@11Sxgzz{HrqwTa?h6VeJQF87#yBF$5EQ3Gu1 zmhSOio<O$%+n0Y_s;$s&$-QIzuB%o@k^TIR(lkjFKYm$mR^7jdd9$7rd|ZG`jOi_? zu>ElTniW%Dt~R*n2kRuYc6I*Erj9h(%mS>&B~77GH<E_%ErTPB^oIV#p~5v5k5TgU z=;(zM5|~jKz@Bk-8z+2$V^cJEl3Zpr8&Pfl<*G)?FcSvP>?S9B=BpN&L+G(<@)<_6 zI`z2sLYS3a+YzDt1%cgb;446d=MD8AvQ0sI^zrgiBr8V%W55J^x)<2`PQ^Pe)oLd< zDXXA^7ozH$n#)-TPR4kZ;i!grT^B$x)NLG$a?!Y28>$5EUz&S5Iy>K|dcx-EBvy94 zZXr!m!glw84+5@<?z|*B_)6+#B763W%9&?BaUkidkdn3zRRmVvlmejjM^?Z@*w~X9 z0T@0yp=#hjm>tDTM5UM*7YJX{kKkZsa5C{6rLMTGL$|End^T7gRdp$uii|pKL!>>N zGg3ZVBcPLF49a);_cP1P+JVvpTjPQa%>*S1l(YigC>4E0NaUtBXwWwy8TxPh=P+9N zfaQKBeeo;g>vM|eUU-0TG+v4*r^h9!1F+P*ojBw`f-k!uCteNo?VZXF0xo!<no?U( z=U-Tqa1KdV&>ySPhhjuGD_uQDuu8ou1TM!Jq*$(qCKRg|FYGf6TvPurUhCWxD^QtH zEcFg4QGE3=5Sqkfn_j!}_YVie)%q|96Y~dDnpMXceGyLevLpbpPl`mZ<^-D+nyl8q zL{2K)^LxeWo*<WQ-vrzeM=kwjX^&GA@n2OXDuzR&eI1-$gDhred6xSp9W-Kt{a{92 zHQS~j9S(9!-@L(QvjJdlfEOcP(^Ty*6QzVv>ww;ZGgmoriBl<ik1o2@${?T9%){5y zQ*1Xa<`HRPaZ<omGl!6Zcm-nqSZwa+@t8f{G{HEgq4X&tup8;9AXd!{^qkb469f0c z%=czq-TUuZ%~Kr})B3%y32E;=WVnT_W($?vIe{~hc3Gl2>3vs=;4&2cumqm;{)~); zrbCpnKjPs`vV$gU`M*W(XLeXrp_yDUxR=wa4(FQUBmXg6;>xkBt3RonHA9pwzy&}k zNj}oj@-cv;yY{hdZ=tJHZgMORgflH$<nGX`dmjmenUv5aTV&llkb~Gw1MCd+RRxVv zQKa^IZaP|6_Y0v=TwtN3N&7om`1q)FSwy}+Ha$}Wx30kE^`N|Y{Z>s?!>4@gZ>78Q z{`S9=)BORAeng;{v7by`AQ71cn~70!j)WsASS9{xE+AMnwZD<_CB5p2Jhl?LFHGlI z0ck?Ck0PZuVA*^~K&JdaY5Ps3etN?>KX7#CQ^9nAwUhib&AYkm+ii71mFoDMYjGUp zr~i!IvcVa3c;w)UW%L^$dGAB$`c=U4o=<nNhh+&kRqNzqhah&AgSgBuw-=z>EQb2f zfP9S4-w=0&^esS0l~gsv%;3Q~>jx{~##jd}J0v^*Zt%tUA*SM|?A7QRQefCV#GA0h z6>q*&*v)$%NbY$HSAxIM3HXs<G)_N{g%ij!9AwylLo;=C8cGV6Jz_YI<)6Xu-zlW( zRrm#1UkWcIfZagBNTtd@pCDRdQqwEQA4HnvhdO^2C6*^naeAjvWv9-fVHW8dybUV* zar?x14$BXvPby-fjjNwQV*A)Lt~R@69%4toC<YOR#{EU|_XQaAYYv#R^=A$^P=L7K z-;^0c5qST&ohp-ws`7$Z!$Cu+n#1e+^6dd|GBN}B_<Tcxr5pH*#wC{3toBxx9CZx9 z>hr-AY~#N){+#=v>#y6%iF|N~Gl13?P19*;_~HU7!R((8Yc7!#UCid2W#G#2r@*Z6 zRIeufDe?U9A47vYpT6wPsLVTb?_oF%{>r3MW*qJvd+Wl#it9wbqUMKcc(`FaFGL0? zA)gdu0%F$We{UQ4p#%`>^(BrTzrixFH<wE5ay3H!saqtdAIjnxf}u%pauylGU`PUW zd)*>Ta?cqdpzRf`It+7qRBK=w+j6Fok2uZGW50LiIfmb<YppHwRPc9f-V11AKjR&U zGbGudh=D+)WW}EY;WVB%r>wz&@^AqJ{{rRe8p2&?KcP>BYQ0R^_mzQ776!SPewQ$C z$&-ZLMff%M(r;1c2`)`)|5YT|bv!l|TS4DjFWX?F@X6Mjj)h-=rm1{@B{I$U3X{1R zI(7XPF9X^&1>rne6ah>KTfP$GK+IIw?1|o*6fV?FfB1-N5GE%Cg-#S9ey<Lg;$K<M z^#I1765HY^lWAz<YfrZD*rUIvR0xKH)ac0!N`RAYvI=W%X@IM)Gl(LSE@^aSdEX2j z;G<u!)jjzT)T4nNxxpWaTcZ_dUr4LIx@C0Wvw@fShlqojz7&fNKYKR|POnrWDg*_l zkBQ582MS8Xc!z#mm00&|zzP8TO5sLqi|gLo)uOwS7ZQj|MOAI;4`q(WUJQ(>BjPLx z2)*9CT_n=&FXd&u(Les$v-vz6$HCK{#*d5ZyUVQi{K?<J&s@9=cI_$8m#^;F2FeK> z@0|8^XN`bkQ6;PN6p>Pd4Q(0$Z}THUw_Lwz$AMRTM(jUW7kGFM!{-4Ue@aF!tSlzK z&U8%#{YHt(d7D7q%wS?~+n3hmj!z&+&cNgkAfu`^!scMBeXkUGO;r$HQJ4jZheTvj z>h!7VMlBF#Gqc(Oaeg<a|3bT@s?IzpAp6&Srxi?0k1jA?ybk}Hn-VNxY27>hOR<w0 zn{2t6S(2;HtympaBU3|e9vuAB1rm1XP`xPG8<?B4J$98?-x}EI_PyM7YRu<Sg8%Bs zrsVR?`nh*J7SZ*sY{`1{KV|#>w`%{<xh$1O0t6Hql~!*C{GVtqGGK|<GHFB1ZRbSY zpuj02T5{EiW+Q{Ov%%FwwPZ_`#+n^14Z0su7G90OAFLN^F6r&YQ+#J@H#&Pus-MbI zBLCy*>3yrto7?51n4%@@j3K;G;@o!!PFbnNmOVvz>(9YjhEEG`b5_MgrXsIJ`&O-; z`?-0Ueso$`h23La5x~!pV=?9ZTYAg>LM+?10V1o`axip#VmvD5d>G}+Bc(HaP(hEy zD5Vn}z#Z;JT;=w)m^MJ6lEa@iB^do#OJ6oo+O1Hh*C+4Ci06IU=JoCC&69+7&PQD< zqU|cYoR;-M3eT{SHsH+|UHSU^^rQkB^syd)P_H?u_NmCz4N!T$8YSCRQ**3WMP4^F zmdSC_K3{6n(XyQ^`xQm8?nE!Q)ET&J5c>psKNDBp2cr=CS<;#b=5QHyT~}W?WV+I| zlJm8vz`le4ge&!hug61axCY@Zd5*5uLz{I85`|va<#f(xU3Xq!?P#Yy_Pc}s>3ac_ z;?9uRONbNf0wBsQH_Nj3)ns@Ys(NpQsP5{H3AMZ6l|RC0A^VpS&w!f|?ci96L%A;M zW=KWtNTqT<s_7RWe{zwBR@{PuJ}%_N8KqF=U<(fmN~})YV&|{Uz#2~6J?-lIrxL`N z7dy{N^)*pTwngXFlKL?6h98*zh~*2axoE7HA`aM?O29{QZYP%9lGXWh7u64P_ei80 zuZ<rWLNIXO=aonlsYrpI4{EZI%2V(lDNvc-r&MdqYD!j7$EcvX<H0Wk)1{1%My+Zr zOXjTwjjYCaEOM4J{*P01s2UY+==CSu*wd~&i)Tf(kOD~MzxHZl-9GfyVO^!GbPNRb zuXP;FjR0M+#~3gF=55mSb|$3pYGmGBGB`<9wKx%0{i{Y?`jG9zo-+N1l9#7eNA-Ql z6}t;Oh7BsYCy$lX18;0^LcZQwyth5DWZuWtjW>3i({9Q*mH0fJ<&@s5k)HF0_$0xJ zstdsl(n(uSpx4!hej{~eQb;pYdXMZ(=G8^}aDWp}-LXfj|Il{0;>?+Yg&{ra3M&gA zNY?OgZx8+J!sO&WAlJeSMFGFS9eFfO9nx3qZS<Rq7On=JFmwnbxS-#tVhRyUAdx(H z<HJT$9KZZQPBjHjramyEt77mueR#QrE2Lm0YX_@fOYcc}wNVGhqKrJ<+*&N9(wW8@ zXaH;b+2{<i%I_b<MvoFkqpQG{ZG#Q6X-B_suF`cN|JaJA0Br=BDbDw+ryZenZ@LZS zwm=#miMppFukHm&l+vw5S~^3`{@Rr$t{q1Qx8nj~GD%6ES`Bbmkt1aZCaz?M>TR|} zevu$C)>L=<l0gVg(6teitJ(0Pg5H*V#R7;n>7MB;d~BvRCH{aI=A)JNf*%`@MDxk} zWA%SHfCNcJ^5v3a5PbFb^MomC-Xs8WP)-t63&LRp$Ky_voU*CifaWSNz7E9oCrZu6 zmyE-rRHs}|^Cnkx7j%<l=Nht|;eGMS@(d5{PyRV`n1`yogoo5i_id)&kXnpT7XZJc z&sB~qx-lR<5XzaR;ADOLUw}3!z<DxxG-+}oa3k+#Uu1dtrF&<d{be#gkPfhoOVY;P zx&QGPYhk_RVS}&hv3b(rEoLM*;3~&JwySMabZYDCgZ_^8<`?WX2r7A4`@M!NswfgZ z4bT?1`PL?^(jw-8Qs4s|ORf2VoB&RkY=M&35;&NoyJT~X<%KA(;Lo8BZ0w5-1__rm zm1xt1$~uas^{j$Xz>JGT<HqyQiiTrT1X6>{#Z2hM-4_UOJr3L&d&s)D72L~>{vHTa zQJ72`15n%-k1!h-WELoE%AAXED=%T?Ln*MPn7F@e0)-Kf<z3RpGAS9JApu&abnzVh z2UYEhMf5T^Z55Nc91mJZOxD6!LJh8Wy}NZG1Fam>&gCI9Oo+eZZ`x9bc>UG5lYvSn zs=?d>^wG}+JsIciLBeMxH2$R(WzIvfUvU*E)qaUt$0o7!cOQ<miyb+g)(Z4iEOPC$ ziQ5J8(|EM7Btga#<)a1#JpqE|Ic7@!m=FKrPR;Q=Si!-0!?aU<V+Yk7%}Ih7t1_=k z9LWO+Z~ZC3??COFjF6`xNiXRrZ~W!!PELBF+6WF+sUdxeePD3gETh4-vN~jjkp4#& zO3HHH*0YCA9EO659z`8j9afhPqK+-jZ~=}$`B7IJ>EfEEe3nB>dj*j8bUYsz6$KHc zYPeU~VW)Usc1B`5wpiC{i?H&RqGCk4K6R(#9jS+TCMrc2DK$r&4*aPLuiOP@xRRf7 zPUu1|?dL=Zl1~sn48`u&$ej#2$`d#O)`)uPg=hr??9Z<cJl}_*uqJNL0qqkoztlDt ze*zI$3CpI~BHrQbKMpVjHL2&Sm6YyXGT_wlke!#p@cP&aGnth-Kt9uV^g;}(b6DMa zG<U;Ej0?+u1!JOlH^Kyf{!!@WRs?fbRj`eA)ld&0)uy?MwN%dQc+<tKL}VZ9*~DTe zJ!JRhMJh|2sIBm@UjzTWT31bE@A(0@U3>h*rqnTm4)kY)781ZaG<3&PPc}uviG)&^ zxSF-z7(gn;j6>f6YNul0=^rL`Mz<kSkP9}=#?sgAuS=n0xPzC@4{imMEWQ+;Jah?# zT27xbi$g|WCEwu>JrA{JAQ3%32W3uvz<Ozfc_=>stOkhUn)6uQrLjSXVdl289{&~$ zQ51_9yLDq$;sMAO*Ix-aAGwuZ@dAC`<`R^W^Y^&+t<LiRIr@P>p0ESAw!sa`XqTI7 zF8Dd^L|jPDHd53@f#zzAD`}!>zOxher8u9IXZf4%gYxVwj-KA7U@C43K@qmwA>ayv z3<Z+Y>jPzP1;D**8L;;nf!vA`;Og$UPtK^Jvm{(GH3JBtX5TktV@yd4fF)8p_Xs<S zYI@^ycFgfpC6^E)E9&fkZGZD4&>RE@6<GT0K^o5#RFO>aynCUcxz&YaYI)Vzc&+nT z30(cs!po<EegGhmv_JHNraoG93A9Llk#56#5ltXt*q}5C*v8K`9|Sx`Bx;+zfxmu$ zUpc;tYygxoiVTK8#S16l64?YZ+Og8Mu`Q~q@a>`{xma*tdA&y262Ups8x6SQiZDF- za~mATGauI5wyqHjjcHnYgBJy844K$&Ea{Wg8D<rZnyS#1)fK=x&TuQj*_9%Ytp42` zW5__7PfY8OdlwlNOB5zg4_IL{pXEK~gWS=h`~VemjeGaA(VI&tkszKUSPA@klK33W za3kCsEdoq+Q)Ts6{#`a%0%Rr_TqzKdn8rhhV@pU7uI7C`VR6hsBYLUkZ&FHG_-jJ9 z?PBz0q_$+}YBKH3cB2gBPJR#zhzMc!{NYQ;n<SEsN+Q|m7D{)k1_7}g1^ZB6js4~T zR6wjS!dU`~Fe76kKkl3t^_xH;C+Kv#(yae=!DZK6_yL7F<M95tdS(0)3MQ1SUwKSp z7fS~nME0ZTEk6I5*Or?#&|kG(H`;m9{sQ@7l9-it#4O|$hpyx6+MGEmdE1ndDhuBg zjtagB=ALNogJ8GrW(#Jn;v(b58;gE8b%1Ih`kS9)g=Cg<T52V_#-I8sDmHKTs_Ih; zYSGo>Yy++2f#b$*WF-K%c@A0Q&e|kQa5)9KMv{&F?E_D5z5*73r_c7E28E?mMHAu_ zre;@?yyG!8)2BDGV+>79T?o~8?I7vmrpew|H@09msRUriv%dw;Kf2g5DK+R6MSxW6 znQZEst@Vec+al?sSY<0lgc;o+2N(Rs$BizaW?sKEFX!GWP_363Dqoz~9j1>D;ue_} zJw+yM`T@NVzF!f8dSBkKvki}4OU(U{nL|MOC&R|_P*DWzrKeZ(GLr<Q1G8jBT_^td zllEMieI>OUj~h7KTVS3=kwnV)l>qIE(OmCh*Q{+I9lJv{oRSBe5t+BFy=ASFk~5MG z=SV3XTsLMs*@b^v0mwx!qrD#`mab;xYmx!c2HEOUBtatbET{CnvTO}swAP{8al^xw zM{isrEQD|fdKpLQ#8R<YT43T~kiTy+lrPMFzAyta6&SBDiW>dIw-Y7&F93$M*Wt;_ zsVTTWPTnvJDR-N#Fu_L;8E8&eD8S<js)=TRt8+KO9@@A_T&z2=LjZ|05sFDdm>BRp zd7HXoPg99Z>e#nx#`L3=5r4L7vNomEd5Yk$s0Sx&tW}MJw%R1cYeYvr@oKUx<7rZI zwHWm;LBo<Ws`Do~@&pF2XaLW;yn05;^GmpYbyPmW=E{Xm?;*9g9@7mAf@t9*6!h1j z;+oW)uH>^7NU>2V_|3IZ8bO9!dr`g42bjBkAAm?ib+2`W>M9cQP)NFW2>noV7Dl{e zIMkz*_<Rz2ozOKvc}9$L=-#YB8Z^(k3%^8;GmKxrGx}=^rF|B+1~4R(ye2)txIatN zp&uYD8fVlU7tFL2!S_81r!!I8Eg=PK(ko??2~G-LbISBc&zy`%3w>+1V|+>mPFP)% zz%}#RRNvqQbJ9L1ZD-7V_G$8ZSe!N$O*&&Fbuz0{3}|mbHB~Z~JWk@udMM0NQ9fMo z_(>^LB*B%kP12bt55W1^#)=y?i4xKbyl_dCrBDE;a>CWjTKF-8k_{ByRDn-Yr-5}) z5>VJGW88+8e-6FKJQ0ziY?|+8eNZZv1_kH8v`9SQc!XVtGI}@fa+JP^7xY?<Nx1@) z33ZkNN2r{mzgfKK<u%>as*8-X->_^u`v(geUCOMHTnqt*9WY4>)Dlznvct|1bF*>X zu6xs7D`moxQ6$$GkRW_D$@UI4-e+W2`VOsb*OlsGL@Q{(aAc3h1A$)diE?k!<aAnE z&9$vPH$_?o1knTuJC-UiJSEwg8#=%-)=Fkb#NmK%X~m&N$Hqwt3_|KHKkbo9H){a> z3d7OFKmMk%0I&<*u4Cj|sxHT6r7kmbtLgjF6TAAO;jL2#{533cNiPzT+uWX_D3jaZ zF4`Jt%1SWtBL>2DNf+-q2+=zSY_a1dTl&{*Cq{W5=90HT+-hbHdX|p45TJ;QvK$|~ zS4fAtzBmbU-qm8SttLBhUUOTdmB<^8v1TC4YdY5b55N)IHE|zB-%YX@#1vY~ChI=s zETo&ftz#-=g|H;oJIY0H!mzh;q(B-xX4-Ti*8HFqUpsM-{&^;j4EKb6Wg#K}UMFIG z9I`y5RqxCqcL|z3pWS$%4vv*L1d6GdYr3Y?a%5u#*jim0G8ijH$ZUYcH@s=IMgRL3 z9KIz!2rwp04`bo*HRnp{W$e&y+VPnq0Bg)W3li)&(tan~1cb-A`>QX>)rY^{4LFGv z1cDQXzhBuu(JdLl`yn;PD3NR~MB6*%^V2OOg|}T{vPuALf2*@zU|p(kIZOT=y}oot z(iG%;VHsyIv~nAfTU^X@P4@zb3{y$Ui=|Hv58(2uN(F5gs}iIew5W?7)q1ygW4ZX+ zV>&9avQsiv)<DrZ8{7v^boNjENkFf{(y-ZwnyG<LHYtRd1bd@H*dYRAHsEg}Kp+TJ z1Lp)|k*|2EZhih%bRtTq-TXwQsG<tnb4}+%Udkt-H^4%kea`6g>(+ds+q}z|=Q@Ib z0l+Tc&7n}hxCUhVpb&fzPPMCd030cF^;~}wCvD;eaY#;ddjwZ#lW1Up>OXSO1UGKQ z7z8z?ivXYyFZ3jG7wT*Jo%iK^H;{!vpI2Kf+{GE#x*Z6vbQB93j<|byP^y=f`s{)w zBYI61&pe*FDdMYa7kpPMbOCV*-cH2`4QMiD+{(y-pt_6!+2BO&4~7op6ATDhg&mP@ zG@<!(p0LsAzm#;2&7E&zvFODMk3;EY`fNb)DszSMXZ;Xh-w*)Zq6&sAcz-+<RYoNc zJq6pIgwA4wW1Myw@OzO$O~PNvgK)U5WK2*}D%oEd4;mPp*ntxXdSQ@9#&01x6;Le* z-}dykgPK*M7b;W5ez9(29~=dX`SWICh^Yhhx1I7x#P>e`_bu!qSEh0@gdkncD3+u& z9JA0~Rlf}E{^U*K&{6EbzF&jx^1A)?V-|@rmg$%z5e(cI@$J0uu-U?6L}WH=?w9=V zVqr90N+=rxIYjOT942&Y-y*m<3ZPOifl#qW<7`Yv%h)^2<TPi$tDmEZv0uR^ovS>C zvdbZzI|*^hEr>JOSx&N=m559DK(F7eb(kc?T121Z(>iqinns04Pa%0gPD+GhB~;QO zIof(e`F)?~s_$&{E5iw{9+r=Ns-sR`iB5ewQju{Y-dd5Y@gvTzFcv8j1mFmD;GOM{ z<HR;G$SOjS^RLU7GrSl3?47iTVOZoQ$lkKSYR|~TQ{a#VDm5i179L%<z~w*QQPmHL zt{(5iq>Ulx4v;e)wt07JQ(9}<#=rD>G&*X#!h0m8R3-Zx9+5(^TcXlNkt#Ld+xy@L zpKDQH`U42I&FGHtZs_pa26W(M1=c@mpG2&Er_RQ1e-v6r`SMalDet{Q6ux1Q*8sRm z#g%TcYIkP>g-vhvNi2w6*6&0s&HWk0<vRKeV0Vve2?hoSqH0>v5dy3UE;JY+Jj0RG zGqY$s*RB|PvxxCcY@BV^B;`Xyiht1UG>{RB_gG=WBt8nN;Bl;@01lp5Z=y{fQ03n< z7YJiWY^C~N0jpQ{@(P|*?k3uI2c#S<z)Som4a{}!%NpC}6C4Dzk3|IKh1=sU(w4JA z_V88ff0PjRlkYrZD(q@fWKM#6k>!>39ME7}ZO^AnY*0xyUY~zbRNl~*nzmkZFdT7% zrvlM=UA`1R9h-!G0Dd9&R#=%F%V|nuM~`C23xXv3)h^5s<i8kNOtt-sbZ2Ks!01Lb zIF_=vP}iTCQ(R7sdKTijhI)r;{1DTpH3HoZ9^`s94E2c`j;AY+6n(cuoBjQiIy<*+ z+DrED=1cb&;<;umEaWJf{m6`68%S_r*GF1lB$<Xi$U_^!0h*K?%7pj@A$hj%7_f#W z1$iAbDG<=JLu4pui6P*MG`Mk_MivA%J>ssZBKSJ$`Gy{LkV0LK;w;H4H0yc4!u|07 z>XDodes7YGAL&`6sqyA)xZ3xgKlqmzQOoF8MVWw+!-FeZD9IKJYUpUjvVh8FHk)&~ zj-+z1D%z=<16VsT!B8>@nlSEc7vCgrZVq-34q6LyeK#2LmCRptg1O&&4GmDU8CLKr zd{IpMb^ZHFiyDiwB_IwiW#BW#c0ygeG#tJL8OZTyqTuc5SgDi8iL03QGpB0CZHNwK zHsy7=HTp`wjLRBV6m)w-Dc4WgZrwP&tb=2<6`sbu0F+7xd;Qr%g@<@1ua<~2s1eG& z#dC%Hp~DwFGYn`x`l9A({hK~@i7KW|rKw-i#=rwSyrolaFL8LNEVtC+V65h0U^mCZ zNBG+JaoE|HvWGIs+@a<XEeXnMa;LBKjLb=sF?O?@=A-9{xLQ4q$n649z*lCX(|jq~ zmLO*WfMOzA9F4kYsFhxmXhfmNkjEy%OrvK*O$u6E-u0*B+2n!o0z)<2KXzK8WEY>x zwyLV#syUPSdp6uIf#<Nu!4P;!C+9SaZW@7@`VEF`bb8(rLYd0=y7BSr=>^rFK1eZ+ z_c6lkYU>TCr)#ejpo^-{shaG;W`52Y`U!jxfCz2yEZzk%%y+v;XRS%8q0$-+*@Yg} zT1yjw06NUl&U`_jpLWrKYFxSd;yOJ=auT@6E!Rf*)^Vf~w_GDm)1~A={Q1tq=Qx7c zBvbh5-^^h=(Wct+;<>pfEP;U#roSwzhD6v^U9ImtQCB3pQoPTaM&i01L9`j1#b8+} z0VBW~UJjCU6meu@i+*?w;k!NW=gx4L#!6$(CGf_*5D{b_&e)D7%-~_>yCcuTRj61$ zUEAE``%*<IsMNxm>Y=&qA3kphOQ<j{<$>g*+v?gIrFF$eVQVQm*<sh`JjHxVRPii@ zYPQOMTn)ZnSeb!H+7Pw~X2YOz(b7rl0M;s7R<Kq`G`hnE$W8v+<)Q#cQiunj(!D(@ ztpsNc#A;_MCEEg`Pz}?$ae0&K#7PC}u6!*vAWx6XH^x6=t-FuvzNniKU9Xc)#qSmA zBrjDY0@edvf@u28Y&v9XMQDyL++azix$~@T(sG%m$AZb9bM?+p@;Ceq8#Q;cfS?Xg zE`BwIM9rLWWDzn+iWXT;C?S~BmMl6MrmUBK?`A)`VbXm3*NifP#mGV;X7cOF)E;`# zh+p0uX7&SyqxtOamxh~GMMd+@>=L}!jPVO;;-xHlf^^n&KietN?)^3nUd=!H;GE8x zLto(Jaqh4q9zS=SxD;`ad4_OZfP{xQB>UT<!z3NCzPq~m&9OIUr;VN+0|xTBdNm#P zk?;qE5NQ=u6-E-0)*}~<j_Vv#H0UorT;%-@wHuvLP5RaOq|6Mq3dv;7+jUM(YePt^ z@s}&cdKX}R@vDE+9ZE3*XH0AoQ??{ms;NIiN(ZJQT`ktw!(|ue-*4joWXx@X36o#x z0e0W%n?tae-hW*1%iL#UN{7e&*=K}~JCpT(4-|O~z+K>tc7(gyV%a5k_+)%f&vg-x z-Fjn7>(FeLB2^WU;<e-;Nx{Y#6{K868Ril^KJST?B1m~LJ|u`LgUa88dwV4Cs~swk z8=Q0#e@b!ogQ}>8g@<fY0kVcAe#DDaSSrogZg{^G$xVF%r5+6%Yd$I&VZS08CvPnm zwcR31IUU6ffAk)Ht*k-`p)8Q&xzy&Awzy$kIg{LI)LED?i$I|K?wIx+lqrpZPufDY z!mP$!lP&cj!=^V?pdyP-|DMdzk0f&A&Nx*GD%VT4I%Jv9SUk@e0cdz(i=5$QhCj2( zE1n38ad_1<7b3WN{Qj*=s#(?yIt>3_A-=hjT%CZM<7RT&!;9qs4t1)INuu?#jDrVm z`F<6u?obcP#XM|SWSuS{;R~7LH=+jxdXydU&?ZlMJPDETT0Gl)jFBjE7z=rfC@b{H znx2?dXUK@4>vZGp0svK*ro$sBDB<v|3_?XSGUwS8bnS8`3_Pv3DA=^7gap2UxQDKM zv6(xEFALmwWJ5&XON)-wTn)Auce8MdUb?c$-;nXij3Y~!8zJtmF)qj8a_p_@(s{G> zMwaow^$;NcIxWG_=Al%c)+3rBl0QG&gVb%WeY?zi5qcb}1vvLpw6%AW7M7UM?TWh4 zpoc)CILDXC)m@sD&`R9l<NoMy(FaE!CY_4HF<zi4A)?cbDg}he6*|sKetV5>ej6&` zS%jM|o*%RhbiE#%I3CDeUokBx?#h1QKQIXASJ>639XKK!5;Mgxi-alfE1a*Mv7UwW z^4&4!5QB|T08j|?&kW`FEPO1YqOC>iq$gWax(gLBRjZF_NO&OYH&a}3*o_#gSlp>J zh#9i)X4P9t4DIZ5-FkKijd%>B@Y%r@=G={^8p)-Qdj90CLge6T?MpK!CeTt%j|#os z8I{@GBVt<ZHxuV$TpE{hi!$6Yh<e3?OXB)aVT}vA050!@xuW%_1zjtn)U)QC%zNpT zj+2Rj(~CN1O!nM|V^96i?;xU&%OU4m1P_~7Lt-MAu0rQH39FJA<0U%dkDKQY_2*BE zP1Eq;0Dkw_&cLLOLZH>6F^7*@2f|oCvQ_d(3QM&Ap7<W)h>tW^)rizuwy`Hx80WAj z4LDOD08pL0r8<<#DBLXC{5Z_uknwjLr%lntN=z#<TqfP-R8j2i<3m>b8OC7+&T}pz zMb@o|1Tprzy7{2e@uR!020pSy2)7+P=s0=Iv~|qy_X@!->~`O{SP(uL=xuj(4KOwL z9aYwqvL+0})!QD%$Zl_QgqJPu&5l#yX8#`w0Ec`8Pmf4Hiv|=52hRGnqMGkZY~c`i zLk&Mr^Pf;AU3@X-6B+V&>&5gAnxG?OYLG+<>f;>?$VSP+odjqmJhk!Z=MZGG=6h6E zOsJ7pWr1uidc*9o!)F=9_qQGHKxWv@$H#@or;qkB>AwOS11iUo5!}a<^)9BcR*XjH z04c>r;71CY`ID_aXk{0TY0i_s1_-p=nR>E)d`i)cQ$f7FV^Jna`|sqH=D}@7h6lyl zE`spZzx-2D0-EXuxos;oN#7rjQ&d1?hz@35-_<cggap`o$2a!V+Rfv+jIB}_#%C7) z%tBU~%w8B`BYhE9LIF$hE#;0XKGMaB0LG=;33JRIv-VOyJx5C*>0peJC*gjEf)a5F znDT%Bd`5@4IW&;6d{A%#xYbdY!|v<Q)ppb*-!-vCS)Fa-S-+D~d9Y%4MF`JS_y0K0 z#0U0&XF5QJ=}Z8bXVXF!g>(=6l$M!5I;$|TZ>cK%qYLKVM~XOCPV@1*Zgx*w0h+VJ zizcaXrHPi+{CMw`(%!O|DYgqnE62M(Fvas_9kOxP*UDYLqNmWT_}?7Kp4kRIv$8gx zrfk8f3uhMRSu9ztFSNmTjaCgv2i9uZ?>P08^5ACN*P1-!p&PvQRb*}iaiGEcwmC|f zAiirB?;1@M>CeI^RU2FNmb=0^0aFV@Ly5J;fkH?3AodKA;geDShI}1s?|NW9>txJj zr<|ZCdkuKcSsu^8sIkRx#y}5f1y+y*_>#P+P(WBG(lg3UfuK@l=KvkMc)so7j}^!V zB9(m66FP6MAT}lhecqqN{G;9~@VrchtNV)5R8NL(hc6U${{kMk&)M`afT_R=?Tm<4 zYxIdxnnDtYQ6AdmxXrH+myMykg+)+H=F#vk;3H5^OUvgx*El^)_uQOqZhOR!90=^` zDD|NQ^BH}=p9N<6xTI;r0JH9QCpMT6jgm9H`1VWP$$v(SUl>iBNaE+yJa`!z2u`l& z0diD2NUD3xNY;^s-E>(mfT-DQ6T#giAuTxKT!Fy0T86SjhNdZM#OOj+(<=tY$#0A8 z>?+17^b}amxJ;cpltNZN16vO)n&v)jN%ZGYJ~&atzkJ(jg6czPE(#{OKHqzDO=V+$ zcgW(pg=p^{t}jO7PR^w_M{C2d<p*)mVNpgMie+{1{-nDA#cQp;0^Z}B?pEmI1HXIP zguI&Z%a%22p(p9$nfqXP$;_Q-Kn^KrDV$W@o3Lp&hFtx=CVDPqw~~DB``NQwM>SuT zV;pk0Gz?4qOL!*H3VK5wnHRr`zp$r)4X;T(^yW<mH(UpkJJ#EiW`N#O*80b*&9*jh zB7Uj%|BW(U-Pt^A1$^L_F^v3dj&MA2exLAl%kRR>cuoTXZ}kHAkHXsS%I`eDoPR7^ ztpki>b+bnPKCt<Y^#Qu4FiichAoCB73o@wZT)v#HS6w@6WnV~OU_=L@E7l9T85f37 zYtCJ$Bt3L-erqRI*P!eh-_r&3Sq_GqVZ-5(W%;C$>-%Cc19Fu^FvcMw(*)mIph6-s z_7k5dW>W?sc-}zd6*B_Dm)+r6;1EjL1H5%fo#bS4t}>giioVd^ho|Y8t1CBVMJYDt zXxFB|Pn2f8X{X@bG!_4Pz7HK-<`M6k@y0?V@OwQT9n{?@z!+&l_Pp}}0roF(fHOT! zzDdqW3De0S0j)c5CGEI1i+c5T)-%_e>%&4JNni!5_W-B4-bve;z0X!1lAecs!l?90 z>2RX&V~#GRJS>oi-(l6cM2)YE5PBm}tn$kv;Edbs$amiX$xR?H;-Zpbc8QR71(8P| z@7S?Z_^zc^mn9g+W{fx=_cz{6Q<yS!zwY+3e>^6Y0iQKMU<oGBy#t$^@-xP%Qd*-= z?0*Ghc*cwox|qEmU0*tFrl<Te4Kf~ZEZtiJ6L6tMERU%0w{WjHyt{`kh4OMVA+(wg zaj7S_l!sg{&z#mDBns=T-+kj93K2?fHENlPzb})4*r0=d#eF6%b64nAEwMGPQN5K8 zsjHsi14vGqd3;b?^Ii4PLDkuT#F(~eZy&BlJ_cT{$(r-|8|dIt)`*fF`c+MwfR9%l z(Kf#E)2hu%sLCiW`Erg+-{VGz3`|IzY;+K~w$<}?ksB7z1k46vCl#^A@<ywqV6t=$ z0!+^jXFZ}p%BV)QgU8t@*>QHguZsJ-Zj-eN0HhG`*97%Q8ewV?SRT@=^0=8-f((Wx z_eN7KQ2|!%ZssRJvMxqUG2;lzx9s^y_{!e}P6^f4sblL(>mEmr2N3mt?}FJQ&6Q8! zPaT{WK4AL7BBXk<K!u^C2~!Lh&$8(8eeuIT=D7(H<dlG*IZg1pX0iA3&fKWmBeitd z0qBVVf2lo>^X?!rh#{>a=SyE5pj1<it?OyB;(TM-zPImvy?jK^cf`mCh5L%Fo6L8I z47f(<SBv9HvV@%7T>m9s?oWgRxj4zqbM+z?CUCbm>o7B%b1zcb*97=dS}<tvHy=Hq z!?iVp_M&jRrh9e5W^ZKU1o+4ttZOBb0mfZs2JdSGxU96HwQ=~q53l-iHf)_TEN(ri z5H@ll3_N#-5$g2*nYfy|Rvk}Zh-`?>|8BY&RUZs}9f(5^W!Qjq4{{C9rn?M&j20<_ z_hPx=)W6HS0V2XHCaS#1SoGz@tXn#FJO6(YDp?Cnf#aWZ1om%XA*d<ne?=uQrJyu8 z|NEK7-3Kc0-$g(lC>!v97u)(l#s3$g8q+b#42=y0^r@RxzXkgHFYN#*8TNnwQQrcU z{_n=pEl^9a|Hb+m=7UvW{sSuB%Kb-hf;0e%o94d-3X>*&@Q<qaWxpYo+H+BtK2JeI zt75kx+5mB7*|0X;g}ox{W9bwnYQT`p(Ws$Jk|w;S*!8{h;yX#sk4o<7AnFK$teu^~ zZol1m^KvSyd?k`-^L*8vAW3wutIJv=G0|wXz*Z;4nsI6_>gt-3rhZ(-RXzr;URPBZ zZ=H!#R`mziaeEv<ri+$H{wEB3IrD!2T0o`0u~+}NI7`0!;q1HrK7amSbMdr1%*E>x zAk4)&Rqce=)Oj5zH?qvCCa%-<b&A`^jr`|6Z{$vvt+IQSH))Y)FWt~F2>UiK?9%5| zvxBw2$n1H#lem9bmHC<;B_%vM2I)pN3a~b?{+GY{=;tMnp-hWdzJFWGnuug(PbUIC zKcs4V)-+W!r?2OwYSW@1(*Lwy)lIdQN(B?>sCu5vE(jUPgMX84nx$=eUan!IT(v-n z?g%ZPPi8|%?({5!;$KrGp8=RAU+od2!8KJT3l$f6d2N>c2*1w-K2Zr?32Yf&Nq6z> zR?5Px{yTmdnhZA|<A1r@X1{K$rbEb_DU!gf1Q#bV%Ufi?`S38HCr8MD8{s^?#&H>a z9p*B-n7Av>V8*&l_Dzv&Wl>9qtv6!uxE_bI%axU=(y(fx`3@#;D$Q;23hv6x{4OoC z94-?0*~4}&HVC0*ww~mhP!1PFCkD1jk-`T9?|c3_nV=4kjel4gWe{J<_D6a)O$cm~ z%qA0L8;b%=CeeOF;G?HSBh&0)`Pa^i_@1IOeQmt~<SPH+<z#Xqp59b>1}doB6glW1 zP%*c8yA|zLii^X=xnNn9BDy%V+p3J>$;-3<d42x!?5E`7AFqCR@jN+ycJ}<$`S<5% zF9B23KGUvBh=2Z*7c*@%LDg6M@Xrw(c#_GDNL7M&kgN@kQ~TS(-N;OQ^=A<h=wG0O zO3CDpqUaUi-t5aZ-$`K<OtlHu#4wNMhdU<gB2`K%I0*{{enu%iw^MpFcS8Zy`SFYe zY9n$*5>l?cqs~WAnk_8IMuM>^g`G0-9wH?)J$LD~On-*N_L|Rzzb&+`Y2~gih|mK< zN+j<;XM_+rlC9PPiLK@kk$DDZ73Ohd&Tkh0cJ6qnSl|LcO-=m?Of}GUvNsh^5`#iu zxG)!<4eG0Du+m-99?p=ceO(v1%$9c7;IW&%u69xpP&d^*3!o;otiOHo#{i^$=H}(9 z!ZB}S^MAzO6ZLiq+Rfk7JahA|ifWY>NyVnp%?+Qs`CBlb{n`H00EbO}l_=SwqEG=M z{y@OxXbY+)2W1D_laX7xdk+TOd_66EDBr<ZmA{&w_}q^Zk8;0Z!OtK8N4X!xqr?6q zY#L%{ute_uEwU>Z_FdlELnq|Oc$_sn{wrtq4}av;WSCVm@Ee&4@V7<Jm;4l#{aD@W zuOs2<F5E<Sh;3}60CH;X#b3l-un_tAJ{s5PBXE{Weyq*<{c%zHe~tftCISiqFtq_1 zCaVKn4PM#+_mgWQl0KLI6}(Q*(V9B%&w6=Jo&N(K6HXVfRa%O-Z~O4xzKtie@YNRl zkbg$j;D1v5n_34K!(ypg=l-^71I{^V?v+HxIAuRjM?*A6mN$7*l^h%ZI--;^Q=+Y4 ziP*??y#=)Jd2XD?SfG1~2xBS$K@H<82v1dG7z&CHBM-5w06*|#apyvUr(QMJe}Zp0 zYZj)ad{<ZK!KO9DzIBt6?^SZ(f}SwohJWh;c2$5FX}T=xv`Kf`AR>ljf@?HwnOQPI z*xrGT9O^cQXb=Q~kmfxAI1!4r$UOZP`-rJ(->hZQ!H=LgHi)!4(MlCjtP%5lYx=M% z-E3q7$Z#z3_ASCe#I}-el*4cZ^xo68(L)Ze*C||Q>>bgpt@cGG>I$UNkjh&yhkq}5 zp-)Vcl%8cF@m@frs?`WgL40FIu|Cu}_(@l@*l_;jh*4nXREgy`5WeqI(6D+Dnj07d zlbSfqsARF3`+CwiMQ(;q{%{CIdB^~{89EkTm?+qdpiW<OGDtd_1HAEvo2ym+!X$}? z=8?^Fv7k9yoOB&ZH{zWP?VkJAnSW;j`lUHeXWZBzRBYi09uh_<XAq8<;B;vY7mni% zBTh6IG!{sLoX$EhP;V~;vR*(B|GI%Y;I?r((FeZvLP;>U1dfe0|L$z82Mbl_LxCk6 zx+Y0%QXBWvJM=8voy@*y?q|UUWQV*Ba^tcPuWh_jqm-g@4)5E;!%dwG5PuB_g+Hd# z5ZAfKF|%3sXkCzuGx!^f@my3O*UbVGvV{Grs)_)8FXE@EW;?U}Z15!AOrS?Wy`@ID zi?1j8e?fBqamv9W?lHTgHwt%<Q0blCmyr?3@Ze|6(c!H-(4m|==sIPivvX*0NMy93 z)3E4wr|KKI!IWTInLJLw9)GkR*rU%Hfk;{@>$sW2=jNo}@PWgF4haRp@aZxJN{gL? zruE%`6xi(f_(`3Lafj_Ho8XjY(Bh+0p70})m_<K+%xQqbF|{J1&eSQHyF16)@doT> zQm5dL*(1|)ux~g-k%RXfHHiT?^W;;j&ct^v+$p*i)H_lN%m|ci&ws(`C7UKQDkMy! z7jTLu&(O0HYQG}kjr={q@UO2EQw~UMHDP_wielJ&4;V0D@b3^9d4L`qln)~t#Lwtv zdg)HO>Z8(Olqaq9hQ45by(1JI#YD6jDwl*D;5ZR2<GeQ(A?T?8+@aGS8$d3cG&?FP z1IIR_=HxeBk$co5Yk%lNr?$pX&zTL+@sXU3o&7`*69PZhvdn;Ga*PTZDX4(uO?&m? z*$Yg>kfeiUTClIWlWD2MEwBm}B3sE0dH}42FeC*hrM=D9-W{Cas$F^zw<){C<OoAF z(V%j-`Tu7oD8x?-4q|YS0@ELKP~1Q9H=cIJKn!j`7_)@aEPvD4%(L8FHyo&NiWGvS z<5x^erru=|_2q0pyF5gp^v*cs;e~3dcMe<b96<j>TrefPS$Js>UC5?5Q2iL))OHIu zw@&wX8+pq?QU{PGsX~p@0$EA>=(urTP@RaZ0dR>@7(T--t8H}~#5D}6f`|*uHmsoB zj*gHp(GmwUDSy#qsS!BR_HO#OI$G@|`n+3rDTJOzwQuWvi|!wU=@9qnYygJp9OA=0 zS0by*(^h2rx&SqUL3G=}_uV=+)LDw?CkYy5Ro!r?&RfLMz!5sW=n+eSoX~UWD3xN_ zuP+;HUNDQ1=t{p@kRQqkLETjF#uo&3_bwNn0H5u0c7M4#)jHvI4~R|PD3b3@niudN zW+zGy$iBo(TGb$Bt?Q;p@1|6Cq;R-^8v|nBmPM6j>h~-LsmlQ1=a9?7C~F;m#gsKQ zM@(u$FY(ZuI*+F_%cm(11pZN<rZY4o0ZG~S@Wc}@y+L=t5<R$jJ^*<*<ENmO3{G9D zTCS(4Y=4^B+>;1T@m$H)&lv}&9?AV4_v?e4Tu`-~zCS~!;}-DDOsf8I5)a2x+`pC# zLy$oTQU_%B5<t~Y9AL?x>lleQKJ~!N3GKj(@ga@Iz1&<7XL7Z_nnte`>Reiw>4X$h z_)8VdeSmQoI-0AXYJNB=NczC|kqUDW6xAGbU4Ja|%6uJ~VUTPF(bH$lDYsg+;SdG^ zAq9Byl3QS=ZZggcX=-PiV6fM>G^OFVzsyv;Y2DI;GRJ@ifBB-hM_=;J4dD}15%eUq z3MZD|&}HIuvoT4u=Hl!^eEao(=*AcE`sI(Iwa#tOIL;dGxGo~p+v;-~ce1xkAGh!1 z;(y0NTZZ6!lV?O6JsQ%-duT}{h>j-rq)+)EA?Pxg<)Qb^LMG`F)|UWmLSl!X*fFSQ zAwI)Z?fyN`rYx6Q54=AKIe-rDa{S6^D`Q)!+o>bf3?%exf_j|!oQNOgRSIDz`|^AV z<Va?fWE8e3I6n+9{#p6v+W`r2L5APuV1I7V(@2~3HouWHLggp`yFs(rhckGrp}w$T zrVUgr{-03B+*iuW;fwFHwdc<5Vcv8W_stxBaGPZj*tQNTOCPQ9;^DZ<elV%+$#T;* zdiX){S@uI?Eo)2t(+xgG!bGoYtP^i6o*C#m9_G23KxYT3=y`EE`a;LBDBzA(`+vO1 z)bdrcmow|L^qR0f)^9<uzkRpmQ`vQdPv6sb)A0*_<s2P2$X#x>Tx+{ihWXZU=x8Tl zufW|Y;Soo6E?}V2bDU8}+DIOSX~!yWI>V9qsh*%<cC7gY{~PilA6Aibj*cS}eojnL zSp2+Kwksbf3nHeo8Hy4Bw^VQdlYa$Kwvh0+DGqS}MrsCuJEDVfB^cZ{`3_Jj4rJF9 zC1;gKkCmk43vA#)Q?eC7*96%1Bg6?1dGYMULi~hXD+j<R1=~waq?y2;+X@+`k){_L zU6*}*)ufrk${-N-$~%?1&g(>jSc*vJLmXmJmk;{VVEJ}bjeDESKCn<}Gk>I<8}Sx@ zdS%jp?bRgv^}T9^dH3NGAB8EsGT*4^4_Excs>7f@GVur3|7;1sFS5AdURmcol~%ox zS0Uv9$A@mxpr2a*^A9ytjT_nIH(GP+W9K<14#SY`dL%RpKDLf`v~oAKT<puq=Sf9s zfeL{PNQ~3(y)3&;%^gkg^?yU2N6Xn6fX7P_-CQ*nBSv0VBHOsM2b#n2XkG$fmhf-J zc!v(cBc=@XYX;i!rn1X}yY(n)#N_)a*K1A@`wWxp)Mb|e>jImlMNn+geQF+R)cHky zc9`t9wNUEc^H<Kvw6|NXKwPuU-Bi^Z+Wj&XWXN^TE+_rd&BnAGXn(N3H$u&=fY!z_ zZ7K3B4s!u6l{t$(CpZz<6F7P-@pte#@FgyqPT;`yLnuwOC?P6t^G&|aVO@vJ8h(@3 z96v{B@*_uQ+wmxSE4d3!F^{y=zNe&VcH2}%QQgv;K<NQwji5Y<L0;+7Qn4@df9|DV zgP^-KP`d;~eZ{SLdw*<2^!p7BBVic`EMkkk6+u;;_QyVu%;I86br9gO)LV8`zKJox zR|0KM8;M*^j1x47oT`uq%(m$a367GQZw~f`xK3%fpi|_;8fZ~i7imQh;5tR>qgR!V zklqzIamM=4N&;ndlIy@#R!rXje@LC>_yaM7CuFwp<`wY<+<$XK)9i0HbrMHym%5|} z1m{*&I5%^<snSaF47C86vX}DM=zYg^he7w5i0fZHPCRY3Hd-SpGzAzyZOL&4DXMlm zM8k5@lS`Xod=h_6c-ygWE(w+y|0e!A7C*4ywA;!@%rFD{!`0FQwS;98R&2->ZGI!c zi6*BxWL%^WB7b;Q=1Q8w@dc*#{kd+B<^!TKB;g`_Ic@G)3>4f23hFPigb?8k*q2K; z{=1<T^`ZFffk^?sUqpA_`f!mR1F(r0g=rKHT5>U)A7;%4bq|PZAFfs+#Jts}m89&u z_2spvxfGJfyBc2`_;t&z78dWV4D&I)x)v>y-HgTS5`V477!9}uy@Q%zzd7UM;{ac& z{tnEg84lahtA<ory>F<AyOUn=a;*@QrsctQfyaY4NbUiN=rT|YJYe5avCK8?*FfZq zP$Lraf^Hb@_ybeR?h9tCy>DLMjceONq)g$_$;+`Ykd00wxo^S*g9v99D^OYOG!oHV zJn5Cqqkp^mH+T1!;_g0**%h8LtY{v^KUX<B|C%T>-r`Z?OT=+`XVHZqbFL|>pYoRv zYl!WKX$X7nUvSW@^7&g;J)SeMCw;S_sbM=a+7ZHsG_HPpcu5TRkcjX|aQSHMs)lLY zJLS`1B*Xjs_#n1NiBD|y1ep>jR92$;!7y#pgMT_hu@gq_s6k{JvaA6zdRjk0`jDuY z5b+m1QnYR0h5`3uHCq$(Uc>e0i?&@()zi-=z3OEgpN_b{l=lp6&54+z>s#WxE#It+ zsph~jtJau^F0b5$>!5oi(&j+h{eb|d{XZdLz?ocZNTY%zl`%O&$U(TZzOvq|6KXWZ z#($w@IJ(`5eE9qdG=wB8?e{@xaG?3T=)i>T+8}$P5YwNtQ2gg%JoRv_`O%T0JObFf z3AL6V3YHuK3Y0LX!qOXV)iDjaFXc9EO1SHOO*Z;ci7%b2Wg`_Ak)3|2PRF{03Gv}h zVLxrvA0^P;?%Cj!E^E}GP9S(kPVS|U9)FTN>K}l!Ny7(AhTcFmA0Mf~Gd@;A=meRa z14*s9faN*cf=oX5svRONFs0xvbzxaQnlw@BDY*amBBt;8pO_3x*pJ8|Md}qthSF80 zrI9-tA6$cDmxQSQkRlwb<N6T%tr-ZRAh4Ei@C9BG$MK~m=Rhb1daSc}ywxL^wSOG> zJ2%q~I4>{R>-gvZR0kFNkKQc)aM=k1`fWu~Yz#aw@tWg^H40RKgyu$%izr<Nkz{V_ zAI5E+5kr7SE$Iumr)$y?4J95t-Z}w>D$pj$vAaezQV#9)yYzW1V`9QBzGTbOV-1a7 zaNXxc`xPwX(oY=VWS?o&AUZP@z*Ji=#T0+=TY0+a_Aa3DW51E-!}h|DNAkPJ|6I_= z7j`}z=7%}QpZXS5=i&zL?en;A<|VAp(<V%c?%z;L0|XQR000O8ZKnQO-d8pZDj)y= zL4TLO_5(M6ljF9L-{)5#-0}_OXt=h^uFL64mvc7jcq`6k%ihFsO{Qo{f=2XEq)bpV zt4!v<r@QffP}15-K3tuu>>ZInqtWPY^b63s@3yQg*ZX1L^Ri_1cGvX-tJ=03s-f;$ zadDxZExUH$?}nyc=qKxayBxZ%5&A*X-QL#i?Zp~@p)R|o;Y)&1REwns{c7NSwP^TM z{xx*{l>MRF?V#V3eZzm;^LEK4gyDcsb;taI!RPIFZ(f!!zI^%O_pe_6Zc2|{R82z= z>D%jS%U5srJN^Al-whq$BEP*o?D(InUVd3tVwhe`F63ytdRLV9yj^vDv8$GM6;7q- z0i(Kq=Ud(m!c3vPubO)GwS9FAg4pq8y{>t0`hTOJe8Z3S+z)kQI=$}tEsXj%zQP$! z0r|FT>m@xZTX=K~(mjk03lU7<&+_S%sXt-}Jr`Yb&tc7V*RS!(9#{1hJP@J=!n*{l zfl2zdYKmpG+;H9N1%7$i4*g-uzTI%%(1TQeH~wzPcf>UQy8pHoLtigw<k#KsMYnHP zaur{-Vn7bSH!u6X>-A%rqk79%Z-^L*DgMBRYE=!@s}{Ig0BBgl@C|jB5C7DP{SJ`< zFo$8&wVHC_TbdiPRAOdzyW;QEn(%UsUwb`Ah1?hPFx7dh(9#`#Qwi>pWXf*&P_FoY zy4p8G$=mz7@7jPfEwFCI8&NcMd#B-j1%DrD`hfG&ZE2FJ#@!ADngo8=^Ig?*%QZ$+ z-pDt}S%R<?xFAEn2gT9{EKtj(ETL7o>bqSD+NiI@G<u1UOPpO<wW|`w{c&VTa6Igw zI{U*Z&T&vzT4TePchH6Sqm<}Y0I~#sb;U;q(o$0_VCAeFhC_h1y01rgfYbrdXS)L; zFY2Zq4&_kY8Y&?N7)~4qu&Km`n@(TSxAE3Shq$BkNzE7Z1Qs0jRRg@JyY~CG1xV!{ zxxBRRmHzNZ^8%Kx295_!_rj2P$|9W$dPX%2xaISv=hf<9<lh3M*C;^2qnE3Hdcb`o zd`w&xJVbd39x2p>)DXeTN|ZRea%mqcMk&^)%F1{eq~Z)10sAE?5cA+wyY3W()E<>9 z+;>gAtcQq+ePpB8W``Po^KA`1_k*NP)C_bYFsWGANKo%BY`68=-^OPbQm~pz2wd$o z;5@vj_JZen_nyqC34WZ}&c^3|VVa||!Ip+_7Z(?3r-1UEYlEc#rv8ea<t6Q<WeI4$ zfCkE5$g>yO<bpOWc(?-eu8{+PH*Wx(Mx5*&zWTcBT!M?-nj~r(?t;x&1A6@0>2M>d zVYnWe(1`HW6$aT1h}4rja@fRHZcA#Sk^7=Tg?f;~!!`jnU)|P2S>}d+P=Yt>sre@N zr<uPq+1E<H_p_90x6QWI9WNc#JUgQDY%khvN-`j3j=8}84n|PRZ9ABIN%I%=04*1t zw(@8KUBBcorW!qbUEj7{Z*W7(CEu%+66KB<ENrzql*+UMNb7FK23+N9QdLD!+$fZR zXDsFucJ)8>b>@-2&?douT+!Zts|gOT^`Aj^hicHs`4+U3GqDN>8jq;1Jeb}jfE2E- z+<4L61(@N>ps%-f(A@hT62@Ojq>%q4XrEp?=$3wQ5p-Yd>t<E1tGWT7NX(@1dL5aW zHvsVQDd<a9has)zVF|(8Gm^cz+JVL(L_1Uxis9)PjMWaK+hZDkG1BrQ@Jw6$I069N z@HgHHK3x00%gxO++*A#S<mkqzA#=yDxM%O=6T6JCzkvH1i@1j<+9mTbt2v)|Z9}@Q zKw}HU)N6$wd{Y?LjP{ZlJ&alc3)2hG(}+s}Y~Ke%XyabwO^49BwmHnds2ah2^enx= zR_C+xP=Av2`rloDk7oODFgrsaTRB1?orS8;-G+yK-=-z>!frDtMrFO0f>8^w{p@Oz zjw1L|hVN%&RweYKT2O3L01WEqPg7#!?;tQs+B|aVx30m|gyopHP^n#EnaEYd#ZdLg z9#To5iGavkKFlYRqd08EEo!Rl(Hs7W)q*BdF*B?}7gW@Luz|NbP%=Q!ZIM}GS2dW% zp`)3@5EF;iq;f-19oyj19G#`iVB#YJGIK;2xbeom|Iq#k{s?GN)cmQ<eRKm16o929 zNq2e256~yCS2^N2ag<3O1wL{<A!*_yN(6UeS9#D@j~&2ne8;rgwZY`Zp$kSQ*zfs) zwt#WK!SrZ<7E^`X)S=nj{+UuaCozMemXQ)wqG?|QEMmdYhK(v=j7#v{ERHsu%_sxk zB0KXGZ#dkL;Dc!0$UB^$+T*Sh8u-lhQv;0dj5&gynkfNKN~3yC8uF;tJqJk`8Zii! zo}zF<)A-FjeVo<=qfDr$6e*K{Q&5wQaf(&o|4i<Gz>nNSeH$Y31(cmS)3HpifzM;= zl_hCH6<WRJ5Y+eJQzX%ZaJi(VaIM16w49{ZnSmaYVGt1@`EOnK(=$E2`2W_$O)Z|K z-81&~kH7sRSkV}|W0e$eX2jEK$|-}t&=+C#fD1E#p-Ev<c<eyab~;-()h&2B!*Wx2 z=BEsQt+r{0!dud3>|1Uy0tE}$kP%=vlLd#(O<@LDtvW6Z2!dJ!z<%4cm+-0s#Cues z7`Ur$tGX>xjD%G3@YZWq=o$LR>Y0&s-JWt<B9k{#=`J&k1G>gBQ9VZPPo9K^pT=Xw z2oWuuqb*$3KHdtMcTVy>+C;JL{NBwNgr7-&^i*wBc`{KECMhNi6i0&I_>rNm0^Rp< zYGtswFROlq+;Y8W_MB0O7Kq4iJ`mT>Z)WWJz?-Ifz^}l=e7MQR=X{*_M~Fo#YW|pQ ztNw1k%Vz9tzvoZq(hNT-fiwKWlU<Us;Ji48IgdDCNtsE83Eo#;Qb5dHy<o%tc{xdc ziOK&9h5r0Zp-4PG#ej}IL2->R$1!F{V6G1AMO_AP9a|2L^=K07GcTiV8^}!8Oy#(| z;Po2V>`3sp)mb(W_<98(&~~lO=wx!}LWXM_I*TF}ay}^vy74M7Pr%MdIL|eRAN@s9 zN1ZkhV4$#}W4HA^*(sF+DAOXF73@oY-gEZA*$;a$u;r%fz^p-oW?lF2z{x=%u%7Rb zg`LF7GlsF&j*Osf;XMAb+wt|jX$}PKP2c^%mjk0rF`6x4VD>#eAv<PU3$l+;_tD@~ zX=1@?d@(wU#Jc{-D$_~`-VfF&!9*_0Y)Xs~TVdRUa_el16I}pGFW<1wKKhk^4WFRu zk)Co8IdDN2GCFj^dBKp52}M|O!}tT@kJC&nx|&I?63#zu79P*D|Ck$aw#3A1s}jAs zq5PT;J-}DFT0W%0iOLiCyE{{YpkgbQL?qxC7)yA%`3a{BKUtRjjt|5a*4{-@(vy9U znY-q2Wes(;I~33{FnnLF+0_+)g(0tOq5$|0ke{LuJXEb5NSDIM3L`90L8=GP5CB-@ zQzkm>oPlh>ma@vQL(f$RZ$nvejhk;^gZw8Wqw#(;rfHL#zaPg<L_;XNd+i53<eDA{ ze1vG6#zQ+Ad!<tB3b}D>q(VK2$oI8Sj5D*Y2Y1ndvvxpl*aHsiwy$=7n<6HK)Uby2 zv2bJnM@Qs7HFWLM6xDvu8xEeLnByKgNgD@a3!+Hq*G`QX88TiwQi4Qrq`8kJrfX$E z2@ErL)ewH-kmcz(p8li;OPEn8*>ks`#{FnDIZqpelR$djdh!gyQn9<XduW|~m8w?? zhl}bV5}<mO8z>V}tl_qQe6}#9Bcenejyy`K7f=HZNzphIx!vi(%)O2)FmA%lUF;hx zmnxBf|C9?3YBv#;*Por-N6RC&vf=f)`73TD)%<)D*gWw%!A96(CP?C3R68sc@m(Bo z5v(ljN&of9jD1(NcOdOro*317QNC>jW^%)YAaPD_OwWMdWL*k>*bqD5O<)kMmh=0% zVwdyzB}T&F-}&VPAXNid77HH05naOphN?Azu@?t`b?}h$`3keq>irzQ<Vc1>XO`3( z+Rbr5WI7~MAdkc}q6E}iOd6%_&_vEme-OUdkvnMmCUp^RBRf@B2Yu#qmH|n!bYuJ| zMo0B_4!~a!z$5T~P^s4KQDAyc27N6a>S2>-#fRC5ZaoR|82;)VSuFl~$b~=?GG3HP z#KUeyivkKtyaLY-zN^^?X(ej|py&8JU?rDJRD)G6*(I@a%k3(6_zwem65ELk?!}y# zrs60Cu^<}_R9%-{?5cjKt0wK_j_)MnzAC<%8${D>PHZNB_&Kqm=+B9`fsPpvKBIwy z*e5>Jyiw#f#sSjIa*Ad(zXZ=G<M^)O<1lU@`|TpH)PEgre)jDo^tQEjbriW=R}AYP z%LluKo#6Qx4>#?9AIt9e`)0~R1`{4!$JKtx&2C`#XJO5A%7&n;3$~U@?_PoMs5)vV z>^=i#uePawdAP-R$`S#O7lEt#_P#)yCD_TGKDfKx?g2b_O&Fl>NR~I%Jy%31LM$|G z&=gYs6!fZFa+SgPlEa&cyQ5vEO+RN0*ZV;wr2Ni!<{qS`>+beD;)y9MY2*Kqsv0c! z$|gba3kEQ4SCg1RRr;FpR&oxpg`QMXnD4j*x}leUu-gGK6?LpT23G$dZ5z^xV$<zG zTC3)vI*0%iU=Q29pZh@vZR*${J-<ZzVzIxa;OqlF@o_cDF0=^3I^EovyH6M8)7##v zu!AyFENeSGH5}1*SET|vm9kSy<$2v!$V1t7>P1JRYlgHlq`zCkq5IC<YU?OSb0UlX zv*q1?nS&$PZz&(vb845ZfnHfESk^62xP-J8QDT9H80-Q2aM{3`Smx=l@CO=YlD9I9 zwjk@-oGtjT3~d=OLhIYQts2@AF_?g2OndaGH0s=+oeb@jfix-y;be6bzL}&o!cR#~ z<0rjyn=SeHNs8Q0Z%<-Cv}wnoHp_g8K6)pA_tU<_SfMU5Q;cmRDOQQH7jnSPh(3?W zhrgYu(`vGgGPy_Iq(aH)^42`r-<O%2^9A#CBwLimIVu;2nK>+X?IJw2ak`r+K{z|m z{Y=-rOL0?M1w%Wi-^#GHQSB}nt^6dc6&Y{$N>crBtI88l<$NMX0PU}DJfT!uq>I6S zwO#JOi<f$r!nyCWONN_3%%F*>7S&3IIU;Z}oO}!%Q2`xyaJ?u(>*W+)t-Xkvoh{Rm zGE)))IN0O@&O#&VfD3gsK<jJsh0HSAa_u;FiAj~(^P^O72}6lIK1mt4PGC)eSLIqY zs%Y1F;;NRAh;e@k^P}VINc&j}wcm|@r&3iI_C1X6@Z-q_OfVn8e9H1kLV@a!jo2sp zD@$S|b$E{FG$KmJ5x%||i3XJKD`fg}mUNiLN2TQR)#^NZ1nYFL0Gq8knc0%%L11Tq zt>FMvkxm?R3T8&V+}PZj?^RG0u&VqqRy2sM1I!EnjSWnG3EvHBRN*6`TqXT~iKzom z;seNUV5S(Z$348q`r}rHImqqxT$7XoqUax(G|5cC&yb|EEZk_dt^05+0u}9~;m4Xo zDl7RDrAB29!qV+jN(B^lu(s0I)Y+^`)rq^20I6DsO&kjX=Ars^;GX3M=()ta>lFFO z;qr$X;9}?sO<+>Ru^X36Sbqb5NKkTF9>zfQyeyuWhQWTbGE1@w{GA1d-&QNmfNhs| zh|Ymg?;hEk#7_E#`2^A2d(5cD!Jr5pY{0aEEC^rvNdskok+O2VOi-v#!N%EQ?WatA z`!Y*uM2bU%;xU!fz>LtHFTv_OmqaNoO|9cdyLM0zl5lD-PLx#`*rYRmQm(o7O^&1z zoK~rH0)9>et-|#QZTm3b=1*W5g<F@h#6KcbWIGWLsFEue0pEiMOUIdGU@HTJs5K=F zf@YM@R~&c3%(MWbid(mfRCkUa7C0=ls!3m*nUkQUjzAgf9fNZTU){BvJ#XFo{j~~_ z_X%ga5~5qy6&+$xnn(qI>tgI+Z*xw^bXb%^xg*+5NfUt<sF(ne2gN2wAWxiRsKZJt z)CR<`RiirdcX&d<Tc|7Qkcbcf6Em!$!mBtaO0bfFJWPEZkSHpB2k3zIS4X1Gf{F#V zfVyQSma9m>MA_507?EgrLki$(;@9D!OVN79P$?NI+p!|B{@7lBFG8e{A(aX#D%cV& zd0BjY3@BL)NPk_QA*;$I{<0E+t@#6YbR4|=LEiwA<SeI3s-P;NQY}v&^6?~$WgaQu z%U)q35>fg9h^80(@&j=R3y%3_S&($-_C0gX{3UAlYF!pI@=C!Ti&*f+ogmf7r<^=x zm&+WGD(P`eTkl1GaU^3X;~yta=qn@mNYC-cV+RaR2eJ$e!fJufDY8rFg>AikTn<Ee zfS_oN`qV=Ju(}6is5~E<d>_PqzL!VEqU~D;F7R#D9s-aU_ycnw4ED>HPK+Ndb%i6w zcQF?*V1gQx+MwAu$k)o9gbzx^pnp<O&ye@b$ewe7id^D<kQ;HOnY=qC1UMGqGevX; z7Wvu)DUR&n=RM<o=~FRg6)4mX1}^ZEHpb*mjYR=}`uZ!6ebng=S<{Q>BoZ(kF0Ak> zCg3M|>VkdVLQkCCiU8}HJv_mFg`D4jae&F=1keWwjq&OhM=o@r2H~L8BT!6zzyQu2 z8ZPc>Osxce>8zG?RpEPmyP<MSykd*jbT(vBE$?8Rmv=08&$d{I+v>28rYlSXR#6@) z@dOaIP#`!_1W}w|$%d?asuBQGH%FoG(c&#IK!=#69py>KgpwYZ957kpY-HkQL{*O6 zQj$1hYMDFm?;?dLXR8QhRhNl^B0?kh&@4i8P&vPUCN&fX3l*^(=}nW$$4VbK!(`}A zJ3EAGe4_LxqB`nbA4`47=xe(4G#w6djA?c0T-kS>7b6T~{)`%&2qRl-dGu}<=iIVx z0vde$Dwyk=DSLdv|AN}#sVN`k?S6~4InOPQleFRBqz4i#_!9PDSpiNCbhV^N1bO*x zWGmr+({&&YTP&D9cGXqFj8M2K0LSDtx=kZjoss}YjKq<!;%Iq{TZJt{lbKDb7#&Uu zIEy-u){tAxkVGB3Q8rMqgtKTPVw!9!YkGAtcN}I*s-aWR=1x+nkWMFhoAJV?!6Rql zB}&P<Q~Wq$ex`pLQ7~zMI0$%N!aS_0(VuvKX-4@hj%(`AC|@~e82aD{g9DuSL|$>D z3<NUl%@G9R6cw_tix%APbYhA9*4G|vT<$s%<I#P+fIiR@!{wxVoM_gYeGtPY$5I== z(o^T?bE?o;*O~T0d7ZlC+kslG*XZio%O=69*wxEBbm3%x2@eBNC_o>8Rb8;U9PhDz zqIf`$F|pb`4YTWZd#Ph)7*X8q2l7;7d>`;1=`#3d5oF|U=v<&CmT&IHBr1S?zr_$( zue}8)&PoStbn~f>*SlfD>Q4I5&=M4Nx2G`U0Rxd&fNU#j9MX|sd0Pl~12DLEOCmdf zx4fdzje~2`$kL_2TPG~<Yk?<<$V)+g-`zd;BX5U-wLyVrcoPbREe3g;D-MKH=s8jx zz;``ea4FawZacWi>7WskD+rMaWd1wuSb-yqiNog5aNbng#j0ZcjP)j^*B5RTwPjpg z{;fe;d*N&gMp_NF_`tbO4qvM~O`_ZOqWf|ZZJa9iam0kZTx+a+!Kp(Is(a3VxS_=_ zb~qA3#A{h5siL2MRUpxx@Br+cxS;|2uZbVplUDzPu%QnujuzS&VIn4&h^W7Jj4+A> z#umi6k-?EjV8S~1Igvq`7d~~*oGtTq@3n{o|7<&i%#BC_M#NIzALrE~oR-b_tSZS- zERx=7(qlK<CDTOS8JOT=2r8z3qr|a$&`O5BBoCV_0k5RikFr(33{H*xX$;ge4K}*3 z(_P8*J%!FHV5xv}!C{GfdWbF(o2tu2098d%btF;WMIF%@IPj~kyJP#EjYO(*t1|UU z!#Smiuh#7Q@AZ9*dZQ|3vh4fsQG)R#!&Jqc^=UZ=mQ#Ptmw4l;e5|g21jEuC86CyQ z2Hvku^^8-wf3NB_nMQ*;^W!MQN*%VX8;%y&L)&!K3J<n;haQ2@J%)+M4Bg<N;!WQ@ zur(d9(7+J3ImL@s+!y;EnP^Lwl6csnMWwAH#L&P1D`v%EpV)$}CWd+`Yk|;;S?wuG zi-_V0Vv`<bL%kg<c{b92;NEz7?7o~7BSsKZ{fT8KKjDDq9^&w-zRi2&pn`vK;e7ly z!(=f(-AxGeDu=Gzx9)X(X+y+ZgyUY-HF_7$=Q0e`Bi*s)z^39_&R5E0HHUG2Z7yH= zwSjc(2r4Dn4U!{f87!o{0Wl~kx5;FT=9;N{hu~LdTxdhJ=Dj(8ooWp1+ooH90f0A+ z7V@y7u90&G!gzoK3ZuKNj~R#rR?pNJ1qC&sz=9S<H;lkfjaa)|?larK9}<lN5TGSr zk;BuWa7w7L{*7-@P4Yh7J$aHM2A={n^cKdIvGF99MYw)N-4YW2EF7lBXtT$P-I^EI z7nAab9Ap<j&f{Bu^5anJ>Qy7-O&%(Epfd#xcNZ83Z25!4!&??WE)$yy^k|0XRM~F; zJ^3Axe<0od(DiQF2VkS_H+NHvLZ2t&81iD>e(;Dnc0YP96S<n!IKNWOJP1{Q&J;+G zIg3Lo>+D;!ky|TZ`V@>^XU(nkAPwpU69jQAY@LY%9xelaQ_^8qfc8i@_SHkF?z(K% zBiy-=mQOw~KMFGz8dS52%sMOWR7U~ZFzD6DIwM-gjvP$Wy=la|cHD{lj{Wq5rMrqM zN{CaI1(uUqYl)opZm0?i+GkcQ^a0rUi20+Bs`>Yf@ivzOPw;T)$3be=cpY=H*JR7< zk8WI-x^PZ^N}kV3>JTM+v@qpG>*S07qJMJgi5){F#Y%6~&DxJ@f=@aK_GmBzct`n0 zvcuR?S^hD-?BvW;j9cm<tgPP%xtV|FLrG1m`6^{3Q1BSuYC_2ZdAuM(vFPOV;=Rwp z{Sqhl*VdyV%T0EAabS-Mv!_WGiq@I5bmdO^-Ci(%WvPTJO1@zEl2^AB-O{h@7AuIU zAAs>ZBne{75d0DClAWUEiIN7V^*VcmZ$LM)oF5$?N21vf3!p8M$Ntf?bPDt^2AnpA z#2uZ7D;I~v#pNE{P{HY*;M57cEP4_t6;TR4$U_xs9pfVv^1yNV9z9G!9?d?;Pzig= zei<Hrug7e@{Ph_s4fw+IH`Hn#IRc%)RGy;HD)OA%#j)NI`S3gm^3A=h^1G*#`|`$U z7tZ8njG9LPBxW<Y@sxbfBD_EHn~H9E%=8BG7Zd^62h0_L#t9Z6_FU!w_n2A?OIS9; zuoJWA&u?LK+Ar`oW}fd3yXyI_-aW5{*mLoJ{Ns=R^V83s(HD4)QhW9BryqUttB*eW z)u*5MzLG4?!gxF;%Ek(`=%!0IFsHb~%#(c}5;a*FY_Y`;UEPM1qw<3cD|fT8QWEqW zN5w2zVfNmWFe^@x!qk?cggV{|^$Lzn{J-_Y$LDYc4USGMdqIY=ypad%U5!~n)6sQ* zuOcjr28@FVWm03hi#Z8!(rVU!YS;JZ!upG<bUIy-;iU_PcSDw3*pbq~FJHZWJ7eE< zdn)BZ|8BdZNSRlMuXpqYGfs5EG+xb1SsfIk!*Y2FV@W@<Cj-!vf-<rAGbrpb#%eDJ z1)%xmRevdOoplhx7up}7<P14XDGrN&kO`P1Gy_tbZobZ5cd~{OvGOi_l2@6bilc2a z3!)jWSCzEO?WR0S)(eduTC_Xy$)C+}a~Gkj5n1)TFm}D$_*|o&4ih+M38)53sN%Fs zOrkaS<h^DOoZ>FZ;ADR(zH^xUnG`=vbKt*pQU^yaob~c;0*N(lJKhS@WABN7XT9p# z#+xy%9QaAG89E)0>BjZ*$ndct8&=eh#eX%V8&~$zGY>aOit=<@@K!gxl^1s{FwBAR ze8H$r1|`W2!s{su?|_#CH#%i<@EeX1A^deoN#3r#=WUH{W-@3cr+c3FY8O%rc{bv` zX44U>Oh(UNJY#>vVBkY7<UtmH3NTkTlTF-Y^w#A-)LL+R^O6GzIJBd=x-mygg3)!B zDt#xJSspv1!9D~r`H<;gnY{1`GZt>R$Bahd9lF<2l1=w;ujo<kvF7QNgG|{T<u}Yh z{@x8aYINHtszNnIt)@`pNHI@peUEub4m$qQ{GQgN?H=^HPzJtRyIZh-aH%F&Cq=pB z>U<9Kz~AjbkkT!sJ#WLC9qhq2;LGub@OMK=+cVxRq>JY;F-#g2{)n$X=BjaO2^>06 z8!Vnl35hieWV6}T$1vhASw?y#gTLf+c<f*9w&DV8odqvpm!e;Ii(JhS#AOCjd3?_9 z<)!-~2GONzXtS8IH75gqKQ?)AG+pO%KG$D$;2}Grl!HO0i8OzrR!*}g7*7F!L^<1d zV~150iAB;?bcM~?Ya(_z=EvjbnaokswSsPekx{>@^jm2LtQe~CKHBCGfl~OY1IQ-= zGHE~3f*4^mABd(9xt!{Y|I1anML-uYS78h*ead^(ttZFg8LD%Crj2&WZ5xZxF<0bI z7Wuo)8n3E#gT{{t?#WGT2xER)_b2cMNN;IHM`okWJL8E=+j!hhq2D_45zYSE%6(me zW%RQNfD;C{W|?cuHvGPln%2}${TvoGyTIukARd{PK3y{^r-lFjB~FegmlltBD7SB* zOjxK(6!ocyEIWgL$vwQ@>WqP9rEGK-C*%r=jz9H8XC`A(#o=rWCpl%T_!CGYw;$8^ zG6afW6IkaK(u6$%s3(e*KAjK<c;<blrTS}|g%96iTKk<$sUE<gT#{>zWd}nyOSJ#E zz%xjK%hY{ewYRh%N0J4w=mIS@>s12OCk0HC(s)sCtS`iWSOxxMFMW|NF8(Q<kq8)K z<5Y=gX>*5lG??pN+n*=H&oP(MR~9MHZ$`X=7Ky4+3>A!uVBs$jW9X`=yXU-pj@7SX zcs_KydMTdUX<XS~WPTn%dbZ*fW+kuCsk?Hz=$Q+5;{O0pO9KQH000080BxrJT0ZEv zf8_=M0D>2{AQ%KJ2M%qf{#xACfipx7004+9mtoxlDwl*G1RH;C>##Ssy4xkWw1=+g zExBeZ4hNZ*Xq!7(R4Mr`E{cBk{r)5+lCqsk2Mm~j#6F7rKR;6|t+p)BSG#VfMV>Rc zZItd9uWQxuPO7?{Ox)U*cbmzI_7|$EL}8w0d|C8|t~#OlvJ$EN)hV5_?|9S5dY!WO z;-{Uc3t=Iwc6EQzDOI)o*@71v_pIB~iT*({Vfftqczv0_`sVW0w^wh!PVLgZ5g)it zC$ot?*T^QzA4FX$ooUgk>OoX;nKfEH$WrJ5)y1;yG%vbq_aHnjYfxVE3IKkhe^_Wc zUR48&_Rn`<MryGYb>~iJOWulp*DGF^vg92k@d`2mHo$*PS$N7GD*BBBaqlkw@x#@- z%lG;9k2l|Z|2DsRefjq0>dn>VJ9lIS%9Y;t``_3<-9zm$3=l6rHkB-77tt(Nu}II2 zNPm+UueDeSZB+WyS@+YQ4QRQlS4wY<sZ9(Z4>x$k<|oeF2Fs}z{GElB_d0$_+L<4y z{>15&VcUP*L{zOf|13C@$uss=ks3j2w-HQ>%6iX6Hf$-nM<MKkUW}>v%PBL3zm>EA zFoQ{$wLqb)sCe6oHk&-dwwnMJlP%+{#J-{<30G#nm77{QdU|D8Fd1C5Wm;8)6308m zN<4**%=U}xD}3`VJSRRjfbY=1Lo6_RdCeY`zGr`rvfCgo5b{n`d&ZrF*fd~aYIvFs zEKZ4E1x+^~hPNTMQi(Ptdtw_rpiLTpdl`5xDN=wGXr+5p#7ikSxVv#ofc-$l2K}IA zrk4LEju_Vh+^=LUN+zwE$eW2ci!No$UB?7|0wLUgk6741c~ax2l{<bwhIEwBJ@AUq z^$LFiq^;;SswBDBtl_$o#jZl|Vhf-xbJ#M%U%M~_E@Ydx&|bTeqRf~31rZ1kp)ELY z)^HYU(dDIR;CFBvNpOrscvG_5z2yu+q%|a)sE0K{JV55pTh<3c94(+^*=~)^&<R99 zs2I{n0gdou4cftC$(H-j+_ZVT5miar<s^TPM(A$eTV*dF2ifr0Ht-sWEW+U97B1=d zNiAAYIEVzQ75YIIqP2Qat=D(L3#_-ES<-<cw$TFW5qg$QCX?52b(dz6>Feoi@*!OK zfQ88fhQo4F0C_+T+%OR+PTRb-wdwD?&x5z{ij;l0vnkB)KBtId7Xgs(s_vY<Mrwa! zXJ4_`>=*XI#-ZtS`n7<lI<d6@7c%9=Jzon}^Q~Z&Qun*2$7-FDdu@1pnT?M(8|Y4a zgs0-@S{9_rT8I*weB8K*)o~5>2AJ4!FZNc|4z~eC_*0yc#fA(I<pbiLcMhTqhd`Vq zst$-@i9NREdjksI-XUWegBLBCl_7tzon`EzEG5M?80J7QFNt0#lUJ=WhzJd5aKfFX z4Wv$sO2CZ4z^<8EKQ%C|7>yGI!f~>;N?L`af~;Pr<|%y;nRa?uzxBKC%$8^Dn~PW9 zo|EG#Y?XzHAdiBz8?}RL!1IfHlD7+~U@1h{T2^G~fKA_Ce0O<!`LCOI7w>=Xrp#tS zXZ>80nIl#aL&hQ~)>x5SWswAwcA#3dC0|`rD5CfdLun!0jJ*ViRw6Jv2N^6#)gZlX zB)rI?(Ly+2_i&<?JlhlBp0k?BtK`qn(Dmgt`|}_EWaxss22aciYpPxAU|HQ*rkv-u zXV-hUah;OnU|=LRBq+|7vO|A}(IN}X5Midym&iZ&0;0*`wSjg`_$4Dsrnhimr{nO5 z!E40#8S18eR?+T(b6~^<lRntBZ2CGOi3*9?;MCVf%08dRIh+DvP)%9aJqH_nYf_^t zFd^-W!A+ru;0aVF3gUi<(7Tqx?zdv!4t!A>gkV)o7HaeYf+%8I{bYX;TDgY)M3i;T z**^yxwg>{Wi^bVlt<JE&T`XcyyJoF<DRLsV6YIV46m$N}pLd&Bzi9*loAV)DkQ`cQ zkz^3_6U55a7C1ergB03bgL)a8N$OO8Ah5V5;P;LQA0PUFjSfoIvdi<t!x7ag9sY6& z_q?yy{gX(4r7Sv|B7A?}aOd|P75Wlwl;3hF%ptLu$0I>`gdUSgI&W1i&Ld{ByssMO zJv*c4p~1~V#zy;&ut6SrxH>vF<_(9&OXCv}yR<)A@cMJ%VV@Yag{4-mbUqYP=TRN< zi?2eA%s|9JlsE+`BOCPiYp%6=%){NF#P-;yY$}SgeeX@m3@m@fgpHl)L`F4;Z<PXD z9NN**=Dwf5^`}C!^zGQGb4Q!H5XKU&Ne#n2L<A*)fPP?b;%s265$|3D6j!w>cgSKK zZoPCNArPAl0WJ-rS|O9A08C+002Oj6#k1ChT>8A)X^=t{4aEklHd{omR8^@*ioJv8 zJT3HjahS@GcPM}9k}*-9uO9inb(Uj8H4-g!J_7Tymld<JvXR@<UL$>i0rQYZ+nap3 z3`(3bf=C)<71|$E0i8tFqjXBO9KKK?^^mB@Z3bN=GgJ2?EFM6k$SF7qIxlLliZY~^ z^G`co!Y)w0A-LM|ht7ahQ;PMu+<)HeG_BjFxv(nmCewe5p_2F1dQt&PfR{rbGA1xo zE{kR+rcLoqLxOv53fYy~)l?MAN{oORf?dO<>Q#k^nuRua#Kc1roKvbwW<FCWZ5)bp za^MsZ>Z9;7RKp3K4`)Fp*vz8CRMpZhYIdykQN<od{m(>XK{y;fmRK$2V;;cp?On`m zR9%NrEuw$q+1P!|WWW@ppn5bnF?M1M3!I^uw?+2|-U`KQiXxaQO6Pl}B12tC-FBv< zcu=x521Wg@opg}oI%@u_HX8vn?Noa&8|xSp3l5xyIHrN3HNH@w2m0}Hna@8l7(7&% z%RNyiG9vYr70&8-zcF&HPf!+F=}Fyv${;c%3}k=&#2=m=o>esI*$K}Ds5k{MM(t#A zYO{yvHU>QS^gPnylL>iLb^SkNVakvH(=6;aoJzqo`MpOrA{SW82N6|AX5?1V{L~9N z$h*`MAicWg!s*I%_^?=;7q%4B?P1D-#<15~?HXkLpvhV9+<5$STJLk|II+XnmB#wH zKlp!wUDW%C4^vYB_0-NjrYRHII!jrjOkqq>rKV=v4CUDm@BW^B4lrmFB>{lROaPc0 zkyrw0s77{yCTl*QFBXCQyT!%A%mAom=lE5X1=r=%c(;u*<wcD;8#ow^@F^=+ax1%I zq=c9~QC}_=DSgfD_pjzVrYt0jy%gi9LrQ;Xv{V{>`_IC1U6F@$Jxyt^dD7D$T}$?o za(5emqjE^^DmyBH-=NAd6UX2>rd3EPI<q02hS4TJQe(b1!U>>-NK7fVR*HIO^hmN0 zfAJOAmfzE$+4VOxM6S_|nhO~Z%})J?0%XI<;6hb>PYdJrcwEF7oJ7m81p=yg0qK8) zIyzDStq?38k66bPjr191%xQR_Hv-DQ#%2a<!0!rj?dZ`Fvq0Y6p+W?yi^W%D1;!qJ zH}8i74sq3N_|neItn1mSF!?7MI1*yQCB$Be<!-&^OIgWoKRaH3b?CpN)IfD|BEfGG z#Lh5j&U_{kAL*8Yz3)_G!|*|QZzg~1wi2vW7>!+R8>LxOc9_cXx!HbStH*i-&Rb(# z8lddSWpp+6hF5{R_-m`vCvNm8nUZynw#6{q_0kxbISzwBs_1qNa?IfkDf(opD^r$b z*&P4`#qJ@g&)C-_7=-Qh#@$VLy2&?&<|4GhvE=&VMt09z9}N;4h=;2(6lj0<GXs<c z+;R9KFh^KPh@@7)080>W)@F8uOeMmgvD^;gli3jwB3l`OK@va9CW&^?=f&X?eG=K4 zF%;DwFrOp0y0et&>^K>>f4n;^s_0`zi!>pn%KNwz9Gr_G9b*x!@p_*)euJi<_kLJQ z1qZ&hCS!^e>fOmYZX<X}AHRQe7DSF=%?1CM2I6Vhl2g|Y<`R;5HZ0r5151W+qYg3` z{P+Yc+8Y5Lwg%<3P1H^TNurwD^DpkGfS=CiQ)_L>^u^SslbX`k%r*t8N!f!%GKk3& zW0Fj6lFR@-mK0DKMyFq*N|=5*r~iKi${B@Ldgy4dcd($K$9}-@F-m`iO#w+Yr327H z6UgKU&BL56x1oD}bSg(Cn}m^(-ZRNyjI)E<Jm#1rXiY3VShrd@T#v*UKORwg<gDR0 zUJvLFBsBC5;pOgs(KYzIe5w-&+&}IG=qetrY!agU4V^?hQ1g#{ls4mTQOPxxOgZeO zpUWmAP7ElrjX1(1C0~CaO}axg^`g4I^jXr~I{Y<mSxq5k0=q5$#uozivgk3mf#`29 z9?h5_4(`$HKe(oF*Z+{-mcY?aEFH>z`<6rjHX~R)PJg4<(`MRho8vjEjmMyb<_}y} zB;zb*GojZ=EO~%eDjGkTftFmo18{8J7A+j(#I|kQwr$&XI=0Od+qQk;<ivJPoD=)y ze&2od|MzuOcdhC@d)Dq=U3<?t##&?aHIaj0SXjVM1l3UY%D++RflZ+Tt{O5dLAWn} zXZj5o0>tl<sWyJ-cUf5iaUCaZAbTb((c)CO@se}0wMgJ1Q~EyKA07^-Q0#GETqfT{ z#Ikg7H>Iyg0A*(^h8HY8Jsiw71f=3`dC?+=5O~mVa+J)WaIqzb3VJXW@v!gJyJhAP z!>%yojob{t%`7_6AO11`XqXvqxW1X9j}9S~%-boO8yDBPC8Kzrlu1Gj9rC`DY#(c7 zzvbPcZwR%1@Bj*PC`fP0g0^hgla@H2XAsoIA@`sBj>Y+lMuPAEa(Kf@pg&IIcNy;# zxLRY0$brm@u4q!h)VA%z)NpzdY+J@g%0fssdAg(PpLNi*CQF7}9-<txD}*)}OMouT zk(1|ks^iM@6?vGu%8pI39+g9uUx4RyF8&oNoM$!JUhYO3dy;(1t=`mOTGPagtRa=F zIlO*^cM?;brQ{U<<jsDE{NFzG?Va@y0SpLe6!zbi({~vK3PA7py=+3y=8zrLFVDc~ z7CFPH48DM2Bq7x9mvFxAWUHbycFbt+PbCr=eJOcv)4Ls?!}tciTU!pDS7nqW%>}-v z%{mf{<&>0Cu}hmtE9u7Sm<Q5kc70E)W*yonM{*Wvr{(JVaA$ha8ee*rou5kPU8&w1 z*A8o65ooQKwSXOsqUkp89U_g!s70UUj)n#^i{E@S9Rq!PxEsM;*P#apTgDdH4)x2| z2GF^ax}N&q_VJoBlVmo1n&n}jdhot!R+VCrjvCP;#43YauUhJSkIomT=3p)OU5?w^ zwMpQ6Lih>76(tIbB_?Xsr`a6$+m`lELnvuT3we@8S%6IKH`gWwn#8S0I*J3?Z6HrM z<u@Hh{2GP^uuhcjX44a(UFE$+CQexb1c@P+#5w0i#Gg=^6n_-iT`;IgcvEl4X+~V* zhdEl<!TH>Ivy1r>IZ*k=eqpUqmzaC6PS~!1g1t|rpm|$?u0sCtFnx!A$QY;>snwJ1 zyTabkJ^~z$;3Y$iM;%n`S@U^)T)6~tGo-#6g!<fG+IGXC`vaj8tBHq0-|!SvxVI=W zeb`%5AGx1Mv78W%|H|^oyu(!j3sA3SEUa$ga}%-NxS6Z7ROmJv&uatmI|KQ7)IDRq zN6W36hP%ix)qL0MJn`bC7-($}shM>LX~gE)bq9c>1|F}DEM#ob+FZ_%%mu#c%;y{Q zM#Ka{M9j|r!Cwr5zXFB0D6Fwf?JcKKcK6!MDeK0g;`r12<B$Wiw{%K+I3|ihXyRu3 znj%=TTM@z@9@FWxTgTf^3S(&aDfk09BDjH;+h?46_=w<;B(r#w`3iy$@lUlkHHDIW z)(!wPTs_>R0;VaZcBk)+s(=Nf#-|(efiBb_uI$<wd)AsXpkyOR=w2to(%}g+T^Ai( z2X;|0hV6(gTwl-u{fAu^tQx{)2(&C{UP6+H7*1HAborGMIe%fS=)u8XL)?v9G$&2F zS-A^?`4)GyxZ~!vMV>@2lP9KHGEGi~6l;KQsclUI$`B%LC()}22c5v0h?S1UMAO>o zRMcq$A5vS?a-Un)!|!SKVFd!CCTCw{bn2l56#E6uraLH_tfJV<+IFhX<~=%%F~7vS z(rtPp=8hBqRMlS5ewD)sWz@Fv7ZVaIA_09E9=}sL72hUDPru|C|GT(TiG)K_w9&VL z6)?4FFnUhsKE=ltOALZ~mE{92O-n{%_aZ8^-v<q}$5-?`PYz306n9P{%kRhLs)Q=> zRzeel3-=&TbDo&Q>$P?NQGd~HNS@;zrZ4MOJ?FRqv2A56Y%4CMbMwNtnh<ihmFcZH z&-)6Z-$1@hJy;MT(ibj(s;?XWEDK7`6Z3peN4CGr`w{W}d@0ukx2FZ%eZftA!2f&k zXj5PT>wx$Nx?(~1w0%ST)~#Xt_c|iT4aN+p(a};BrHW%n|N7{~;uoFx%l^bXS1Xq| zw8z`)rMSlaO1R*<(V|zhpo$u<%~6w}R-&_p-pQ<?VNbiLR<+$?)^JO*QTt1~M&<xg zx@QAzY3A$qv9-+ohDxlG!drS@uV)(n%Uu(N0PokIx03N<tGbKs&hq5@no8#_(v~Rz zJ}Ht+XDel_LC%D<h8X$eJ^^*jp-^s}ZOZ(S4mkfup+kuh1Dc=Ov~qXx3103c7e0=P ztu$KvXW@fN$yq$n`$8P6F!t?ViLdj9)JUsrdYd7ypXjl_v{L~)<K6_(1p)Qh(rHrU zO+9}X4K7ou^XA5Xt3J@Hu+iA{8~RoPEaZx^D_X=-t32e-sJ9koM^(IxB>D~pDn>fh zV_VXS?N#Y;mRN2|hma5|tqoQwlEJVa$OHYf_D;!f3=AdH%%V=HY!McU1!7s#Bj!Zg z#uq*90pdBlt*hnkK~H&0p<?>o6A<j|%^WtclaU_=tgm)XLMccS<JQ(~#FoW?@-IK) zSQlUgQ-87>;EFQq#fAe2id%)(z>(kl7g;@3-h(_#NuxpSmkWUrKfk?Ao!1&qVo{TA z$*=|{W<O>=!4U1jcN>h^3GUdRS|afZX*PE2p8ZEeH=ns-cf@SzOx%r%o-{Nxz}ygz z)=l(-M83vN?otA&<S;VQQWr7+0VsE?%;;$!sI3L@)CxkFI2S+61Rp+R2M)5<l&4(P ze%eP}%9W_rI%8Ws#0GX{Uj7NgpW$C+IkbkMWT-5ZQ>oH<#Z<FT-%EFyHD^1@{9rx* zswy~nijM~qp(0tN9wfb0xJ|BA6}ToOP{n?^IYY8I)o>Fpd9IU^n-BT~Kp>R@G%u)Q zB~z=gf2pfMIO%2VKdQZip3Df#Y3Hr?)Q&c_c1~C(k+Ar0E_I0?|FXGkCLW47^p&2t z*bUq@snRICJX796GnJJ}+T>9ZTI?Cl5vpI{COq>^1aqSwBw0-kB8q>4RBfzGhW6>i z#uRa^Yk8{P*7V-0Ya_`8j4e0S&eEDoZ=K)vJ9?Xnou9rrF7#tA2OA7UlK9m~^o1vn zkvHNa$jTFFO?i8Jk~S6)RZH}}O?>y9<QKR<vN7@Kd03(!GD~1F0(^qHQYHEW^&Z>z z16;vq3g@#5@cA{-OM7@cu6;~BeNy>~8Li^Tync1M>)XH?e9O(qM78$Nm}0-?qeAnJ z9A~#fR-6pfvETJC5fAcV2(CC?;9~WXb;o&NQ7@KKBJ0~O2iB2d=dxyN>YU5`!qfVY zv^bYK`spP-<g5sh&c+W$3|J?v^wwbo8XvId&(wr#JeMdcP5sbnL^8se5|(o?S&^r` zMe;`@+wB_@6L8@VYICBD(ux4b=xv?08h6yf1zPp-lV$M(I_#5qGlhLtFTruHWe_Nv zwZuse9JY_l`wW=Kgn3n{`?Cs@7GPpq+eOWB9=~fX$j)6rM@?Qu4v;Pqe^pjBh~Z29 zD->R>OIFNlvrk9~e$rHtOK*9Pzm}j)OOQA&wxbIh$^dg%n&ve_v2=9TC2h?nWZ1dc zi0Z@Jvff%U>K)>9XH~DOfbbOFPJS?r>O4IKGZtYoe-&Dj4)%9Wy;-~bSN;OVr9N8h zw}Ig!fJWwm;Rgy*3c#$$bS4tq95OBtt#Q=cO~o*oRQo1qXs1jZlYZ-_E!C7v95lO} zH^Ep%6*+S(n0xIQm^752lATbY^;R8vOHpZ2r9>nkG<H-=whb7jbO%+aSAelhkoM<G zhy@vBy<8;s%n#BWhP2_)&XjODda+hyEn%y!XVX{*&Yc~qR)AwTQO1<wk>WbeDT6IR zwij;vv4~3WyC>P92zdiHi@xls6-3&g<kb3$F%63j^#Ou0Gc^925M8bFPwm091rASS zX4Z)5DS#qH>)yz{x_Y+*X6Y(#lF}tdp1JQWCR-`VU5>>us5M4<!!A7=48Fm!yI$Pn z@{1x&^dZvA3t;xhC0BsR2}2Dd5X7%`1jX5{5yTUZO<DFvR4WowTx?7Z+gV=3Tv(T0 zV%QN(NS|h;HHGI9gLafUVGWo#07?%Ho4jBB!UksA)Ut)2N#14kH){W)=lU6I!ugTM z6_AKEhTfj``@WgtpY0q-;r5OJmY|5OUB-zH1EuJ(3xJ!(v7c7I7-D0b5hBgN!EV5T zv169X%^-?{_g`AKAmLM>oG?g&*=+_nj)5)3phz03Fv&d#1}2^aV$G@OOZ34~Mn!0a z8^DCitd@~-Dh4}uKe0skZ9F|o9BJ6STvN93;tR@is0=YSfkjY4o{x&sT*y&O?Ae#2 zZ4nQ15umXgf0c<MFTt^O1mNf$4bgr7_;_2+ydIYvDbo;gBay<LTtc@h#oqDA??I3I zx_YcB41Q&Hk^3<)yo(;w4^b3gy^WkWG`D)e%$MGKk~S;)Ze7ZYTzO0+yeJU?HzmY^ z$<x_%=gR_>344GW_F=)zEjtGM3LD~pNo};+0toRm2K{ijaM5+pGmofo=d%+v@kKzt zqPrlif=o4Fw!#buOcBa@5^sgStF-TZd6~F7A>NpTpoFE-j54SqUhK$1Pl4Iug5=7M zj$)X-yfbv?9BtN`iOA;96NReJg)GcCH&L_Bln5|eVzCot@iBGirMdliv7dY_A_};& z1HART{oO-mT8nxjokWQ4Q)8%Ujwp*ICUhJ`yRN|%$XV1>1mYQoc|l=n#V6-kd^~U0 z%?r)=^Jn-b>yzVS)p-&H*(c;T7Kl9in~IoSIUaS;**SDPj|W*em&6(&`08}Q@yJ{N z!7S~B1$yx7l&IDvWeEY>mT%f_SK%kj8(`FtAQ|7a=y>QDwIWF-DC!B%LvY*E`y~Z| z<&e;%DMf{6X>ggd%}RBHEmc5uXOpLDwgZCa{9(<k>b3{7Dwx|dojc&e%e-mrsj`I( zKhHa+v7uNJcc>;wUjUu`l#}`-md`msLR@v1cs0CVUxgbEGn{6vHyf!_0XB*q7$En2 zr=MCbE|MCv7V=&T?p`m({9`POgLO3{%+~gdr;2Au{1!IMx2d2Cn~hs=nT=hjAPsiD z)p>juLbX?BxKewl_D}D|kJ!yEh$2BsRwM;uiS%(9XY4GAW5T$~)ys|>4~?!`8b#uB zXwrPB;+0@~Qe!-1=muaK1l>*z3P55mGwt06(6;P2yJF&WsbFBp-`?fl4pPH3BN`y0 zzAFQiO8N^8?434LY3uAEWH{xxRMtW^tJ(tPKPqxoaFTM=eMe^n&M8P6oF2MYWs-zw zVxuFLx7#At3P`ev5TE>7z@5uRo163{RAG!lAp=)A!}-18DqFz@$V=8+l>ovW#h+tJ z9XXNFG}3&UxfA2bYkb#NKJe8E;d}!jkUoJ8VbIm%u_Vz_Tge?PFt|qiZYkdd*OYHV z#N4dA(n6scBr)%nYG(d0%yCf|uAA0iHZgR61)V9E0v_--DcjW+?AYJ+54{+k22_XN zS&WXAv6tZEA80}8u7AAgmH@kwu7GnV2WdkMq5G;m7;KO6_AR)H59YAU6w`KMq@CLj z#sedLQ@du6ZZO7>8h;bnhN_^~ayE)4K3h_a&2*AYL?1uYCfy3}Q6Nx)JIp~tHjubr zUZqae%K#6$E?S?$C(XAcgf{^%!*3T0_I1N_v18~Vt2KlWqO+05VSvpTB?&v>wqlE2 zLxJjFx)*k5en<gLH^ZQ2-*^$~x#luG;B{PFaQley-Nwn|y2DdosZ3m!lm4tqb!n8t zIA`(oO@9~2<JUb(1z_X}f6P~0k~D$oWEwYGh5D?x1$BP8K|(!iJs40HrVy=^%&;Nh zjKd4gIGI5s*wvrzKLD0pW2y@f%Zji1aH126NC#GmwZxN9nVts74?LW8VfflG^Y`tl zOYkfckA5+fY!|>fE^7fzN%?Ez?KE;4W->lXM`}_iv2cF;IO##JZSW;tPCg+c5nhSq z!y}<1k`$M{^IPypLGYVb4mnCxYo<P5jH}^1F>u_!5syKMt^t6XmUN3eHE{Tx;-9j^ zq~TBONuD=#ITlnK2!1+qaL!lmqG`pMIexMath&h10uI>`C!)Ri+ns?pZS<v5R|X14 za1u0B<9jOpI1rDEN(%9$6AOQP#l6#D!J6yXew8edR!pP$s85yHy4&^^?}JZZb!@8> zg^v5IkYyKp2m`?SiiR-!^*Qma2<HrQtIhoNs(wJQgtZKlpns8tfy{Z)9zq@AY}w~6 zaeX=kNfJuJY)d%Q)UzKK6Z*#v(itM+u|seXpG?uSFh>p6L0M@tm|}Pdh{c@nhW&;7 z#a&uxPw-|d<(+Z_+Lp4tWSC!=F!J2!2HqZzmB5-)+z2ou&dx(}!BlIFhmubJ6ObgN z4Z%Sf4H;TeZ*grrboh%~M(-D_GUap2SOh+fA!MmEOsyn_gR@xLehzs1R3dV#6pyPZ z%yJMgwJ!#y3Q7DKJdW&u^BS@6GCN^|cFw~9R%Wrs7PtxciUktgqpY$>>k4*q$?gU^ zdY56t4KhG5<QL_g8k&h{sZe&|fclt*fQ}Yx!h%vF1R7CA`t1UHD^cLW&oTCJO`EH* zCQGf}`H^Ebwc>sP2vDhp`ZloQeXBgF4u{_>=2g1HR5+VyL(nJlNukf)biLRsb{6hx zgt*`h&Sx<96G|Yy$mUGj`NBtb;b_DP<lA<|K)wLB3JSwA1gv`b5f*fjS4VrOO*^M! zos^PwY1fP`Uhhsx%*bl0{%I-k=B>h=t?Bk6KPb>bwY1+Y&s;Oyx|%nUVbNW%+PE4L zYBCXmacMo{j_LS7v%C2ak;s|2TSJhHy8Y_RYgp%1by~i4cqNWnQZ>HbJ(Yhdwv9cC z$O8a|e@O0tnQ+1N_T$TU5$$xcmo6{i1A8_|>(@43RVOz(%N<}$xXtYQp5LqLK2OuS z3&#y;G-AEIfVOPHL2qmrkZRw$%bV+ewfbyWO8jxDljC3!cXV)al{>$*bA9S)L156^ z3_&;M9mjepQish#!H8W^uYn|^L4oI%tjGZLuW>_s#GiU7KbTA9c);k9OX5pS#mb1h zc@o1S9lBs2Hk~%<za(_o+=`}YNqD#@)jvk&SeP72-ak?mk0y!MqZ+_AhqJjVuuhPO zWNiB9)Gb;l7i{N7qITTn;<d-iKP1&Ka0korkeu*q5>V+5AYqL3dHk>uhM%_FA1nfl zI{+KcBAeqtP&qpOJnSZKn+e*d3J*v1i#}Q>OZCZo>ke8is8Yt6oxxSMKOM%h$Rjn7 zS>*Y$an4vd<SHOb+Tb)>`}Tp`m3=tmL2)ITt)eS*u}0lhR!2R3en6}hftY4kILc3m zLK!F_5lTd@H+rLuudUa8j&70Vn!5og=n)Y8p_m#AGs7EqvykNc*fhJ}%DbXS=}TeA zClr63YU4oqVxNF2K`?3<0ktrdh(eL56VEi;3dEc0=r~93ZnHWO>dhwbK>amwru(}M zRIGf#FZdwXhESG`$@VD#;$5ci-uPxXdiH5@M=BlW=a81;{UhT8hA_%r__G)wYerLr zdA=_P(_M9pPD6vk_k07+cfcGg3;L-SmTiKTk;%5t->*QY+w+6_cEsju)>SxnmZ9;6 zF4ktf^NqI%h>oj27e#SGZ&r9q7d)bPip}3n>i&}Vw*V=t&yh_};+0VBx;t4m{e_Wg zFC)(R9YCCwB6Q2|s*%?Mn@EpfAmoj#uIKgtu}cT~et`VvCEX|m7%e?O1`IMiz7_jN zdMp+w>_6y8h?Up;3OEoDI$XM<CopQ;cgQ37zw*D{N?<S`{|+Nnz{WxTiN6i%|1tZG z$}T{s_s4*s{wF0wo0mG+KG?sJ20CDzkpB$RvyH*#|9#WZ1T5}fAz^aL1(vgK)#3nH zARwxAA^;dhS_d{-8;=#(J?KCA!8LV9BfbN&q0^lZfd89c!wxL>zY53Cj$k3+|3ua| zLY7uS0s;A=q$l)&VW$_ngCPUhkw32t2Dk}~+w{$vdYXH{eJ!(P*7L({6ER2y6`@i@ z*K`+)<BFHh3k5!Z5{XIquU)<j)rw8ShO^A_vbfq!r+x3sdtHv=wQ%0RJjYsD_q-~Z zw`cydY~6Fzt6L^<5aysY<oESWxn<gUJNs}{SS2q|lKnDdJ`l@ve-#5*8g+26OIa~o z^(2WQpQ^X&-ld)vcdxRys1i?fAL%IhA>Q|2ezI-5I=_jZsU^KiU1MBWR1p0=HXo3_ zdMEwG);QrD=~sPBZPTMlPcw~PCf!N1=fZ8e{p-vqx5o8jz}f_<ZgWClWax*wXO{x@ zo)L4VYN7d&c1m`8(HapznB~=bI;YLW{gy!;v#6E=Hjm;$ZrVLaO<dr?cKRCZz;^6u zD%&#TrNywYmg><H`YW@dffCQAnkv)<26;ZWCD*eOwaX4qa96hr%`=kG5b_zxR&8N0 zt>KNfd_2oJhb_v7n^}i`&6VPzO+&?;7Bprz<zRawb3k4NOt~Kr{)e|yfgq==CCjhA zCL=`5mKH+SY~gk=6lm(;Z8VC<N_#BrqGq1B`f@o064}^*&L`3cr9Jb4%84QOnid}U z_MJgwiV#nhNRo$!kHN5OwW+a1-Evm@IUSEu{BCQ{+$C!8sNj#~ryq&*1e$T-x5KLF zR0bRQx~VdWC0Z|l&__sAF$vpf=`)6jP_uuarJlH)=XFw72dg#N-%fj$-ruZczv@C1 zN0H}&0jy1gD~Jce597<k%E=&Vv?=?kuA1$!dxXBPuETj>G~b8oBB~z`3feMv{qi@W zi#~E#un&y)XUfhy`?FVNZhk`qmVTo?O91HUvSISP)7TY2<#$)(sSW*-*lz#C;kTPh zq4n_+9RqOArbBj4gbn2h_}lCGblgU27kSW+r7f5J^uQXZ)7xu;Ti(S#_j>m+EX-a7 zhY(`gn}wWOCW5SB47(957ucd1VFt~p^4K<iKzZX;&or&Tp9+oTflh@l=OL)IG1m7w zh^K`^f^LKXiz;MW<m$GU(9QKrx%f&4`2=Id6d#ovbU#r~!NyQgnN_{(voZO2O8)kn z)?3H^V7E*cbbP)}Hslp)jE<Tw{H2^jER;_?!y2W95GPHUa?O0`#Z)cb#Ty+f1Qt<w z<j0_vPdyvi(MsN673L}%ew6ar&J5YD$#*QiLXD6DP;SeDb1a99$W#5R|3l3!>mwSw zQW{T6JhNK8MrRg28mFvcOFzAdG+5J$B|A8XLOtM82DnamGK<S0i&v0P0`bNF#WxUE zs;_&hR)1>XO{+}kmqG&T$(?8wR}i*iS|06*B}t6VpehM)@|>6f%^wU#<OM0jUAl}J zez%x9KsBOHp#?~fbAZ<kvarswwXs^WAry6_wkt7AiC5!!a4x#wSvDv^=We@%E9IAD zGq2)e4%W1PpV!wh3wKRwAx(4tXGCSuN*-xgX)aHQ?dc^X9YzWXZlb)U5zH@Zv`Kj^ z+o-{l0-?~Ka*b={T*?gi-qze_<)H4rCMCub0Q?19;uGUP!}F;f+vQH(P6dGq(LJE} zyrGN4TMWV1lYZamdJm*9#llF@d$Ktfo0zYgC5^&*ifxBL7ct3iD=3&pK)wxROw5wd z!C?ZU#Yf~248F#`!CuB}#W4F!FK=_K%>G2X{4u`;+RV-3{vcbJ8&QR!A>f);jy|U& z4UiWv&!ez@$AQt9W#~g7NB$xVSUgqXlr3(n5&Uwp5hduEI3hddwXfpckF7GFhS>`? zS0YBNYU$o8uVJ4C)ZATeUHN~V9NkqpeBJJMT?3vieiuTMmrZ~Ne@RdMG>Ai7dYSLK zhpW%sD&(5prz4_Sfmq^{;hBxE@#Dyg2T;mJB5?+1W|%jAKoKpgsTQ>|lF$HSa=df_ z0N!JtQ9A~`J`aoG_cv#ElV36B5u68#ZJuc4V=PVZZjC0PJE0??{d=P>;(O5C3@w|A z#Fkp2lwRZPXwVI7<)=QiE~p~hw(h3q7L`;8EiJk=^X)ovivjC-Y#N0~iYwp}0NS{V z0M-NWd>rSX=|r1~o5<!pKBiy&ehByobaoWR7XF*)8AZf8WA<ux86eH`7_q4ylEXhB zp-@~zJ>7pV&%oxCB@=%8A-Sk)Q1rxulyd;n-nq1fPfB|zbCC1kvd#!3F3_fPI&+ZB zmJ%?9?O_WhqaSBUFXVFG7q}9f0_t%6W=bP7B5sDj{*024_AazI=aYkH_zw)-R!a)z zd$@uRm;5SU9vfJim%b1OH>cR&(bP^e7YffVT4xC3qLu~iC)(mS?J(a<4@~$S@37<B z>*$Ygy_J>Jt6VAxZ(h+fQn2rRg9837N%~X%RJhsjrSMJ?!g$B2h3iVw2w)(DF>tj4 zouyK*BAIgz%0loE$vAB&Jfg_Wlb_UXg-2ChD#vpUxe{PsExjh_l8^JLWYK>EM_rM+ z=kF?mFf#0<vyI$}KCy2Gh{w-z0os|@N`79=E)Kj}@0u4O`vkg8h<^s~v9rDQ7`vCV zD4_drei~SiW08KV%k$3A$X>Mf&Du$dgtH*S{FNA%_jex}?9g>z4@bj&e>LZl)^|82 zT--~~4p!v%{v6x)5=RpHAoySAh9qdwMIIOkr~xFs-u(X-0Gt3Y(|;`t+VTUy`2Qt# z<WTg9|Fcjz4CS9vq%|Du^WOss)3#3#-;0%H8Paw^P}>OOzzE_0Eks0fz#{Sf9nN=v zf&90oSm_3<0{O@10GzMD3Hv)`G7=CF#Xsc<z{Skf&C%7t!PdpV*v-n;^qYa6*06Kf zU`PC}G@MkJhOcOi-p<&-!-zLPY-Q$)SHsyAE&rIhBud2=UwATn`FW6%$;53yT&9By z(MEfWb)9sysfs%g&Q>kAK-}RTmd=j`DoXS06|1{5M_ylSyT`*5Y|ccOD>rx-oMXcP z7;+-b=GWRs1$0!w<;iDvz3Di1S?iHCUG0+Dd=jjyU;r=|#X8}jWo~v-M;w=ko7$Z2 z3d`GEFg69huw_=If$huouIr}yZgTYb!|SGV>IBWGQu5cJ+FJ9`GT-znm9J@`xM`Z1 z4jp^0r{%~-+LrC~29b%pT0Fl#Ys5?dD1^B0Pd9F9a(~aGv)k$%YHR&FX6gXF?`ShV zoQ=pbs`lK#uTn|FfPGN(MPUc*C?juo_hSS8;A}QAkv<0Owc_ypMPbB@W3pl@C@2GC z+Qz`;!Xr$%DsR-_9!s`R8I0RDP)@3HX_Z~r=N{E6eJ*11G&W(AdxDTCxRq1@vmh#* z$XKPtdu_^ci~KS>3GMBSO4YPqzS3S?j=iW8gZd}41SO2hy6q{MURx^Jh=|cLC2T&^ zdOt)B8coO+_7WpVP*g=iR5cRi(Kz~qR(lcHbcmMSj-SstpR-MfdaC6aUzgJgxE}|? zvJWdfg`pe4&F)|2A~lPYCz}euRgazy<{IbY89m)qqhQ&pwU#zOUrFv14`&Cu=+tF? z1AG;VjHa1>C&Gs{8lP~NH@vBt^R7WXt6M=luvW1EX{N}$qZD2gd4MPv;T?U6dMRq5 zyNG=?O4gd^B<mQpU4raO*qgbqpA3oPHfZJ|cmuRXC^qcd9@??}J{$uuwJTJ_)JPL{ zMbY7T_r0PVL?8M{<Z$1zVkR6`@(fGlhK8s^G;;1VNHrm*hIxNdUHPGKUAkx~>=z@c ziDQEH6me#J3g)?sJk~j-*wmimvcQ9*DSt|itsFvGbu_|)TwgYL`zybje2?Wq<Zw)J z`j%Hh8X8lAQ@AnB@|qREgP%umoFQd(SFwxdZqIGgtW(IX9w^9BCuaX4;&_^$!FA%X zaw?zFgjZ9C*URRwaI<`w-gc^pv2hi*qm!@Le)dRx1SHqJ-*d)H(!>EjS0n2+UY5w9 z@dT}>+!ktY7}adROUw-aoJ=wa+tpOll%@ok?5zShnR<Uo27xQUrM5Pwy@@E$Cp?3( zb~S^sN}E9}*Cx-{eIB95gVYJ;YYtT<1`GfOUgC1L9|oe5s!L}m)t1;FAA|{oT%+Bp z778(`+g59})i4LUkgllm>sbc9$m$NI6G_ZlWV*DtqRqO4+!nWaliSmn4X>!+0T;p< zT~dp6Fk;&2o<kL2|31vNhT!5|I*%1=E^=uS<4-9w5nmW5ffz5YJtnB9?r5Sd4g9^W zypEQYHL(XREtqg6>-4jK0b0<<q%a2VjSwu2k$TxFDCF?!=C0m?WSC1LBHN<?&ymOE z+P(`L;Ok{TuYZT5EVsbkk&D|yO=%bJ5gU{BF5dnpB3=ssSV5YLr^n9o(3TL)b7oNE zHp@&Qm%jg<03+^IfS@{;lpc~pPPc5JEh#kY@f<Hb$Gunl7b{^A1Z6s}5@VgA&Ym9q z+x|>l=M05a$iq{8p`AP}(wmwM?vGoWglY>R>+-OPF23ZdAuCx;?H}cqU%P5DRVB{0 zaq7RwT2_4kFm+G%HC{XUElW3ctR<FL^bLq1)X?HQ5H^wBbqk2Dn9-GmBdL|K7~FcH zTSYn;)*Y;a7lFu=3)D+x=S>5AYm8C?^N0AiECPi`zVB|IoL#dW)!hwiO2P;WfjG|E z3p*seqil?YZ;@PM*~PDaZFy3ezCfY;T=Bb8m>(wqp_=>XHe0;FX(tM`BPu4eIjj&g zFT1g$FBsc(4-f=>WF!NK7QPn8r0;3PIR@BeBxJiU==8BZt0&A$eV?2rJ*$|(ceg8E zrZ*9T<6?w~oz{`DWW-(ixC1<E!9!S9Vd1^!EcIJ`60jFmm(TGk(q`i1nkSCu86nAM zzPLFO@NIb>@&N7+$Ch6umG>xr>o6{}o+vfsPc|)<&XgkkQzj&E-Sy!;ak%`E1(UuN znur5AQTYS+0snhjYYXcGtAhEbaaHb~*sFm70@5M<|FyVUlVF4Y9`4nonU#kF0xF{a z|89Wk*WK8#fJ<#Vm(5nBPyarUl2InDJgQ0g1J#-&CmgOKD`TH`=H!OL0!Q9WJY)QE z<l^F{gU`n+dLU%ZzcbG3@^1w>4Q|7e#tn4{=rM(-awITok_B3HKei*d4%~W^G=$5P zQ02&@J9V3Ab(-aLqG?T>^-|r2+Lg_QaI~oTGFMv*0lM@h+TXj{x(vLR2C27jrq2Bx zI)&isFd7lB9(t-zQ<rBa3lX_$(*9~)WpLVvPFvU~<nD3r{5q4RlkN(ZR<|NYlU3}N z9Fe1^CSFNY?pIfui<3X^tR5@fqj!ypX3ds%bvb|zK4EYmFfSA6;rDwyyLf5RP_g#; zI6TU~1eCP}m$q3j1htwz?b}pzUBZ+oQMhO+kW>07HKCM+xKd>il+(>HkF4Z(nlfj0 zd*AJk+@;qk^xZ87OA&!yY@;6}OQ|@-MXGh(Uwnv+!e>x_%~1U2mww)!NK+*jzqyDh zPsoAO8uI7HXmZDrko41=(NQf86YJlsZKqXS0x0b>x%B_6G9E%Vo7T6@Qma@kx}WFE zo8AA&+KHPNq-#4&F26Q%WyXL>YjdMhO>VKN-5G)I`ho+^#dh8`+JRyGNOc^-gfm&8 z$*eQnRMQHeXhiD+$!#n(#SW9nAWC4-pEMS+j~cezhi=!iPV-Jsba{&V$}H6mV4=w} z1Z4h30}HH1^o2jq7aKuyP#@c^Vsp}!+{zi64Bf-wF`{};MxUdt;^dA=x_X)wkT56f zFq{L%VNz+&(s39F9|iXlbtGEK8E+7HgOg-2%D2u6ZER(U)OX%YZ0RbhXl5W~H<Jh> zQ6f~>9`vCY<a8)S4<0z-^!^dmU7w`u1mH_3;=d9`gH9CXrkN~k3Q8B1Ybvz|{{bhA zqy;`L{s7~>v^zef%%Lo@LQdQuE5>cMFa3M>$0`Ckv<@wrtc^wg53sq$Z9^Ol`f-XG z&%e$+^lIu$#zYQ+EwhIBZuu_r((1aM7=c*4;7X?ur7;_d3vl6f1<O;_Kcgt7$^f{c zn{ElE!o7wv-)`Ps*=~0vq$WH759GK{FpSncHp&rejk62wLeD1oe5a@AO|z&-*dT{% zp1{ICb@T3Jw!dlf{xn+R6gjEW)1qxQXeEfd$$%dk+Rw&C0+|lY#Q}UElI`0|_;GEx zkx^)P5<8W|JxjUx5tehkT^WKH4*=ykAu-yK=8XKo1l@F5u*MbRm4!>0?MPl5`D3=B zvNHEVFVD9okKk`(?w%iLopEt-y1JnI`=u3L@Z;n|Q@LzMpGSlJE7{q}kMZxhhG49Q zCfFkE4l&E)a&Tlq{)^U11oY>PHSl$lof$Y1(;D^kT^`99IZl1m{Zc}c)&PBd8+9S{ zx^GwG^bYtm2)4PyRj^#0h#4}Xe(}tZ;>3x30}w%OcS);B%|ya-{BR(BW<t^fIh7`d zULy&EH89v?8Biwg72`GZZuY?eh+6hsUPFTbJys+7gxK6(x&jA3JQ4;c2|Oc{S85)g zhrjne)^g>UuYHWt9$j{5R{)H)h+Bb}#AS|XW{L<40}P;NY+#3WR9kdDzwq@PLOEyQ zb^RE9tKa^AmfK|+MUwM`MN^{GjnnO?CsP_I#Ec55W#<t?z`JV68vD(4K_A0zfg**J zrq0wB$>AvT5ecA>p%A%nG=wrVjDHHEGv1VF1zJI9YehfE{w6PdJ_9gaT`9#Wv8aew zW*RS(J&=x$l2{BP9iWN&@v_qP3$Mx&eoAP&>rhEHB84H{L)jcH_Ot#)(~v=wsA96? zB6(tmq&LP5RyMxIwaPERhaZzaB!}M{$RDIshhD-0JG}5VSCWRLytKr$K|rB;9#v^3 zDP$Vy*3}o;&@lL!Py=vn(pBci0s_j&+z)~(=qHlOTfXXCqr`S)m2Kq22V~zGg$Z-d zP;7|@0mFcA1*<zMPbx{)W;4ldNs@30bYd!dbnK=pSIW=rN>f9Rf*?6Cb`y<w`lO72 znOLzA@*VonH6P2Qi1VHfQFz0(26eJtz3!2$(|g7|!CSYLYXh<t38F`(QtgV!La?&g z-gKi)!(5VtWTs2dhgi&QITC|DSGsR)T?$?`Q(;A06)O~7Vu_>S+SyJ$DN8^fO<`D{ z<4>42qM!$^#=)LBHcx=xYE?aOV9yafo_R|n#_OcHC{_mG0*9diGo~i+jMah-adW>k zYP`cWp$gHZaR5ET;JaPe3AnhiYAnr`=Y3Q!JXn7XWp{5sMFQu+ekcmL!Vv`pt{Dm% zs2-@?FrmTsODsWj&{*^hW71DG@BMWw1a`VGG*T+@QnDW3F4}T-U?*q@j2oLB6P}@l zcvt4USasFHct2Rw^04p^UHdZjClgLE{*wE%ly64(cL$(Q*Y6q3nFtRfI&BZcIPxVJ zHj-hQnj##8>XvhlL@k|uO~T!AjkCC$zuiy!8gpi1d5v(@MHLcSswCa?gy0}XO$i>W z+}VMrP*0;DfAvf5DY&|ZPVY`TFm13J{Uh~9ZT<s94b;rNBdSQUsEe^?Ea_1)()Rp3 ztLz4SjVqw$8`<$E$P^*y+>5LlE!D|@4iEmQ>J}|_L~fbh|6Iw}jaVKZW=fV()2{kq zd|~hFYM&Fvh-^35O@azSULR-GLoH8#(Se!=`nZ1&?(@D&GkW53lOQv8o5O)yga~!! zY#j%M`&ceRXb|MJlFPwbq7WN{zYtY&aOT~B4gzqbJ7t%`GL8z})%*cTfq{r^&I<@< z6W%2xEE~8bytl2dq$hmTRKOk&)$KT0P3~S<UtyNr^#Unl<N_Ugq)xt?ki`^#16?(S z_Qbw0LW9lb?zx9xl6`+;2D|ZzRyifjZ$pU1uh-BNDrP*CEe+REMLW^+Z4*P%`IQxi zm<*t1&?au&f9OsCqJRwxhO(8e#@zn1FJc^7b1`TmS*C_|+96^3$8gGj2v6acYTB5d z{Pb+D8|$pdC{y6IFa=+O-tz!_dZ*&GJ5xA<tIp@mO4+K&?UngF{iUQaSNX=5J$&~x z*I&;uqmE454J!Ri&SZ|oI39rav`w|MtQJ6XckK+nQF+-pbB)nTYoKjI<X0}<$8kXO z$yJ)$H5eoJRsmvHAb}wN?6lBP7e0QhDAX5lED)h|UB9%EV;I!m_vd-=^_%b>=|}>( zHG;B>Qzz#8u1hBggx#H?4Orf+Muw-DxAq-HcuM}RXFE{p3sDW?bePO88R9OT4`_g~ ztL36?ye?9j-Yp0`hqBsbd)LGLzS^Sd>+_8xbVEn35!1t*V9n6xC%EV84?oZQ-P;v| zPop*hEN_bxYbvlG`w+=EqMnkl0b8fA?C8_N#5SZ#H#ooY=<T)#@p?p;6uL|H_F4RU z?+K8<Zosgy`8;dp!E)yFZE}nas3QO~Df8(Vm9Z0;r}ujYOz${#Ah`8qqlGc`bV_gn zsJS8u31l*sj*y8KWGX>(ARrjslOi>-@!M+bt0;0o-h=ut@Jf5Dpu$^xhPpjN>H<e| zFLSB`ujAI|ZC$@t!1ZF1eG*3JwB5C_fZ+Pw;DxDqg>SXIT2$;XwH%uV;_?B#slOrO zb)}bd3%MEc7Ae5Lm1EPQC!HGb>TP~=-~||2;LZdkKB%^}P?`zYSHeBl&z)U)s&W}# zt9seBuSS^XoGUsu@1x6GoZ!kM%7n7}GUe~pMn=kJ*dXn$YJ+5y8dQ-GaQ(Tf2L}ZI z08xfIhddZ>p-F#w0JHu<^Ku0^L@xHB-CQ3)cTmm+aATzr{seJ>I{I*crX?{6*u;x) zh<+`1JPT~DHNimNR!Y??&nLI3+jCG#>`}{2t+f&1e^k>dTJb-9pGI4rt*8wvIBKe@ zJYKggu04aKOu0)#?e3KDERj3;vM=4PZnKCp7HCG%h@70dpmzzow}JyC-^y=M$ndD( zg|PA-9GmI2k&CUnAm<palm}Oak=W5wTE|Sq;mHe2ga%*Y=I>!Ewc(53`?dUd5^Vfd zkixs9kn${IK&2!iK&V2$TFWcrEyE^Y%<2!r88mKGt@(Nwa6iLbB16mWv6TEX&B#_c z`s`pVqRzd=JwoxdQuPIRAG?=!@d0f2<`aLxObAAyx&>#o?s&TGjJB8cvDO=a`kk%s zved%3Z$jYKbD)uug5qLB6V>gVN1gqOZp|M~$z}};71lJdk0CD(3ftJf;JZEPr%JP4 z^r^k~&^_)di%)qY+HrU{E$R#HSB~undxpI*WJqQXj?2&duto$V1IvR>?UsB0X+!VM zcd*no(7?I=7&m~BAw)uZhT{moHd`v|>N)y*<1;;+cSv7Lw6u|@^X`ddeaHjdCur$V zp)hyMqu;&%_)r;vd|lP4V}E!2>FiSmZ!Gm1WEt{DL7Md4Fm-F=^#&Dtu<}z8HF&b> z(!@3cX_wRUaWNiHHnxjytsmhKj%U-G!X@Hq_ha+}5y$LKn;kQCOy;BY_>|a|m!VEg z$+_BXvEv}_CpXs)hq{jh1z;USQ)zn#=P;Hgl{q+u-%D=yO?vf=#`8kk2!mAzmVsIL z=S|dku+E)HitH!!|8{PUiK<Qa-z2I~+wX7X*Z)gHo`PxqOGBEE6@u}8Lx!$M>3iK^ z*lnIyVDb?ELX5xe!OZ`QTCzTZ0sog)<$43VNB=i~2^rk<zp<H^;AQatw1>v#mTJ_P zKtRVD|1h%YF@4_zsRso(E9So+0#^Xff`GOw{nQ3NBjVplFINMn2Lv~4u)?TwL61bq z1tTFjv-J9z@`9uDO8NRZ-5CYqn3K!SU~R>FE2>oI?9UDqvBN><o<FEr*0|j6Ipy2B zZGPb1UF3YExJv!JmOMwT;HI#C)k*C^{1o1`7xMjmP?4$}bOMnYT7zGWzZ6~#G<=Is zW-fY79Hz}FzsAFHYP8L|Nf|j%K;>seqs>GsT;$13^NYVOLE-kZvc|P4<1>HS?Xj+3 z$7q;T41+{I)>wCOUgz%z^wxP_julE%4A^DSVL9}VLulvKu1rT7J65KO?f;#HHf>FC z|Ns7&n_A#^|Khc7pmlz&-yHDRcU4A`ero}a+xD&t{_9@?y6u-KIQqY=^3ayEsy8YS zP_A_PPB1t@8=NJ0BI-YgFKuAk#MSro4Snwrp!he#q%Szce={rvgWvx<LFsz}NVsX1 z;Be`WQQ)Zm&IEikIQ@SE1(U#u|K-Zl%~Qai|4H5d8jw)&O|7GSr>6T)YP|G>6mW?2 z^kHncw!BpErvLsS)U(0k{{1>=$YdeP-)z19_md!f2M!7&J$eKi#!!al*R0x{%nXCH z+V~XRI0Lnk<jgFM)HDs%vJ&O2B%SopepI}|)I?;wM!i~udXN<LA4<x=l2qfszh)I; zBbhH=hHTQ}5aOlY5XatO<kGtTkK{-CWj^@IKj(ZYCxe!21p)+Z2L^=ypEJM=zbpW6 zyDWe}F$ZXQ6pb&_z0TCFUU!~E^>UfyK)#Bz7N+jT>a!vX6LuN^0%a}CI&-=zA3v{# zuu_kViU@I6m!r5$YS_vIlu4qKU70vhX4S;-6&73=Q!I{v!lkUQL#uC>MLM^)V<w%P z{ZzIw*I2MdQRGsIf67WgO<O%tti%=DGka()vI5qd6s(%;A15!>a(=J^_BU^}niC2O zGE=4*at?G~`i8$~|IR*D12~8J#GWn~PVq^MevoNs#uqU{)cRmR2~$G&uNk!v8^f;Q zr6g`GaBVp^&n@HL^r4t;3@rPB5<lL=*Z9n>oISi{qIPywDR(W-ag!z5TBv2$*fhh$ zm;yHX=(?ipww^YWes?DB_I+EBwcRz;yzsBv2aNL}$=FkExDCu*N#|a6k`9x*cCNwH zb{$GX-QaD7ueK%=zZtD_d-lM{w%NgLUP2Z1HDehoY;2=N3e5RMML6n*Q+{O}JkFl- zzsFp0B*p_E<Mm<_1$G-=QFzr0yV!wk5&%*HSqI?HV_|Jl@xI$#X@F)AzZs%051$o+ z+^6&H)c4gcT8)zc1q+9qM;x+wf?vcI?T;cciuSHS5>CLD6spChPJeXGDsy?Dg{@x( z<1t@i3MyemYo&UaE9)oU<OxwdGFB{o)qNO*W0`SZK~7DT3J74$Y__5KR71z8y#ZqP zv>t^CkT#o5q#d_R>I!2Ux172skI6`|^%?<rZ*$99+Q%IoY}8qSRfGdgZ=S9*fF)A6 z_d<+Mo@>oyaHBNSXxK2k+mLL+v&~|Z`?CQ|!B<4#kQ?8<Ohvi5O(TWPch;<n*!9vh zJ^NiGu{k$nb63zI;&bsPd}<55D?rfe6csp-M4WA7iMRRh+tgu_axzKrU0BeLqR9pN zqY=uD<7Au9Zx`hXufwI+uf7?%m9Ks0uSuG`pzWQ$bCS7_<2VCs8K(%KHgAKZVkjsg zrWjHq<n<_Z#|&ahFfvIHAt)$vql9<nPoX)8wu&vyU_thL*um_3r!?DeM1YPJW5>Xe z=Ze-$FaMTicy&8BaN7Ztr^e@|uwk<e1Cb8t*1d@P(F`3hkb{!d2eMuf@_Y7kTM{Ja zhN&R?-LfZ7wE+nexIG2&2GNgp@P*6I9v17+3(xNJz+IhU(Ih(hq*HY)(8~tgS96La zyD?T9LHW-GfSP9OvqUk$EnwCxlaQvegWOWSLXbYY`KBjm<ar^>U_X`VmGso~ZcAZ_ z`rM$=2dB`dJ2Rs<IJB*8^=8Xl71-9P=Nrq(Srut|S9-p|z$5bNLLYoPHxD`8Eu(Di zgMa7UJ&|l49f-GA(cDnrGL=0Einm`RSP8f+Bu~m?2I6=qN-<k}0UVBbf7vtdIK=;5 zJQ#t$`SU`LZQ?)d;|JjN?4$A!vp(N=Jvub;!Fx6XdRx35>m2T*c3G>`4Y)=dlPR#c zOiJ#!Bfe6seG>g39OIspPv1|AKPCE|bX*wIFx!XblEW0yF(pcYDVVffaI&X)Fhkbg z%rx51PlIO{+TRHp4j=;NDp%>Px`5dBNe`WRQOC8cqx1~v>7MU`^@KwqP_5^|2|6wS z2S)aYCxnbV%ce4l=274HarAfb-Tm4avZ;JHbkvuE@D6sXs0>mOW9+Y$FV!ZGHfk{t zdzxd?u?yv#yEp!dB|v@50)Izde7(Z*<+s7@RITn&9JI2_69Cw=+>$N+C+<pog2YPb zU&ZvQpDMo}$_<zcB1@ae3Kp~bdd-;>xKLbcvbBReswgXy5nwAx8eJpthqBQrQDh}{ z=k!NIi~b%+O;60Bu5N((#5s}8I3ToLt(>evmUcK-f9~E37MDqoozxs33taj@GAq;` zRG|0<<S}Z}^8qT}_>%tOJ24Zx>8H3bsTQ!{9_%y!kwXMV5$^dh9(yq}?FZ>Ui|Ap} z@i;f3YURB?{sYa+g7DCfqq^BfR>L&D_-9f}-cOsq-ayvvMrX<L`idM}sc7&&9^qWi z{v-yri8Ox-!?Km(oateh*Ze$H<xBNNG6zLcEtGzy=-&gxD=o%GtST=|hbd?*fVue> z$E)vwEv_vf1a3~|a(Y1%y?#=~@_0YW2XiGzdLhaPdb6hZoXH&H`Ow@3&{4R#33Dc> zO_&u!;^kG?znDJf{Je|wS`g4guZ}(6xPtXf!+U8h@b7r@SH@(ji^r4=4Eo!EGRUQ| zHM28iR6GlauAg~_0iZC7rf2OLuYN!YM{ksSEzjd}={+Mo+j%u2dQr)49_cntTbziH zJHh?n6OZ@gH>!+<bayzYghGrFChw#Oc01ZOxq6T<#uzy{01qd?HwLqFuJwrm7}!oq z48PZGs|on(knjX|Og5tB^VOl(R67)s1X%_bc6S1RBwp?j-9pw0nQQgWOvxulo$;>< zl(DvR6KwL4_XJ<kocw^j0uW-Iufto7gK9;{EGDM@KyhQ>`tBg#QWE`!L#t<m#JF08 zg&ZAqR#V8S?<rg>0ujB85|%HZIucG4(?Wbup;{%A!CUp@wQ_b0O`aD)daxZRA+?bs zKA{DqbZDRKH>24CbsWfB9X%C!{DJsF4BRs6SM1BCP>yu;v-BH_g_d@xH9ti4mUl0j z=Z9Z1iEXZ_+ypkEigz@O`+YEspi_bChkD@E9TG^6XAmSPdOg_HO!EF$-gS@PUMlL| zvL8ln>&UjP7vshUR*z|RzVLVBDJDQ;*yLN>e?NAwnaV(U%W4#ZoLufI=LE%w#d-gj zR#IRt+jGc7a3bgEqJYX%r`|qnkBxmyWo0V|BYbmfwQuD8T+d*8>dlNf1iKA&ayh;V zczJX0r@4jTNL+j|hJ9H<R}aR7ZC=W~>XC8pOFNC}JNR#c#St3IdSk~M-2M|{zmagj z1OZbZHxp4lwVpsI(#H=6<Pq{Lp<oB3YA6rKtW<(N-d5L%g(jb?;g_e*eN-lYL#=E^ zBr>^fsLedk=eCBQ>ce%VHjSSYy)Po%EFV&wC=f)(BhG-Bub$GAuirTYoVNtiPckc* zLCH|ywYXSfP(Ox^Kp>%W2A#mfop322)K$d;Zqn255g`cy1}hvYJ`bbIpz*e=wk1t8 zX8M7an0933Yi;me)*fSG9Np<x%(v&em~XiakgaC@>f;@WM}9^+SvGoT4QPJFzqbJh z4ir=8<I||{>&ON)`7uw_m#ISsUAX$fw!WF0Tl_b<oDNd#&kS03W{YG}pL1l;5SiWu zTH1(%R<Yp#jp-E4{n?9-&uW>*h7(sZHiC!=7kW3WoeHub(3@H5KcD4*I{M>htc9Uy z{<?k#DE#9Yu|9Pf4}e<V;bAHp&+Su=CfnspoifhDLP+n?@S&2yqm9l1zb926-P?&8 z=+{IeK%ea<kJd-$L4q3gnH%G{9Tc+hc*x}WrTPTWOt-)$5Mnp*>}9}ka(Z81KhO(_ zK6iMsizMj@fGJZZU62(iWW5hRV#93ktIji8K`b1FoQ3qpl!sR^aOm(eQDU~F9EuOf z9?yxoSuG=%q74Squ}GI<YrW?j{j{F#^-Dqn{d8P?9VwLg+29=quEm47S1?&~KH;A` zjk5wcpLx7kik^5u%bbKF%y{Q#`FLTl`;C)jV%=%#+bFDkrE9Umk(Rd)Pw}=_$Y$;+ zj*g%>vDi#Gx}nA+c21nwjvV#LY9~tzR+>uUcyvzVfZ#|2*0JZ+;MTQ}F(>`7O(&KP z9OYpQ@qYkSK&ro;JoU>Re=|+NfCL#r=0!e}uRcvQj(>;m4)PBy&;JN<ySK&va2WpY z=KOOQ{u}fCLgGP+z)*r%DLjm$6orFpOkSxwg>Sw}D|3fI1o=|!WILu@`SJcS&bH{b za_yMda$E5pFTb4tuGhhAjiz7wbs*bm;kPi3M7y~;vgwO#6YrsKv(13E6M>a}ufcF- z@L{?;WPkq7B7IY4-bI(UZyIe`zcnbYG=1G0HY*QkI~G_6!0^V!F=YF=K1dqv^$^*| zYWBDc@6~}Trzf%PWMW-w3jQT`Z?_^n?1x3VDfPyc^^Sh8>Qc<$?eL(4?oVnb<5sL! zf9~D=$B`KL9Me8R+BYwXzYb}@M@;)Q63dXbhJS!hk$7tsSw2T%;6L3!K2{C<C(Hg= z^+wHszvAZKJ5Wv+0(UiZeAWSfVV@Vuf>ox-i~!JkO02YdO<Zqn1g2Ja&L06pFH$Jp z4+pL`af7Gpn7Ia%Y|4tmtMhSM`leDtR!y%U5G0%ySr4twYU0JW?#m=n8`P59nA$wX z(tkN@LWb#(SXy@-;b*R5qJhW6>rVvhn+pU;s(Z!tKzJm@th~6>Br&Jgovo1Q91!P` zfV^ZLxT8ErYHGs+D#PRYbQh|QGd*7VJCKV`DYoPL;Kl-(hwvRaLn)_66D|?T+}?Vn zwMs>Dj7B~r6$>t&a&xbAb0)9CWl$BM<bUskAoTitrXG$v+I5RS$4AlBXm&k%XrAly z&7M^PlJEVy!=xHaycDH`S&W;T;>JM8aRUK9a{by@7*>q6{?u<yckp7fc|qCoxSb=b zNHeKbQcE_fXZ-SbLRZhEQ;zRY1pLslHMPcNo*KC2=FE$x@$>r(JzMJ3<$Y&Olz$CL zCyNqf1yPdQVeqUpkb?jo+$W0z7hLuOFSZ|WfkBM-sx|8=KTA+MaQ(p(4b$Tkou}aH zw2+1-`RKYGp$Ef7-QYQo=m~Je&Uv@vS%+GLe_d8!fL&g7T-^GH0`}6?O^ug92Hw#^ z4G=L;VA8hu{f^diakf{q1snv0Gk*r><T-BFoq~2$7L0798-IV%R#HDYk~qrvNnvZp z71XM2^{hLDgRXe_ocae7xL`Rp(YZK=(~&Dd`uV*C@+(QPAHBRkRjdHNlN4j)KFH8; zj%6|&mq$73kLN)2dRJ(_D2f3;ONxD=OZ<(7pXT9QBP<_;O4*(tRUcl3D1UdFHedmD zWwAo;1q#{ZxZ0vP^Xi<ixOy#Cn}m3#mEK;wtL`p!?^m<Rc|ECC_A#H>GYJ_ikZbzc z=#n?@9Pj89TDNoG-y{-DPO$PMaSWo;>?Ydw3I%R2vvxEJd9&~5$@Q_p3*R`<2bTD1 zrXze(Oy%O`IUhyZxNgSk#g_^f2M~XgQJK*9-U#eWF7~(q_QPERfP2RDD5j4>AR0HR zh?MK)X~S)jUr$q_VAn_;y)ZD!bp1|MR|TSjCY07J2@E))m>ongm69_~oCnZae?3ur zL@udYD2V*J4f^%HaJ|AKapr|YL!41vx+`PK&GcPDfafrt-_v+yag2elGo^pvXG$$! zR)yJ9Jb_h#Q<1#B6MHyWd9I63ZFtd+m{WB(v9m-An4a~qBp;Wc<a5(vCDmLmV@QH` z?OEl=&haRTA-M@JPgmv`xVJ`<m<moN{@tDn`uPOJiyg>NM7-GM9jn-VqGz5sTvQU% zn1k4w>tMCJ7iE7ropI-&rDcDga)(rVYr%>{GYUuyRdyDAMgtRzBG7&jsOCHvQiKnB zx15|9G%rzFg4BWaa`|w%Iz>gDhLkN?siJN=Kubbl*@<dYC;oy^bq~j&Tfnh!f3I(x z8_|G@7RRwRmpF%=qM;x8@mh;znt5Xw1`J@)2eD&kYbufUl^^)OpL16JU-nYukLE`f z<QGhq#{W|L<p2GgW&WG@{KzYRHU1Zz20<%LhBuuRoZMP~Aq?5Nf(hu$#7dHG3EvFg z8h1%_15KM}*<NN8r<*DXMs9>R*`+zDub1r@2Pl8<ilU-)%cOwO2B+3}sc8GRa%wo< zH1bk(Pn1yKn|wihM{>Jv{JQL|@M^Do-6%P=6})0QFidwD`OQ7^FV)vsa|37T4_kDN z_AHs0C8~xhoifeSXIBAD;y;<GRX6V^0XT`HZ)|e0Zn3xv!2OYE$VdGH^#m6!cO<I< z)RKR{YK*%N@XgWiD#5h{w_mXoWN;>EIPoNvbo|k0D_q>(MV?#x+pwV^;71~*q_-r> zM>1stXe&^o5$%s;$}iVG9^)tJ&j0K&fbBW{o5%PiGyUx`m_TE+Uoz7lzkzk*l4ww- z86}F({>$&AegLK-Jy;4mb&$UT4vbSIFuZ>WkGBsO^Hk#!$au2U7UnC0oTh)J#*@AK z0@Y}*i)bbX3AoG?F=ZH;_U(7I?cWpveN{BHvSQ#TwQY&FTp0{sOiItwizLK3;D%wL z*Pra4)V6<&1-=;kD^<>QWu*fmUGQ}8WeJ2nJ$8w1x5~jGy>5UqDq|UKPh4USS^9rm z2$j;KQ_jjJr#>UoKD<*1*Qi5vTo&D@2B&_Ky*e4)FwJe<2f!iCV(o8dosIvqy=u#% z{;!o=8t(szS7y0?X&HvI1uGH$IsE<2@gJe@k5|8kJ`w)a1qfL=DN5of2q7Sd66=ot z<*DFSlmczNU0dhZE}psaISSp*-&TJ*i0_s@B(a4XYW?^d)q`CJlR~yonSK*1Ah*N0 zXxAA)_a?NI+KXb6y=EubYID-f9OyS8Z~eZT^H92Fl-HX#H{X@&l3Pz0yo(NJ$nH@I zZ~0>++^%QwZV|NCpsq1)9Ru!P#>plv72$iCP`K%Ff`4iED_C1DdH!)KsPKO#4fYiw z>IDdYC8DSCpF>`)oHffF`5!XBJ!g4}>w>l96pUj!KdBu|E1lbTnxHEdh~|J>QD<+j zmtmy!?fwz5W{vYl!;ukf@jfEfFV{X+@khk^4_5(f%lLH_f0#-9rI&=p2|!}qt<1H+ zM9vOe`Mw}}&a4dn?PJn|g^z!RF+QK-V_{MJz?MyPy*AVpr?lH812G+xM_{PAw$BgG zLeJmorMh7hkBB@*Rf&7K!hJ+*GG>k-eDhA23%h5@RqK)@1@J(`9P<i*x7?%hJ*&cq zX(nf>BH<}wBqAN&N1ou{RM^_xF&-N?J=i^af3?dcbPR`QHUGYxZ%=>Vaj;{OeK^<i zLllZjDwA41I^g-d-01F%vldshms@)ZVcj=W(*p_L3a?%28Mr1Erw)jp@ll5E(QL0j zcr!UK(y{Q!#*WqZJ;?NDaxthn>!O-Os`hk*K^Jde|2Zid2%n?*0!(M2YeS-5M(BN_ zSGM}Pp4@AX3YwOy$Z>zAn%5g2JSfO)Z;xCp{rdRQSJL#B)aG15XW+%ogxW+!gM?=+ z(AovRyk=q6?J+v?RoJCk=Q7JRZ<B+Hhot9Bp>iP*xQv6~)f582b)te#)MIil#=1a6 zFM2d+49m4U>Iex)azEV&F+Q9dkwb;>=`!RRTJJBPIPe0JO#pxA3>k$@w{E%kMcK{- zH-`E`9ZF0+!<Os@dSlWzk2!)z9-roN&f0(iqbF*WK^NsSpvIlc37nt97miqY<U)4B zrCM+?NM2A4Iv}aUo;uDk&Y)B#^iucEo?)2D{bIQR&I0Mh%1@SdXpYc1zPB&5cj`B5 zc`9$IMRVgHTFZZfb3=T1N$d>}KkaV7-`U=N+TB>$hVE0J<|uQ0E!C(132Ehz+IrJp z$@PBkzxQ=Cd>h&9%c@5&1~ZT0iL@03Ps<jlu18UWutqcu(Ev4O8IuvC^EB`;7Ix!{ z{^+tYiof3$Mo9?PVK1<T0yiY{%l(DAED+%Y3YF4PiR*vUtfi+mxo0+bk9aQ)s!Fqv zm%lu<<yu}W3erIG#LnRT8MRJ!<t@#ufwo|2n5{kPz%WPJzaNPTrd-XX;Q&P~ae@mb zvvLNdS+SOKy78~Kp{04I#6a*q96`WPIO45~OPohaoQBMFee^6IVB}3xALk_D9$i%t zmJFjohSYzom>LO<m10v#!RT)_(fT}Ey?;X57OixlpE<G87w?`?4{><VgT<C5#uO*c zon7rHNIdo;3L|h;<pd94aIJv4PryLzCnuN5wz)3yN#~4f5JduaxIaf%y1Q<1yHE|P ztbbtjCG1aechP3-BIWKZ8^CjXDtOl)B)lyAhJ$~3-m~wt?<4J1SCK;t8P#Fsh@CuY z?D-V7usS^w4s?ZM@7hyV0w7XR(WO^rJ2%vaWe(#KSY>10y8GjNxaWj)d_Av;A6}(< zJ~jy11WNeAFjnDU`W95c`F#x?3DtD%fnAAkISJz9Ilo&wYxTgc4HWL2CuWzbC-f;j z@iTvQOj+@0G_d^wu?r7CYHfRxLe#}o8^lLxi>MB2=}N@g_P`i6ViB2-kx*;-Fd@%q zel)4VKk7Y`q!6K90n=g<WsT)GFHz(?{`=X0T_tl2hx9{3{C94K#!o|~rcPE=(EUGK zaq(xa_&+n_2b}u5v;C5bCD9dQkqAt{1crZXZj0;ZFa^U94u1u;GIEn?+EmGRV~FTm zYt6S3;CSoF-#YK3??#E>*WO<1ePp(Qo|RgryXp4|%g~+bVbliHLTo#|Un%Ei(nW2I za7AZZ8Qt#^u<QNUj`CJ`wv|gK8<IkH=R}g+UA^!P52DyM4BagkzkLaUyRIj+e}aE+ zik|Do^(mn6f5XwP3XuE_%S8d+pauCuwo_xW*tqpig#>*_eYNwKa7z~DuM)6-h-9v_ z*_WvcFW3<N$)`|nw&U;sCMQ64zEO@%jY<@(dGjw!U;z$nMvmaAS|@nK^SBoeuM}?A zhWRc$)oco!K#W+3e+}xNtQLOwF@Aq($LRdbK(z@l91YYQ<tGZNO&`}jp7Y;5Ch&Kj z^WQxt@OPf`-#zBnwv*1^M~nci)`@H1)H9dbpsyN(wN<IWXiJi(^IJ%;WNz80zS1Re z_hBg`^8@SScuC>Y0$!H~(2ehbrD^4IJ?baBl`Ay9iZa(1>Iv*`bfzXj=7oQ<b4?@> zAA?KbmxmX3N{-&bF>&$+h^>jH$+^c>zTKYeyBv)gQ4aD+dzV#Joe7;jqj!jRP^RsA z=h4tlhmKmK#soNeEhGS4IvA^18G3ZXb-XsJ2wLPJorkBwJNoJRJQH@@2poegPu?M* zVtNLdMKu>4<Y{&Vdn9Nq*)x9+68#>>ROJtqB<a#4phT55iTk1ubTJ<*%I)IOBF@VB zb!*Xg*5p?vHJknnDDCxNbaEcO22^MWojct@5uRY)5d75j51xk8tq?2vTt#=wICENg zQJIBc(p;_`s0OTlc`uho;rIvX#G@d)5NAQZ6G#-8_ZC7R=5c{<tvrA8Xrv%lzst+% z)oA0};*G`6M&KrcUi_GE1i8$GDB_fOCpdXj6EEi9JlYB@N^#SeYHd*HZ|sg1CzNrS zl4sRLn;;N~o2H|j>e=Wk=0;gwja>=_;pu2Shxgr;45;kP%Do?`GE}ZAPvz_+KkgKY zOo3muF~D3%YI#Q&3pRg(sj!ClrphnrVD|Leij4xnZoU~?nStcN+2#dGciXbaRK-Sm zhQB}$pdSl=bFmXcjLr3hsOYPE5V+Iuw)=@ZxzH3Q*eyN3mgq~w2>e@ZcIA<amst1v z9`2}ydJ98ybX8k3ap|MZ0QgiK_*=wym-G>RaSq`;I!EM!);fQQ!h~M{(73rLuCG*E zydM<k!oyGb>>j0a@rWX?;5=@92ickc8D@+aIU4wpkc3GHFRn0LvlMvG6=Kq^G-8(h zAfFYI@zIHzMD|9rS_Yj$8k7QsyGw)HHes>;ksw$z<|=P4(|HC>pcan@w6vw1iq4qT zK~W-eXhiGniEV$aB4zb9_WHcIo3(ZApLK}lJyz+-m9Ke7^luChvR7B1QeMb9Chlhs zspY$@<ahKDK?;1tX|6x{Nu|$_=$r=d!aT!=j5Vdbe!C%xc%V}Scj>Xt48co)5Yf>o z^|x~~x=zSv47?&xk*4?4tTYr@2Sat45Syc5UsC$uZJ&P~KnvOXaVe8!X3n^GyBPP3 z1G6x=2QaR`LRv*{l7ogvi)(3&i{t0()z(;UfwEOtu$cnw8>5HCo*pt~pxMcGrh`FZ z_!Sw9dwOY=^)FjhrbIrz7tp1tvwcN0D>bOJ;si69Zh%+BVwiff=}S(Zi%P?ic>H)A zjFLT?=^}smC2rYd7HHRiJ!CQIAI5trX<BAA3EtjF;D(p1f-E-;<j)IvB<T}f<O+6r z!X(}C!iu5QQ^=Qs*`D5Obe~ZXGZ?R!MuondT9X2Jo;{SXQs($Q&L80V9=dayw5SUC zprpG`cP!z^_QBAmpt~EO+0xl%QO;+W5}uOboPd8hW#NbMFygC}MjUy0O4WaX7`IBf z^9Eo3=b__&H}!8Z<X=tuYo=m5p#wn_LMZ~pKpY`Kih!UMPk!y+9c{2L-4D~SWJ84H z?gy5VTgMoPZT;ITA`FQwWq_ey_rRfBfd~n2pby<!+%}i7Z@Q<@R(_WtTcRM`4Y$|1 z<J}68dn*&neg`Je@0Wrm2P%KX!|<*R5AW;Wg{DICe@B0*0nhq7s`$|ZqH&jJ#&TqH z<uI#GsN_$n3R$#1i#`2{uo&%kK+^dPuz-)Qv%di(agEDAnkS|YfCYR)Sl^oGI2qE5 zzXvLT4^+7VNJw*-&!BR@_VY7;eq`XEKJ({C2L9<Ye|}`(??3a`IS_x~mn4Wp5Ru`R zD5A}*_RO)`h~E!e7gYwWgcFT8C2ijjtJP-bsl=k8H98<%RI$wYHVEJrS!_#fBnIOQ zTZ<ivb=vzGhf8x|nBV0<fbYcw-%p8Ljvix?{?vNifV}upm>zUruO#$NymDW<P*m+9 zUTMv*R`Z-S(xXMoIdgvqTsX(`%j@*!gM&0h(J@TWdKiklAIrtk&=wi<8^CcfE3@F8 z#%W==3>H-{Xb{h06dAG3-^v^P5Xx&DqTL*$SBv;BqcTFG|9?g0|9I+uAC>>jw7+N$ z5g<xY1O?+5h>!?`6A(d?B!olcmm1P(xI0?!US^xB&z|(yY%PDljpeUYJKbA?VPeac zB>3-CHL|_6Io_2t@ZH)GM7MB>CtHEvCaJN;U~J1R<K&+7P4+DBZ#(m_EzTq4wjsnj zr(b7Gqph`$*k_6N8bKJ_1ckQHn{6inTTUq2S`614*TGv-8{SH^w|?k!OC2ZJ-tGLC z5`64q=YsZ+sLX$=uu#Kupx`zg@-gwb(e(d{sH~y--=z9KM`c*&^v_XwzxIDcRQ@~9 z{B_SX@S|y39768uLM$gpOo$10?>end`L>7L)NG^7nzNJ5xoi}EXmp2beml_p94kOc zt<hOCPxle;F0}okmVl~Rqq>jnslbQV(WzD>FQmt2ewTl)fIZAQUy9-hqR*bB0s(-| zC;!IX>gJ?#8qfbur0D!hd6=nj%;?jfQrMQ)`nCdg{5)8t8l6^%-j$C>gtOJ33%DON z$5eDlBLE$ou9l@7nzMFzSrs1l2<R#r$G<QhtSIIw&+)@LlvH=JF)(+J6&^hU;ri-) zBz|=)pow5{&Axc;`o^C=rVLm>tH--v%vDAerG(^13?C-;AhKOr9{s2Ev;qP}Z#vmq z_mlSq?C~EoP3yGBTfeyE{yq?Y8_T%1tn;@0ud_-!f?;WU-Sy`{|35kJN0k5DbN(Ec z1u6$4e|)JkM{eQ_TfxnqnBI!9u`K}|AzK@Bx+knxup4FDaV!;mT`;uEO=R#^60~j% zXt<@RA#^XT{sx}Y-JWUJMUD61yGb=Azb_af$?di^Y$x$nUB0<D?FvupciL2up#7*f z+Y<2Wt8lm-B&U0!IJg7xcz14E$A`(5Mkk_8e`#qY0qaJwDOdb;_Pp7SbkX)lCpI0J zqtwbOl3fLDZYLA8rh^`sjtTy$JaX^E4xLY(*pNIaC0%_SVM;E>c$H_%{2C;|+ORx* zT?M0=o+Sbfr0C!3rRlfk$fNeXPMTwD!}J2dE&lNJ{ZOX(Iivyq_Q=-BM}u=wo!d`r zf0ysqW^HjX-Kj%OtUjAx_O@pGE2;i&e#t-|t6)7(SDeao+?g>QUU037vK^b@fCy2> zH8jr}M}M5cKAlRqSe~3)@HF^$q5HbRd$%}dfaGd5yBfJv{jJXH?~T_mbQYbf>d`=9 zwl6MVB3Crtm12Pc=*`fhuP;TdKA|wTe~ElBAJ->*?W#*D)a?{sK}W-3ONvb{lmLWG zTHFrOi~mH>!%cp0{N+&MRu{jN=5WfELU5W<hBd9X>Gw6%YJ{882qSKiRFE|fNR91? zd<#Aft{sREQ)#?{jv|G=!G|0YiL3rXUuz(~XQ}O-WAJjgAMxVO@V(_!LAU^wfA>m` zPi8rtyU}`GPb{5D@l{oVUIXi6s8P)AgeEj~sV;CB#?QliHVQqrA2cJkC;tp|%iAum zypd=Zb><h0Qr^a!o1VmlBwUvAiP#=NW{kphpBZ?&g*DK<1{Uf^q!IN^3k)o&v50u@ zCja;_B5YA8M^9z0Inn+=4AU?;f0t5@b#sWtcP8|NX}j&pF2FGoO;O5#M}RDz6dCT) zFwdvL5VT#EBV0h{;{<i@kW4A=8ndsbE1j=`KE~Qzein!u!ouRRTRFIiWg;oR&}Q-8 z7{but39t2M)<)cE;lUFDCQo`Mi^Gv`mKT+{;-#rXcu7=Rp76N=z_dF&f6h}PHro3{ zz9AB{6Qhl<eh?OMqL1~>(|d4Qh+7>`UUx7vyh|=LA;M>a){zik6{hG7w<oIN5l(5~ zskmgnjpsGDG<;%uPmcyNEoc80sku1x=M;gYg{@!JpzsIyv^g;;Z$a=!(O)<IK%I1V zqVMW6kxdN-AyGAduWtoDe-AQ%;F7MYNJlvxLkHTT5bDe4yT7e(1$-fJ^+y#qr=D`v zNY0)qB8h6N16$EHuLfXwihLrUPb;Bp8}R0#NAdi4onT0E-PhTz8@s-U%n>u+hICwi ziMI=bId8b1<$(=GM|cU{Ch`=8eF?;GBp%+2<Q@D`mZS?baSRW+e@w|fIgFR4WqIN* zrW#GI37k`A^s6(~z9PV9kB=wu4jfc>!UhYPquRM&JxQ(`SBMi-B*FwY7gR)|>E=6W zlQcGgI|Z*Kc&ekC8>EfwvMO+UfjTK*hP{M@G1FO5DHqi2!T}jfxzajIUI@%?!H5~- z_tVLF1ifL;7dw+4e;cS~3e-|3EAeew<V5h$tMi!aLT+@c9%;WulJ>5sN+vQL0#B6v zwnj%grEdm7K+cgkav^}{k>rLJ8Y{g~k@(iv&>_smYBn=OZ@MXr++}X7#~~QXC(K=6 zT#=_ll@CVGV61Z^foE}IE9`301b(1fT0m^NKw&Z|I4hstfBxxh))0H7C0x8;^f)7A zR5T3kj#_rlr6<(_RK_`S_HB-zm*|>5qQbTPwiT2&BG}~X2p{M4rnd*Qf21<vv{e*z zV5N`l)nSkQp}PW@oFZ|;-XJ9QAYQ$agErkgI6|zTKOY)VKHkKpSbuTHD7nA=_C|=l zcjgNU?gjL4e=>mN28U&Ailq}G!r4eX@s5<`;yp#!$)mKVUQm^isuFVdym~IWE{9fi zepYUP9FFgI4N%u>T4c%mw=p2i{>!4};9r8SIseOIwSU?Cx(RWH#oOzrqH7uUiqd|( zYPg%XQ`&t(eE<0@ANcNX%<yZQG=!n>%GOszhhPY_e`)FvFu7Ti6C?y;7_|Nee%+u) zZHK8;yq`oOTcSSMWrU+>vtWwj?XYy?zwnk1kG_U<IJ|={YUkGARz<m;$s!xi-Uv6c zi)-#;J#eyfd1^b!UGW(DJ9Et~_r6P%MbKVfxvK%MD~X8xVz!;!Lc5SY7;a#WOtw$L zy%BGPmz*#M6o2pducd>d4bo*lq=QX=@aM?5@+f?ZMOaN9ES}WPpW39+&slom7aE_B zvQ=Ta$3nIVf)iJ+;r6?)665N1eD@a3%11!h>ldvXx9-*}#uMBbva~Xv^>XD`d^4#W z-F_5K%b>9JAv3nu<R6o-^mV0Epl5qk&9C@yoq%yHH-GWmigJFakvZ}PxG-&r7Y+I@ zM)v(0@crVLa-STnHZ?Nm8)@7ft@yxjJhOKzUDr%><i5c^72s%ZLz#8F@V>_QA9dj8 ztqy#hq<0+|)k=TVIP0X@<9NzHP^->tQo?}g)}j2<?6RN>ZXGBVPuc4P+v7=|r<~p2 zsFwY~z<<vn?m=gHZih^-p0=FYSFq6^kAbP(gh_8?B6Z$l_P{iv>62qI$rA=u3Fsmj z7ik>4`tuUh@J&Bgu*yFo5{$?Ww4N5D-;YTq977UzVs!oaCDVdaEt8xI6KYO?dtX^K zh#Lj|-Q@Zo+5iDR5~;L^sfQJ`_(4uqN7KCEuYY(tKEOMs{8_cEqBYUqx}R76`_^8j zrh|NT@?Mr-A{DBM?;v^FRvLm9apcOvA>XlO#g@aE52bdo#BI2RG0X=huMFJeaQi;x zg>+tsSK<5Z!#I&@P_)6QS@6&)E35&JB1%b%-^Sz-)7}g;DfA_I^0_ht!{HxPO?~6D ze}8EHS}4B8$)B!1hX2)tpMve*zUd>jesRMuu@u8#93pU%MDdjpgBVIs7)jy;1|gd_ z2}WWoEJR_9`0_|}<*+N6MZhiGteg_ZH_=53-FPRycc8%NcHp>%-A#-v`&z6F*#p<E z9~OrjT~6>VW0m5Y-Y*&M9n<I*WY@qNZGY~38;M<Sp}^m{l4RQn>l(!IWLIDhH+Q@> z%CEDK-z-V8?QoOWWhB;bwiU%gn?}Vta(#7cjRrTmpN3m3#*tm4g4*h)(3L>{Wlzm# zvtpZ1PQ*KjOHsk->)0l0!ez=Ovpy3yj&^B4<NZ%6Uy7hv)hPV1^7vr5(>DP}5r2c# ztA6`X?ghR}_x?KQJD+q*6aF3&F>Q0m`|eBpU7vB&8=HZjE{@hGfkv}(cjvp0LrDJP z98vhDwHQ789*AN7CmTJ@;lW=AeJAF&&iVhythjqg=8UU)jYY5Coc-k4RJs*4&q6&_ z(&e0hB;T=KssKhPc=#m-lW}@uf`6O1#ntBFK1D6~*(0!`)ujG-l3jY&bCWsTJ2gN{ ztaj`C=D*&i2n?bu(6{jT!t;_QLYE_^cciH*6ov$6JmAq(2|2;;h?Tw)UL8ou9F)vy zL&%FN{ha^^giCD25ihHL9gf+_m&+9H#52?qUPhT@eju(wHFK7uzZ8lUSAVYAhezX; zWunk#?n=&DWFS#XmE<*0;u;>eO32&8MM3xn;q!fT37M)%K@qy)3rC&~Rd9B$xA?&J zGCJBkFhdC3i|rC*6Z@$7yF7F1*>bQvg=!&Z&f?v^26=fdrJ+2LY;v%iSv(4taD8l5 zz6ii_&C&b2XTw|uMOnK#Pk-xjP(!xQj0@r*%kUucWWfm0)?cb_%`vE7vy?+2xIPPu z&jW{e0|$wKynPLp)=|&7h^|&*zEF`$>MB~X#!J4B4%bPJ%N!K#a=Ob3R<`ydFIXLr zkIf^FAK6q1V8bJDWks>oOxfw8O_wXL!QzqaONBZdg2csxgOJTca(|r?;#|VQv~<7( zcfowr36kxkXHs{mXTgyI#?L!y;MYoPS-ov2GSWCP>%4V~xWCk(eZ@h-7LTe80cI9n zxqpl%;ejKCN;Aq*wsi+rhnyO&?d@UqaAywEBSX&jQ!b7}-Zr?%HELYPt;+)FuL{k7 z*`NHmJsJ3*yEHVvVt-VWT5TSm{l!Jg@M34Aa9{DnZwah4p#X9ASyk<$>?yxiY8DTD zo-~!bIN4)(vJGL0Rr=Cvp#f3yN#&IrDXygG#$-@Lg?c%lyc@+fIOt7g&rm-RVY&Wn zrTd!?%kiEewM2|Vt4&uNiFi2-Bp34ZP$h+@L0px#F95jG5P$oiihX*S^Qpoo>Kr)% zDyPHwy)0O9z?a6?7Hh(VI0vu16)%ebckXPO?75dK;Q4qwr4BDJQV4{AOwj5bo}U!V zvd{}wpI1R|Iefr1FMXI!`_Pg_oGeDbuPT24CrbbX>sBbt*`7hfxXNcnA7ryPaXE_0 zoA*2@$TPSbZhum#BBsCY3NgS8C`Px-OF4BysrJC@a`TSQ0tXi-rL_7sj<`rwZ{9ah zJtf@vBHb;|o*9$SK?!MFB|sAh7u9zRv1)EtK+x!V;auS8l*9I;x}~gYny<w&Init) zlwZi_gUGI9uIu^iYptALO=jusVVu*p7%<>{@UwEh;D0wCyh^!xxOz12k;j7?EyGo# z!<Gm$MT@c+zR49kx)ev*;@h$3$>IFV;|!of<xf$wJSMwj1Q?66H4x|}tdT-EI<}fA zLsBiT2ahYG9~_4AF!<B;ifVA=CSr^OI+Dg11y>vra6$9r$mfyZcm?$-so-FWfKEJ> zD5=ncIX`A5ety(xygR;{h_p}d2af<l^S*Z1d~%5Mqt7*GsqN%dc)=o(&~+>klXs_- zed7NygyR1b2=&{Weg~oWua{pq2OEE1W*H)ECvEt~8IyD?#vzf7FmALF-V7WNbgR-1 z<E^{~{~AKAI4RmSR}*Zbg5+M;okrUPE5${_jo+>lBgpQ!kZt<Y(atc*-)hF}BUZq) zsb23?a<i#PH(?kI+<*}RZS_4=wyg@KHoBY;dnyFpzH5wc3tGAGy8iVawZnfbXjc=% zHp2Rsl$70i^aAl$2vzEDNL}uVSPGnPCU@h;{VBV=q9@?LflnKH0)7XdEa$J`(>H75 z{{lX3=n426@JZO<)9*ZIfNw%ucFet`bAKpO_mM#Qxvi}>?X_Dq_jLMH$ku(2i_P9R z32yFVJx(J(R!rHKEnpqmL)U-MuO%BBE_rvu$rkkkG_o`U;ZIFXXUu-EdLxekM=0{( zqE2)G<8>RqWwn?jz<h0)Gs}Ljp#5v1t<O4Jz_)a)IkC@m7wEzStSmKGF9mixOngN~ ze>#%*p{K>u2xOZ85e!3tnQXO2m3xFQk79vYOw=A=<91n44Qk)GmZpEv$Ji@SD}VdL z(QSxFsg5yS(j;)OB)8-Vdb~)i;LdG3fvV<_gk;Ec4Vn`+U`t&b!@55oSUO#Fc_qQY z5s{kcT_E&7;DV~b(5hp(w(~C-guzXE|9{2J{hq$!^k4VpQS{CF#?1dE-XrOAJo|_D z{s?QI?)xRCffR&;6or4nC=8M?1(7I*QYZ#vU&gR+#*+B1cd&A<bU#X48QRu-yo*Jz z+z{L|o5_|q{W6AaEiJ?*MFk;SOxXfQyfp@|*SERD?Vt@IwzvcBwUFqpRYj~}=y#Pd zYt(_M?aoaVVL#?e(7i?x-d0HL1_4yG8Mi0l78Td)>k2S<i<5urjoGf!wNA1!PZ-=9 zj#t{b2IDontxS*#|B?pg8x4eiWJ=Xb^`<UEe=h0)4^sC_zZu8*<Tqn_<|Hrl?MKze z-R(Cc;OOGbKJ4bvg?fiqYs+PB3e!?_QRFS&0p9~2w*@?hr&!aST(|ZdkKOu;<?&k_ zlQ+q#=~EnAKVN^N=+89J`INK$JF5b|TGn5#YK{FLwlct<d}Z?8SBBkvWpbW=71-rv zbun=ay%J{-aUL8WJ@AfuDL9OZ04wZ+6ym`{I;+K_6p+h%7-A=@KsOyQ9%XUV5Z(zB zAIs=XiKv@$YQFS~A!108Qvu-5k^Y=thIf9WXvdHiiRFJ8cbMa&N{dl=s+t($0=jA5 zrnk8kzuV(G6A10u^k!F<2w+~$r)8j?C7c_z6V;1)*Vy=}B93U*U8zY&=u(%?3PYDx zRdGSRpOdz|d*Bg#*4B9hW~Fdo8@yN;ZRNO(%dOmaxcWMFtaduF_biQDvwX9;Xw?2h zu2^cGgSLNU*Ecj09h3vW)KXyiX2y4D<=$<91q<3~(n&7lv3<Ya!<%MNo@+Y(MAMMi z_7dJ&dcygcQG`>d0Cs4XPxgJHI}4@6;xwifuD^sq#QZ1$?>eMaV%!|h?%^B@sGHmU z>?@REDMc?y_eug@)Qa2<#|_8#a(yhWA~VwIQf+_YxZTm^Ua$d8u|3SVkS91OUggfs zAnsu*=s`lK5(LmwEpnL$8jbu0MYOBJm2Ggnh(9`>7lJfr5a@ZiHu<m=3_2B7=H^Hh zjJH-KD@sd&<P>xryCC#9%bYg2aczi$$cQ9%YsW!6n#X#SrR=1G0_J&2S$duQAiZY& zAtisWvpfPVk(OGJJYGI|<={n%4`5Y`gPdb8^Ij$jb9zrgck67o>RVhgOGTYc_le2& zHS(zx1|Fy6VBQ~uvHtp3dPmctUTWfR>y-XP6M@fe>4HAKB}%yI$jVcxtJh7+6mn1- zDr4~{v-EW=@Y}&(wBT)LFSN;8Brmfe&%J*~xTFD(J!XWcawCSH8jf$;#xR~jc;RoX z=)Kx&+$*jo&AphoNhlr(fmi*~B$hrAYjjjt;My;ZRp*j!$|I^ypxte~H^)fq+3^K0 z+%bMH)RpS#84J88*sIP(>-vBb=GkVuJ^^rfJlhDvfwFpBY4Gu>IP}n@RPi>Plnj5* z_bE&i_(G<g$L9AbW+?VK`nTLN?*7dRGT<N|Pqf7uN_@tA=ABPL#)0!JYvGB~jKu^s z+9qQjLl0Nzpa&<!ar6UxWSoOW7f@6H)etvbr66&kKc<DWgh*Yn%;m8D_6hcNShxM` zc;V0IJDlb@ox)Pw_EGv|_$%5c%9(!v#@BpsT}L3`yjD=IoKKv~zn9?Y#EFi~U=_bY zNEI)6N;Ox>H|>x*Je_oX?@jjL7!2UZM+~`K13`mNidTYYcj2Dq-Gs!^^$Cwg4g-DE zEI-RZS7;Jffw2oFvHV*cXa0e+faV$pXx-485Pm~#m-BD(G)}B$2R?+*J#>G431`S9 zLGH${?<<e9(tF7#ZP#EAwtrL)0DV1%*-&BKuJPpj5;9a4{MJ9=_hL@nSd0c`MSkuw z4GC(O5SzmN;W#8B-b=6YeUO1r7@9Gur)+>K;z&I#^yVuzdAO|)eZb2)Z;S4k=ueXb zr-HxpE~e6*Q$@!5$E<12477jQ%cKct)?wx%Ede<=cZ<4!`hX1AD*p#XaI`M0kSBh> z(hd0x>FbB!im|^&zc=WVgvq~>^pBdec&j-x+<nA4)C!B`b4v7oaNZwD(%;SbOVJy( zl1~spArJ&N;)#CQUb3R6mAj$ncG#UzyH(OI#uC9BM~m<cYHjE7=~jQ_Mtxn#j73{b z8NPdUuV4?tw)?_e1`FCv#n(^5-Bu)vw=;csw+&n&;%{Y2vuLYfOXEF@3T^$(TZJ{a z-Moo&?YIy9=AlY%1=7%7IF0NAGAs23_uRuqpOdY$InB0?lwAXBpYX3w?TL+YQa{>D zWG*?EgScQJ!G8(Wa^HXaVX?8P3OcMC!`dRF`r@vL>d&b}Wufa2z$8_IVHFIMe8Wf; z;9tx<*fczUs`&EZdWg!OyqP9VHC;^HZ8rqv7GBpKs-*MpE6^O#-Dh=ziw?ziH+Q$m z?%w>9#MFn3>1a4Ks5u+N)6h-E6wsFS;^sH<aj|5X8~2kFilu)k*5WT(nIpQxkDm&f zg-vbxp|S3V#%cO(savTXHHR1L7O+ee*uT`{mAl^QcM%)ln~QnRIF-MvVYzqVnU;L9 zZkZoN%)vgXpw~X)&Ff^&IvE^X<(~`+|3Pmutq-qo9`1o)d-;<sffY*ybO6UJDr7Nu z$DjDJFg!-o`OtsyDHA<!D>kHLJ)g$cTS1eoa%Z0=CH$KB*^_F4Srm-Eh!{skj*dZs z-HobYwMMBBe#X&baG*SSCKxCy>5aJDG~_8?ZDM{M;Y;`!dI^BNWG2V^?ez$F9>!zU zHL>CRkPO`N&#p-InxZshu7l5bU|`RtPsw{eQ4Mi6-THqOTmcD+I3plmQR*z`!G{99 z(WH~e+cTxw2f0%esINfo)!T-lb<e>{jHbAD=`s=c1vFG(iQ$HWUnfq$u}77jN&OD9 zdG7~Syu9sml}t4?gU!2o^k;Qqkj@8(P??IwV9bLtTfh^tG;e%($^9(gqMH(Ts9nZ; za(GPV*M&Qlk6svXliQ{Eoo><TR)UR`RBC$8oTMwv1-K9e)PRoik|^ZNfF&WHp3k=` zGu6l*+bO;Wm8wqcX<e6CJqI6uzM#YD&8iZ%D2|-4uyztqo<0%*e0`^2=W-`G8oXSP zYFkhqfT}CE!i;r}#Mp#n_!ygb_ryg!-So9JGp~agW0skcPO!2QdC0ED1F2NaG42ZI zMF{Y~B$p<GZuXB6%q+R7+>+pV_j3{LkE0|kT`x-b$WGPy{%TMBS@R5ksf7C3vNUwY z7~oAd*s%EI>2_~TKQW#$rfQEzQt|ciqc?fiYWs}!lqmznsRSD6;LK0MXHT$p!`<$x z@yI7NoA^pGosa8TMYhhJF8WeVYgJeneMO+aKgd*k*~h$j%G1sb$BHh@?;N$F$Oj$P zlj63EdV(O-&?L{3?W{U~vXbb4UC=yUoU4+{nSpN3K?Nw8Nz&B41_TJ@Z*@M)7<E}9 z_0rJvDN~Kpyi6tj?3qCC;x4)16lKbDSz8uO;r}0VZ`S0fxo!>K^DBI>J|FXRBKiUZ zh(RD`3GSFBMllb+zCq=xT`pH0+xzV9Xh%3J69IaaDLwC6&#=~iCCPzq#$66)WN)Oe z_uz^+pvY*6G0m1mCl-C%xh4skP1Y;M?J9CZ%$+bEcP>tpH_tUxfVVKT!s}w84=t;o zjECkcON^N}qpM8q98d3{9lbbQ%lWiA3%$iY9xwUqv+6_LnIZ&Cx@C~2{vw)*%L#Sk zDE@7;+6{TS_voR2ty#$-RjyG@cWExU3Hk1JgOagxPE3$#c?;Z%x{&j8h?}d@Dut^y zfnQ{eA;F%7>EX@3DdaSaT2ZU4|Ds=dC$BQ(AfyA|F+A}BaBE<_bsejeeGB12a>6nG zK`SV0;@;Kod`1riPk%NYiAZ>CP)xCD?fxNuJ+{%2X#m-OGVm(AdV;?`xgoc2&^%^i z#a*j@L1I{Z7W7c!{Aiz~Cfcbtl*$|Uayk}@3U5s%;G~Cs0M3@`deSUt=MLWt6(uQl zViuE89m>sHs%o^>ysQ~X3YTsVsAU~BSO$~$f&gm1Xzc5H6Ou<<4=)`VNNeHhQouz0 z#;0A1J%_u0Ii)ouIbBWa?Uk+kxIsJo)apso0K-7Ks+--ja%#t=gqd0m)*=fe|7M&U z!hR|-eOpzpXsRp^=D!ah59~Hpzwzh)`plet@Lk_0QS%!Y48d>K+7C(9|7QIk=KuWO z_;#HBi|K_QK;(b=-d};_kN5pU-j9F|gHD`4D3T(7VVp*B8Y2%yRTx8Pl*S<%!HA!F zPX7)Y(H$@zy=XLfG++eq0qC%gvj_T-2i)P?A&U?H6jUC<f1flV`f+@YKPDsr^2sJf zpX3~V;6j}E<Pq@$5$<Om6AtmOv<AbY1&@M`#t#BHGKM>N41%Ky5yOXm{l1tlfH4k^ z*^U^0KW09Vk7EA5U>N=g-;*El9q1#W1AV|IdSoZlzx1s;`v)3HzIjd?iA2>8iMO2B zxnwnFK*RS&|Ji{A`mwdnusQx~tUth&XFRlRhk294o6q>NV<oiQ;)^>(D{Eg1epIb! z2IMa{xbh4BL0$13`sql)cM}EQl_yji;CZcojA|1OEmkmAXuEOWgUD>&;Vz<b(W-F{ z^hFC_{lhWfo_~2Q|L#ixzP^@!_oV>eswlsBDZi?q{O(l31Byg+mwg|YD!kATz1Whq zyG^OugxM`C>c{g{rHyZ|7{X$eBw%cTuB6=DT;$Bvq+J2RN(DU(o$pwY>#Ah3R$IS+ zSk<H8GY^vxbUi1sn}T~NPt*64Y8IV!X0oIP_%;xfk^?`g*8Ng~M1n4UZ4`0dZLc(; z;0_j3>qG&(Kwp31Pxgxn3d6Y;QsK{>Pj8?MI2V$Y=Tr0~=n*!1>2gIZB@(An7BDDp zBG)TlxY^5M_bviZKnBhDIEfmK<%7h3R0H)in&leD=#>psQM^6!EC`08#7`aS`SSgA z>jJG;MBn$u=;cf)4RSp74-_SSk7}QR;3WQ@Z~uqm@qhRkeu&CH-2IQXBoqmu6a?@5 z9>HK5C#n4>a^FZX0)`Qkz;PJ+Y1i}4o%a|SA%}))nj8`B(7#TPiDBqN{P!q-i(>~l zKRVv%pN8h>Q<pyYH}nai$x+LVKKinIYz)bRyCa_>OY|uPWB8|T9PDBGSA0J?29EdL z0w+J?%lOgRw}=0o(FgP~@=OLt!6v4Ur`b=7KGkIWQzJS=G2r8JJUmVgKgS{Lc$vXx z14-k*<ookM1c}$bY)LxWcdogAoN=+rPHE>lCz!N<#J133OG5l;OY)DyGw|=mH1TU+ z(8r6p_!iTgsCJKMDE`d#k1)+6s(sXX`0Z<$2L5m`XNiCMl7OFWIsfD(0YAQ`zw?s5 z?-vCA!q3FKdIULaVH?_sSwn>Vo*#u`sO9!t&osLFKn?MiEkW|J=fbmpwXvy2z0+bS zulTKo8KGm=%xmaZ%tm~R@Q<a4$@E5_8K&9v8w0jG*Ih6^u-R2|BRSUXevHa(5uB3f z;hDt|@kTKYPT?qPmDjUW_Ncsq60+eoCW%j@1+;mTbhVvnH9t22LCqod_Tk41RAB$1 z6STXob0MK;(oT?6uB0J<cDO2sT+W7wR(T2lCA&A-E#l38zDHVD)xOe5OgwcDL>|d^ z##8UIyeAp8)CdEIPB5r7)?S7TWEK7;qkzb`8}=+0qv!hA34%h{V{?ObZ^xTi3G@4B znon&-q;R;%jQ;3fd=qCFqV9iV*trBi)xIz-Y}DH%^OyB3(Uenv!W(+QHk?Df^t9YM z*x7K^Pbt#s2kc(;kH?IPGeqC2SNj4;H`b(4zEnA-7q(HP?jz4-2ZfeyK3jbc?JsFy zAf`p4x1wb%Z5I6SL{9O9uMgD{fOSw9Bc_B_(cP!a>Ss{TbiDVWJdt%!CY|T1(xZs# zO!CP0&-~rv)&9+Y(9lWq%@NXpHb|4~G6ZdwAeU&QT=4dg9*x>t1Eh~r@J6D+RxPj2 zJvpxY3S=9QD4vdZ#pcOl4PZi7c*{71yNEu%8r)89*~}nlL~@|l6<M|RNj{jJno4Ii z1hWaYL`5_=<3w5TljaG)?qg)IU1|8^#M}SaA@MtnS5M1-0s|9swQC3)Ev?2q!D^SQ zrk{Rhh+=p@=Ek%mHwJtXV*Kj`8_e}OFyO>)?%<lTSiU>@w&NR~K{0i960(+0`5lhU z#11m|{q#4@UP=tOFBMB6>S?C%FbAFKkqHRDQ9X05wX&3O>kjdD|9Eqlr(tQkJuu0l z?C$1gUZI_Trv}*6t`6c=tRo46(b|O21Z&IE1K&_}%3l}#jVMpVeBfq8U{j@yiMl1~ z#XzokJYbp$;Hs@kv~x*sz6ntfq(&TKcKx+WXPRf6_6D`4oDeE3Sz>l>sk67Xx2Kj8 zoQXRsZvdCHwiT9iAeWz&@HQ5g^e)ctypQhhrH~eXZ%G^ysjo-<Nf1%qSY9%cGOKY5 z7_9J908ez55vt~tWZ5JYJy%4~`SgCwrHX(`>U5i^f!Vc;(-xQK8mB;qTx%q4t?Tur zRxJQI5v|opbHAINOo5Y3iMf2eUuXh9sRc48_uRZCFXOJ>xQU^9_?>oJwml^^4Tg>6 z0G*_NCX};^)7n3`d!k#QofCrr;?Rj0gS%~-OC_!%;S2bLiGic`15Dq^l5Rk8RBRP! zWuNx-zPJ|+7hZa788gnnJJcnsO}SV0jubhno5-x{MM)DZ5q}p<J$n8yM7G=L03=y% z!_AH{hsrvPm|=%|R59x(ahfp5F@<{}^&NJ9zxh$IhZF(2$MHGHWg+eesNH{wJi@0F zcJBuT8Cc(tvOUlUw6@%E&5+<KBkk36Wf)=92RCwg`1b{F#`*)Wz6WnvQb);e+6S2h zW%7rL%m=OU<&FTmrhc_!@}sKFf8c)Kq-=h;*B{$)_8kO+@M99=0D2^bQV2v7FhXE| z_<n#UFbdwkIgI5Yg#2mIhx$}ZLi$jP*-;Zp93oshY2SfXNF9}+lsab1&<~Uff37JO zrH9l%`O$`j(&O}<K_5*r@u87$2n?jh!~_-|di)6V5lFy}al5ahbbkVYNOE9G7&<0K z4prIcvl#5-Sc)Htx>WL+9-)r}K#CrJeUkeV?ASH@xZv&2boBRpk`7RC45I8@{TMYt zj`l|k{!0+pA4MPh8$WmW`RZZ?s}@CY5vn*It|Qr3{Rw2%pZm&tKXk^pJa%Xaz+Y#^ zu7yzhcRcqk_U1jzcZlH6@Zx-GJ-|<pkq}<oJQjDrQ<Y16|Gr&NRe50tM&%cO*_ZF_ zPrem(H16Qu8LVv%@&)J_e>|4&Yy82R&arbIRXOMD)ZS>^<1(zYQsVJ1tEz;4U;dA^ zn~yvUAn|U$qe3XEjy@y-_HEu*+32gXf#Hho_(gce_$J8keTfG6YsUdygTR$$1wH0= zzz%!a$T|Z{d$)5s#XE+>iNrg9JvU_XF0DhQgI-+4tx~HOXQWx^keuy5y9K^ui9~5v zA68VQujY_|ie7J*7(ir$Yi=`y?}%4>prl+tVVJWHqTd<QpYhJ$WZ5va>dl<&f*oJg zq$&6GvYpW^zGVX7&Tq-l--JQGPoyvgTqPsm*v6vgt|WUQwIoYra>opRQ9@d57`sEN z+Aa4SgDr$T)dw!k!z|_Wv9?-y23722C?lHs^+pvS?KQo*w0<}{8W=NimWz}U&CFd+ z_V+Wxh}=y8$`^PwBIU|UHg~oxStK1DiA&U6URHTwqXjQGT_@2ph-pW;FmqdRo%_1G zmKZ+E+5tK$pioa3S$fESyov1j3mF8A2xZIo)>}E0=(t!0FluVlv&i-gl_KVM)WsZ4 zTzPp2ka$#Tzl*qUD3yC|;Oe3b=rhK<<57x&2>C)lPyf8{eTAB9_Ym&$#m%W8lS-mZ zq!3U`zSpN~nt*<syb3=bAJSy|%L|6p`3Zsha$nm9&vxJ&)%g*BNnnNDd0vV_S|24V z1M-=5gmg7S_f?nq)LFP!Z};)lctOuS76-3<)h|!R^tuF0;SIQ?pR;SNTN~M=+sp!_ zL?_z=v=m-qORsyC8)Qfi6sgTk&=j%1GMM7LX3Kprg&s|3fpfWs72l@*Ot<xV2XJsr zya48*`TY1}jploQyk=agcb_^ay3m0wL6~=H9-2SsF@FeF{{ry+P~>5Ppjv1<O-1;v z=Jqouo)KI@01X+>UnF{dsWsbX?l=sPyN0I?SY8~$7Rmg4tK~8Fw=xV^g03ZF&t-ks zG(o)!1fC|bM2LwNJF;Bf<1>^fveKc=8v%naM9!^~*K(eJEo0{~I1gH68Z!-vW!5v= zv}NdE%Be-zOBfW%m<lHh;-Q!oHthHS5c<L8NBfe76Hn?dSmd#jPlCc@8HUh_nl>{% zFZPjXl|hJ!+c#p`l$>a_$ABZnivWV$C;Z*7Rj)Y(WkhFH<4@?2^Nwx?&-XK^%c0m= zQC>vCY|8I{APz5DE<QoMWyW&Rf#o)$YP(_aoqxa!<BzFmofDcc-f(!&CTDrOH>KY{ z&vdM(&+PKjd4Gs2ZzS+#VYeEHh0-JERo<{ZjeRxPF}h_5=QjaIZ-xzX?YT*(06;*$ zzbiPaw99goy$3k-twJhWm`o=Q7aVv*(0cJkJwLN3yk2=@e^`0p-IrnVg4~Ay_Tsuh zs34CKZM_)-L^&iI32LjeX7(g91Zo#?+AEylh{D9ph2x~dD$<v+Y_nPg?7WA#b0_)L zrg7w7)>LSgT6@I;ZIt8$dIoTwE_3dhT+UcTvw;(qKg83+&ce-Pp)0-lZj_z;p-w8l zk$HmgL&&HMf8VF7`!uS)2#g?NB+|e}D>1ny7~ODr4IA5rE`rKM3*w%n;b}>(mZ2Tv zqaAXa_@`WxGqbOB%yS^7Th5fm#I<tf1?P6b;kV1oEkb~B3f}zv%%I5Zq>K5@zFOZI z0UvI12)yGpF)7QWenr1AQ4lB9{!LKFznMv&1YPv}e@&|U;Wdm0aO?N0e!zeK;ah)& zg8##9zej~6LBS;TkwBne6sJ)VBCs7MBE$|C5gH*6Nra!86%hF0<xYo(bO1#hgCK|I z0CcqK1jqsI;7=FNk#yVP+s|t?5OU}nK={#GoqmR94;MY`K!Ff(Sk}eK0SDm^x=TKA zF8(+ee-OW_)$AY-kAF9MkAveDIC4PMls*IyLip(B*>4vk#~q=MwgP=P^&PE7`?C_C zA>Dl`VSGT&L!f~^s!xXp-+tj=Cj{RI>cG}-3Bg;`+TxAoC@Q!WR0{c0fu%nSq#WD- zm=HYX2L1r5tc%<4&~9g7>b?tTPygZ$$M@bHe?RhDB@iL*Gpe*k;>$5Y&37!iAA}XH z2Vmk0^(=h_RiVWTmgO8xK&I-ftmQyQI_@jFvOcZU%kK@|%kllq=4Z`zw%eBmZ}{{0 z_T`ucH*?7CaP|-VO0e_;jD42jW!Z6InM=Hodv~)8@B8-`Vf4?e<v+9%@E&p=oLM=n ze>Et0nD7TY*6M*!7nx&TUt#P8da_PY9_CYyEvVl-jlXyETh)Kk$0JQ=oS;jfn%%nW z3r)V(23OhCHcG|iyz(g;o9nz|xbB&d_kGX8$wD@&hffLO+@DM#_g}i5@c`yt&LFW_ z!=;X|<7_t2V+@d<w67;M4~(QXFF0Z6e|VB%YIT!xF&1tfZPz0&@F!%PfPijNh`O6E zVJu$$)y$GWy}7Rpu1c;A5YH)}BQkenK+(-<ycLi9Ekt-G5+!wx222Is;VtdjA|4iG z1Esf*@0+k6ps*?%ISby-LeayOyx8LkD!9=33kJoY{PIuhT4dEU1IqJsJHzzye-PHu zsQdTJ6R!+$^VjB;oFPUwY(2!wCjkb8AMI#@qdogs^HW^aRH30EaJ`cz^`78MH74P5 zSCfgoDhl(uNlX!PL2(16xkxpPr$AQU?j2=9rbN1&^v2kti#&iC9TW1dp<r;TXQ!-E zuK^aHi5}j;aHI6KEn=rte=##Fe?^Ka%XQ&82)DmG*Wq?+S%A5y&S+~1YD{6nJK`l* z<r9xU?^&sW)IQ&hyX1TBy(^|GreRcSLBV>fNs-W=*5I`OPr#_nHeQAE`d!N|u6lo* zlm?Wfwt5CWO1B{;-mYW#Ha4Zvj3?8THWp$I?_!K3cEBCUbGbp)1YS&<e>HmJiUl4E z6H&#-BBBm2rMor7-8kz@H+7n!lpWpjnP(1xVaUq9fi`9ma3V&WAPrj3OX?S%>w2|s z5t<n}1*wwxuUx5)`X2Dp0w431B*M?$vZSb|*1l8jgLt{xWM0P~VIT1OjMXo3pZ;d| z-v<5?EVoML&CR7zuH$sDf3tL|HO2uRFm6ou*S$~-hY#LOXcX|xVPs@7<SS>D30(1( zRXt)g6UF@vOZ8afoWs1%VaC}&6i9I&;Z^tYj+xAYIY_g6j~3k4Fb9#XGHRR)$s5Yl zb(FRfZbB!!cdZOMoVlw@kO#p2_k2&TsxLYit)q6LidA|R@#Qk9e+=!N&1P6Q<86hK z&Y4?y?XH4;cF?C{&z58Otq#cBmBpRHpFnY)o3T0P#iCqI+RMG(PUci4{Zov<Cr3Z0 z+z^>LMR`l2*5E6gSRd7aZA%46oPrz4+@H)$6m)PB=w8<|%ukqFYz=vpiV4-wfQTbY zVBmKShXTw-3O@PPe*om{&X#eFru)a%*0#<{cc)@F2%Bm}zT83VqD-s$Ao{*a+#(N> z6uHOG`>Db3`*Kww0KDQNy@$+J-&|FCHe^4J>s{yLj_7Y}{(O|1-sR29Nhb3}>YK8u zqAR7uaFy(YeA@tfk=QyF8T&*&Z-aG}SdL)Iv?3|~I^gW3e`bq!t}Ro;zu$pxq5G;b zkeI<O6G_P;X&}&FA=N%~)qX}o&{gHk?Xa{h-&@rZo(by0rMV52bH`TDo}UXIk2|BW zt@ng64dvZ|Wd@r}#UkZmLKh>qjZwcb0u1)?Z1*=_SZO|M8y~9kx-L=_ywf4t1>VHW z!+^oD0}!Zhe_tfXtgina?mJYW|6js=|A|}w1o!=T+dt}GkvNRg6pmvQ1mQ46Vk8EU z7)BrvjNv#=q9{!eKLdV2awN#G=xClHi2p|MkNDLG3Q`|Qej5GE!=XoJErfn<ED#;d z;n7DhWyf_p{-TlJCH{{x?#B(3ihnm(K6<{BBO3_8fB%h9zW{z{dO)=(IzTD*5!pU8 ztK*|qeaKLg2h1Zrnacfy{nqdYzLMxsf<`{&UMM}l;r@J4aJ&{6JyL=6$BP;I%Ov30 zJ)lJVuO=NIwc`AddN-sCrO5{Gi2`mvwn}Vl|6`NRKL&jV<^%ps(Dy~D`dioY&q3c& z++EZ^e}cZ_*nD%pfWAM>8vZ+=@4$S(zXSSAN8`R0c7cC^zMtz`eUrE1G70Q1Svkl= zr_l0k;22C*NkCD$C92?(;S=epuYp{Rtwp|jmT9HOGhJOTrPM0}VY%tKOUpZ_Vn%{` zEo+-|_Wb~`Y*DRvJmb9>hET;R_*2NAowgOTe_?vF>|JKus7;>3FXXL&??pSlYIdyV zzMGoY6RSCQC}^pwI-!m1_+j7tNud&8vdiVq`c@I6UW~KKib*%+k=?xJHf3I2B%8*K z&LRsP^ZCj@3~fFJ^(>-yoR@g6pc$FlzUd8lBMDC)t4x{|C<$09H0MU3)@;0Acs^`E ze`7xT1T3wY2Gom%=)&z~GyXeG-+d8zt>ru_@*3Hg{qFz%T+OfNTZC6v1@Ep&lm1g6 z`RyzTzcB<t?8NocSonuIn*YUpzRk`2yZ87*$fe0WsFEl`Q8-DE$WFX}7GAOBh;|3B z#*QGn2cyGj`Qs3ueC$}lk391pTj8B)f1^L=)#1T*Bjjj8+ygR39|RVqKcmp$5wY>; zD0m#^Fwxg$m-Hy)q{*-31<8-F1pX10_$V}l$VU(wJxVA$)82#c@ijjBfC%h}u={D` zXZq(T&P0b67Kt2ET<Isg9#%2XhnDa6`O7)zi*STj@Efm|E-3egrqdXTKOd}df4(cJ z0AGh^4vhRYl-!)Z9x9P||Gfu<J-mwY<X7%?96VH^4XC_GJddckw`uza?8f~r0DY(f z=IWw-Q~~2J0?;`F1A9RKc2E`gAH=y_TXm$__$ouqmET5szE1Q2e;Vm|v6tW>58i`a zL&V>AfiE={iPz*~CP>RAMBR*8f6KE^y9@C51fDsiti~HL<s=nIFt&q6WaKz!h-qMW zauQP#o3VDhjkN3fTw+CjW4!r(-`!*yEcT2`y^arb_|9(D6?9Dea($>Zk0FMk0#VS@ z&I8j~n$B5wdr!DI%~o}uuXf;s9IIc);TTC-C*i7PSkaOo_Y_mQ6*#C*f6@zZrKRES z!Nj>B?tv?v+dS}guu2G$s%i@;7kLcA7(d{Vg<~8BfmI6WRcJz>$!swG0yKu6*&NF* zLU*H4DU|Q49kZ6*ptptFqcnfk{Kv`&7bhJ8iLm(o9fO+J^>2xiRPYHr2r8Re7D>^t zB-!@j+3E>LM@T0aJ#Q5}f2dCQbM+_VzDn<sqk~Gl(!P4XES27mNC;F^WzYt%qDw(@ z8-__#G3E1SG7}cMv44Xki0YtIL4m}i460B+3aN>PhZ<VZDxX)t2Mtens|3iW9`tzK zRr5qB^O8RNYVfc!kk2fFXdvA}L?yLt{20CZRl|)E==H1H=@1|Ge~+tQ#r$esXzKAo z<kKys5<R}aK|elMI@m^;JrCE#4!{)&%vunq;+l@L7}6qW1Byf~cXzT^L#YPgs)X;9 zG$==stCK=sC%Rr=Ef>22wWx#`=S(8etiR1kd9lW1o&<o<3iibnL5cZ=5j*5_UdB@f zUM44VrmfUq8E1#)e{>h`w|hZXE?Z|yAY&+-kIc*2!OqRn#S3KRLgGXxS(?YM@>rNS zsgl_Lu~cG~TQYW<i7|Rjy0OYcz#wWO_k0)Ejj_Hjm7EVX^?fVd5d*7Am+JGF#qgPr z&6(ft|7RCgaAH7Wj?q18zRsSc%pS`cj<{cRmyjnEiwtpHe`z#EqtUO*HM|RRRMFKA zbiNdB&v*`m2sC*pyQ#vw@74YOtd79W(peRl#Lo5rcHx<5W=rNmF*{-@n2U4#+aJgG zf6u`2K$x*l=7KTd%pzYNU?7HzJ|}!`kg@r3VaTx4ah_)|8N=<=eP2Dma6F^^LQ3-| z0rht&FiTVzf2oTXx&mHzd&p5^G@mQk>N$2^5N^oZn&9sWOQxCr;;xvHhy0vlio(Nv zbKcId$Utxq<hCa4P=l7K8$+|`Tj1diT;=^Oy<~mrhZq{jrGcL9g1r-k1~!+F3$M~U z<H&d_nJ5v1PzIi9Kx_JwNiLERaqTH}dOQmX1D1Vue*~X9UG3wut}MM&64Z2L+bsQs zzTg}DDkTip0XM~v+lf&Js%cf8&jyLpx@kfD`qp$6`AoHjBV;4PUrG+KC*xtUws7_z zNNGLL#peNjhl;ODphx-3My)fdUC9TP;CWuexWa@K1($++XI|<u<`3dUC_}TK3^PQt zjjVZhe*rjfdg*+st8+e@>9&kg##|-(jFh5V-yAy-1;N0^<h=5f1<wM?wrM4i!DJ~( z)FT6M?kBZDl28$CzM`W*XH4(iC~+6XWC)&^a;&lZ-qIJDi9x6c3PD1Pm7<suImg*x z0{9Y&E|_7bAl0f>c1Ywl3wPM|4NZ9zZPvHRf6X#j|E4`eT4D?OPPooBGt;L#-}rV0 zQkvt{djI64pnN++&h-*PMqyX(!`t7W?gOOG^_yt$cF67h?KJNHIl=T!H7TsU?m+{u zoufZ}Tkclld_}-JdQVRn$-4Gzc{X9P?x@(%rEB0xC;f!G#gVbxjcv^OO!YWT0WM+7 zfBS2sPrMvYmg#WG97l|wzONf0iHrNUA?I{7&-{Tl-~R8{aqRqj)xmG)=KuU(*CGDV zt>o`t^%YM3>5A`RB7s7O=LiX75JKYw1Q7^E<2#nbaQK*;Ifk-nltNGn-%p~kpJB)y zsvSb8@G)$HBL~Ra5f_GjCTNnQ%Nqaqf9}B_CQRW!pQHI`Tpuc5pPJ6mn}dA1e~6E~ z4RVZs2k0?ny;E=aWAD5Jz{5A{SFR&RXH9stb0g7lJ%S!Zn!}6w)2%}W2O1>lqkAa9 zkKq~&Ic941l^;*=>EcPkPsRcL_?84m*(t&g07=IG9ix9q(YcQv*!^3dPFS_ne?55H z(>ud`;%WtiLE;q@!9z3u(O^xT0dxC7y#~U*(<g0g9w|S)#s>0yhZUb02sK{$1@h0> za>*I*Uo#{R|8t_myWFAM93nJy3}b&&ndYKRwHb8BmXId=t}?wJ1HL;r|2wMzzF*E? zu154`p7F%re+S35AOH%+^UPV|f4q$0eV0|bX2Hv12Tn}RHN;f|%kpkKQd)kS=W#)` zvo>sfIlvTk+F9on0w(bJlpD8)8#jz<&I04%HE6W`B9X-uo-ZS-#Zyg({4G5hI?5ob z+Nonazq_Q8j*U0L*x178+SYmFXMPS9S8LPoT5~;F+tX!`6`D*HF%&84f5WzczpEzw z!{46^_C3=#q#1B=G<GLZ#7-4vxolS<H5Z;XF0~$oi#xU<4;Hjdo55C;+(!fZCg5ZX zQrdbr9?~<wp)5wk`lT*|K<U>fl7o`}n(VtgXV0|l3hzBfERT5?<LYIVd1pA>PJ1ZO z8o9Ih3viL`+dm9x|2&-if3r~blQ4!-B!1YG9x;oiPz>FJ7`ewan#8C>C>o+M5+*Pj z`e;K_KW#S*sbjPbB|k%aA64f)Hc%gaxqt4Hk0kRKiRi<QDEhg15_ZVm6Z8>!_Gq}% z&cl8lKY9v}4Btm+?65%kG!4=pjp_Y4VQgpJzcNn>4#luN#1i;1fBmvYBjRJ5L_{BZ zRs6_cQqXZDC_YNs0sKj2epDv{`Y4O<#}AP+_{d2f`i!42wm&PeN5sE0%M;u~5^N*B zg)!r#Ej<0&?xq<J6rqClgAU1A$elmSgMHc!jsHa`1B9G$z6KBOYw-BVxQMo?%a;j0 z2L3uOavTG`9n4woe}{rMXXkvRvd!0V5j$`{eTrOgDMxQ7=;4a>fGwKEHRtatYkXC7 z(Be-+qx#eCz#j|0z@I9=SJ+K2qCK(@?`8L_t5!6+nk-TD+~diBh0sK=ZJhmP=q<&I z0IBO-L%xnGDdqR5x=lhJyl~REq|$Vc0@Z_Cq9)X9f4v2de`{lo?DfrECq1lI=D8LQ zz^tn#%bny)H#lf#`^xD{P0zh&&w-AzZbd+hQ-eBttxww2?o1#Uy@|NnymoR;>jYdn z-3lXRimSj4)^;w%M0jL|?mgi=PU8-zhOnx+Uaum7(lK!l+Pb3k!tylgE9TPxbxsrT z<eyY1ug?qae?eB%IWzvSs>fqzl#Hh@>cqJ=b8jC}qfC?u&xE|eBut(|H5P%iY@Mg& zYd5m%?AvQTM{+s8k@)Nnx51BuPIoSi9wF?5go>-&Ems@&Z{7QnbrKik10YkigOfMB z#iUnpj%a0kQpN2=*&zBlzpNUqoZ>B@?Y!?q5E>gNe+J?!+(4LzuCV2+0`@2yoig*n z!*^wMOmP%={PH3j?Iz<ZTq@`KM7>8UWstbNTYc%hb>CUaN<YUh#q%}beVIm(HgDr_ zhp+~hc4Xk6Q(izK!tB$uBkWSAD~dPpw@cfYAnfS0hbst?u<fbb48UpIjd{D-$yn5# zUyraEe`zOCcnteqpd@(t2tbp}=f@d-b4zf)5HBmiGDk!?^US}+J8*kgqs?E=2&hz+ zcG)UW-P-9^&8mB&Q!9=uy<&RrWl<MbhuPvP*yS)WloHXO=0M<E<@b-2*YRpLryh6S zfL}Mr^s+t*`b)PVuOFHNfp6V`|FGdt30unwf06;9bYY%P)`OByZ#j0mI=4^w&aLVE z-8r3PBs*iVwN^pKInv;3W)u?{ilHJiJ<dPxF;Fl30E%US-nfj8(afc)vakKwQS8$& zzh^jLAnNgW*iJ?8@nu79>lr-tx8+I~*9W|F>1T|Q0!+QsyvxfNLEM5rb&DlJ_ns14 ze}=p*Q`{0JvYc<Mwpu8`A$W_+5GC~db5a2%=<u`hcxj8R^x?B2m-dY797YAvJqXjV zz4KHJI?H-87_*nA_<C7jOdQ*D;7-zO0&LC^mF%h{tT~wLz_??5RjTvTpKsaLtW+<` z^}>laWV1`z&@%ZkEY;0sF6)rS{M7<1f6DdGOd?MGgG*1z9ZAGUk<<JB-pT3x1GPS3 z!<=2aR~sD1?%6Bih1<-d&_>Hxopyjky(B;H>D$A5nv>W+)*D6;auq*p*3eTgkXsvj zA@6GkMa@D=u^Q~Zye4b65$pMo20|K6ozjK9>HKKK?Qzqgawi#CwQ%~2%&<pEe+q-* zowb%v9?Pa<?;zfPZ*-;L7M5x(;7sHDS}XT@P4l+a=o8!Ie%+HTa-Y>q*x$Jr-4v9n zZ1>kPlr^6_^Y-vYp}YBY0qcPMf=G?mpH+rw?@RC)2Pv|OxmTL;nmP1xY3ukQ4&+&N z7|xCv3|&Z9pr)t0gNiAsfu3C+e{L_Vgo3;}4BD))NngWO#p04ZtCLuw{ogPAN|t%~ z0&5<~4uYh5NV+;1zNlv%&XeCtu{$#z-#=iee}45>JoWvR-@_@2#OR|fkESV#geV$@ zp~J8U+OZW(>|Y3!fZ?BWLG)uXgdNRzJLNi55r_j&MIVC;^5YqtKnLPUe~52`wm+6@ z<D=>je(Wn^{3wc2A4B-)qoTTBihrCUcd|(1htley3HC7~3V!7SzXLK7`IL;4qnZ&= zM=@asVLK(=i6BmX#xuy{M>zhde_@B&Wc=Z>NP1L7sL$N+P80W+u!FgMHRz{LZ(j`w z{UsN?99%H?rVVWvu@Xa%e``y&2{T(uPgp0>`E$8e|FK+qSn$5pbMzIm9HzOD|By%j zVI0y~dHndyr|!ta-Tb?-dPtT5CMr+ygINTdvA=;SzO<bp`U+t@HUHEUzBmqX#uph? z?u%nP@HuEl%i(Wy98`Jgk2QtTwblL8BY#z_UXS9cSw<CJ?l-rAe=q7@C6rkID!~S4 zR*WhdjyT>ujMIMySE}V|mid(%3fX#aL*Pep_&qn={K>7cGj|?m*_zb5mE+NdCOx{N z5zd6fVVLj032v`~Hoc-d$B)I`<-O`gWMN9L&0SP1e2JVDWAsdHB=06o4>Qh`pdu?q zOi88`Xh0e=C49fbf0zd6Qz7=Zc!Ad{6FC-af9aH9{7Zib_?cD0S0b76%Do%#tHn)3 zF};#prH2<$t{U!q<A%VG<S;?Yn^~Uuxv)g@IS7$XdsR$AU|?Hr;APi`eb=rF+rVtf zSn(Ygu2>x#!Nk9CxROxn;D#^IMMKS&{GiLu>oy89RBMEde>;S0;^&Hk!Iz=B!?~V2 z7yw(032upM87Cx!hfqkb_1|x9dFhJujp_Z@Z<nL-<oq1#>^TR&*-4p1P4yvtzx{+K z{`0H9wW$2*%0J}4AetZ$0zokZ+P}~wg;T^KkB^|xVcGUM_S41`;!A_@CmTh4Dx?YZ z@jw3<+a81df9X-?JS@qRBVY7WW47SvL`kqCA|)Ra^W-BOfPI9claHwL9-WSgYJ433 z;>xziCE`~`LMe0vxG4FAC*-KYhUrloJ+ubHqt9iJV*B;cCmnZa4?rJf>fo3~+xg*P z+xBTqK6+M=LpEU#g-0HY{wyN;OS%{wbP@h$BxGD;f27|k<xwgEM_f5AuHK2q{j?)l z|FI+4aW_6!&-~ZslwhZba9G7njPRLz`Al(5;qOFE+9@OO$v16`|84rLc)lHekiVmh z+0|l2^>+*19Tlx=F3q=(622J#Up#dA^=LZL?B6|f{}k=ahia*_WPG*IeRF~6jz024 zft@!xfBt<b4{7Pc_4oRkv)?IXsItY^))>pWl!gTr*>^3_`}xrVd@m>cS8CEUi-S(B zuAHbBlc_-sZOlNcokxp)At>sK{$x`4R`6o=o+V8V_MM=1Q>!H8{lVAeNnH|RI}6QJ znD&IBI5x}^;6@p$FQ+}6uNyALQNxF)7;J)uf6H6af3N6kq)Q<p#&N-Mn5K=&z?0Yh zf>(QntrG#1erEbD^3Y-*W;`zIK#TXhSrbc|dg6X!1Yq|(b=V{2#~0(A?6X25PpU;- z`Qmf+1{gi**V(%DV)F3ijiD8F6mduF*)&zPp1H`r+IYehHO#>wR2pQG9#$PR8>gZ) ze=;9Xq^o7*ZH@?fpLk_vT{tOv%LdBa^lUq=Mi~bP(!F^<ji1(wQu(#RC}lBsVke%? z3Ls?39eVM)Lvcg<ART*L!*jAuMmx`N2bw(p$llLjeMb-5MsJRa^5^E>=5T&{*sG8O zVmazaBqZiTKsii^AL}LPJY7AN<UO~vfAq4!Y`3zoU%nOk4U+AWqfvB5M0?*_R;~lq zlY-F!Vf9n`eDc%UOp0B-lmuGLNh>W{gmGU8%dJ%fDn$3)!t{F+ewWmYnH3cJ0C-0u z_`NZMh?Knsfo^qrp3<=3i=1X_%5urzEkSh1P<j?wO+J9h+}k5nUD11`WGWDVf5DBf zl8WkN4`(Eq-`4lIoFOP@1uEH4h80sk%_9ZU+-V6O3j?~>%Y|=i<pPc#0i?YIrU^-1 z7vqFCkJ!1KP%DAHs!9J(o_l{Z5~AwpaVm)hoIE;;-Dd2|3&-VNM>0RL%{_XOeo))^ zmqIkVZ?-IqiVTl|r#6nwrP+6=f1G%|cW?_Yu&v*oGhbOszp&y9AU92Uzto-S!Xm<@ zVn|R>3Z2+sb9LgOlxiYlI2hkb_8A%?st2p!JjcLIrs=YdZ3I|6{y4kP)ah0^$kBG! z?mHH*)dyG!r}8S~>B|&uJ;9l&;q)?zQSz1-KE~!*PY7E8_`MCib@qO-f7SQBy6@p7 zzF6(Z&kI&JCS?}yqK<u4pE!>!;Y63qULEmShcDR7(sl$qHox)niYs4H_3Cr}mJ)$< z2i>R%-zjn}ONcaNjb(a`h0hW}2RBTD5uD;h_HseD40!ESGim~#Np4^b75;F(pWNy` z3W#2c1K5C2QcfqnVm-U)f7SDZ&o94amPBq?0oI=laK!_?8)Ze5=R3!0(@jc|k;<S4 zE^*GIoZka@rTtP%BKS55NoqiNcYXEV>GIz4YbO9&Lg7Kd@9()w({##(g?nP%`KyjN ze8xwsDV|+B7wVukL!NhGqC2P1&4a;$NMF?40Y%}4;mggQPN+dae>=xjV8?iH%18_w zUgk~CCY5}=@0XE#-+RC4oP}WcCx0bNW?a-0u=75w_U^3pdOy&?@eR5Q*K!b_je6Bd zPmAm4mRNGqdrZ2Kq^ZN6QJqKkN$_cXHyps@ioTy&_d42eKjV)@7Fb4}K|*<?#1w+7 zejY+sX#`>ny>zx5fBR9$DKfQHd$}3^Kp^@%>z_YZ1l^qPfBL?D_+P)7*sl$T{@G1_ zVnFocS${MlI?&f45C9_-jZqYapfrJ!IK6)wf66a+uyPn(P#+!%e~b^m*kItt=vELM z$Y#e%P<s6MizGb}9<V2bj#A)$4-!5A(oQ0SPya4P9SG_$e_JGvsjU<{rn8706ovnd z{Hh%|{HXQC<Z%-HG5Ub%BlAz9A8_}{!Q<Gc_w8dV0+Gj3)A)Gk@biF=fi~!4gbSlb z*V;}-QRG+?a;ORZ73yNqBfso@v%}SxOfS>P0Sgc*&(>vq|Hv(K*4R<}EPc~3UWPx~ z2mMf>lg`lLf5f=(8)JS=+@FgtCO^RME<fKrsD69!_s4!u)JuzM^V?UOjw4a8ZrKc| zU(AV)hBoy6-O~Q<%79-j?eDDYzO!QBk8*lne^DyS&f1w_ENrQ3J4fb{dsE}hEFy-m z#%gqMb}sH~bz6YZ3$a(3vMz<6Pxb9dBkmboNS3+Ee`7S0;IfM4MX`2N#DlG7;afAu z-_u6mpE&G&-51>}OLop3!wMDzPV;*_k=VA{YyQ-nA8LvcXYRK!87Y$r8j8AdM`dDF zE6kmB*>r|65(p~+B1w#0u9#;D<ul87Bzg{F_o6Xq9%qgUO2`d~vlc_E>ze1#qPDit zg*T8afBnAt`ytA<es^{~NZcOMvoiT2BlppXe?>|E+x2?>F(&=(tG)%He_iqCpo1b1 z3ehA&U@(b6hYvS~K_v3iCRym?Q5HiVFIV^=Yez1NI4Wp|1}A)Ev|{RLREQ75)t?5P z<RcXVM_=YskN#AYI&ScVl^vq-$wBY-7(^vUf9;Wuk9klE{<R19{_rt<fPREX;zKF# zOY1KE>7YP9cfmimKBiDVlA`-M4_Q6(h)T&v5ao-$=^jSO4_iG_VdN3>qV%sTgGW+p zJN+T(6bQBStR}xM<4gFH%JO{BJn?5qt#5+q=cnqM2tE18QcA*C-H=0I?P!!eItLcj zf7o~j_^L3^Eb7&SkL7*!i~U*^+!;qMt$z$Q|2p<D3)+K?_yg(u+p7V-U(WxunxFZ_ z9{sSuPyAvL-cYXMczecpPome8qj!n>h8U_zk@M*Rt!yJN^c1KmI5hx5F75T5JQ{4d zo~#M!(>?OA$a0GA4|F`?RI9%yR*@fVf6_Ybgh^|9eP1t;M^BIHLGFKrlI#K{gqHFx zat>suC+*gow|r%vy>;5Cv(Fvy<t<yP*o3yFTX=@4pmp{Va&z(-u%qiaX<o`mcKY3{ z?;UoUS@N8y>E%2}TzYN&A!Li&5I;}y^DQtNGADuMC<>&fYghs;lb^~lqXOBqf5}EA zUgzE|vuee}R=w7bdwm;gx3czk7Tt6?@|YV-pRWruGeb0`fe7@Q+js2KNHIIJRm6f; z?yZuGRRz>KSk)$5Ewo?0V$r*xPr;d&(58+Q<)t^6-*gn<fdzYd<a7cr=&26G)3~L* zf#ao{K$8_ta3-3>;wdABU5Dr)f4s_j2N7FaIQ@HLs{&A8?oFA5($tvq)^;~5wA&h8 zxt?c99J+OHWRI07B4_DaeW#$C_xjB`onhp9yO|M<0P_uI6cDyK@?67quk~L0VrXpX zq6H-?n$(9dEiB2xJCZ=SE5%<_$9Y_nN9^4T5_$sxr%$&k>{+a-&_#c^e~638G@e+d z!892QR&Dki+G;7ob2%E&osT;)<TJ{+u_w;$G{DU_vz+hv3Qfb!8KZ>*E!WgW`O4j+ zw1eibQ%Q4z!A4*a=IrTqCv|n}X-g)QhQtB?8^73NHv^9C3|DrlS@VGcW%bjdE&sJm zlvLP}js4m$Rz9`HD-3}%e?FeTg}4_l?9^1Q61-o8)DQG*cV3n?mt_h{v}bQm#K)Vo zs_ydIp2A1v-O`M>@(ExR{C3_VdTYvAh+0@yay=j)mT|s#+bTiZHVIN=XNy57senj0 z=M>w!KV3>A35Rh7o<vGfV9UCu`8t=?Tgse8Z?1Q87#`Yn-pW{ge_T#kz3)h*{qlmO zd>6xT=`~QK8(qZ!QoPRm;~uY@Y2}HJ1WS5Bv7p;2rmUcDm6^=*JvJv=!AcOFU9=}u z*#;G_W)ym*WWdi-NnbXK(gs1*=+{fVz#NWpHPj_{w-gsIdTC>DexD#^OeC?9U@|Eh zDvGZ!W@jpZnx*mOe`KC_P{x?|3o{sfcDG7wN=tV|bV27gXqdnq+EYt_SIJcsI!Cb| z_%m}R&jA6S7%nxzfkqn<DZE`DAv=p!57DuSoQ<tZ_{C>abu)VHDVR(cL+DLvHs_hj zLK?<4K#VkVe;dl<%%*n1#@<UtP!PO%A)-oXZfWy1%Lmfrf9*u3J8-a!_|?6yy}`DV zz|=kf@x<Cp$%!0E52WYd8WWgSi?1E+pyl~tTA~&<_M`6cvW_F(zr6<09<*~lLCebe z6@Uzqs%ztx_tlg07(H2hRJej5GFSU2{zgec<`VI+7@(f;s>+~B)|O#-hVzpVp!+*9 z)CqOc<ykYff2cAG7}ZkAtaMkaFQW^NXjY-ozk!vIe+w)9)m4ASO30tF5{A(jbP%;q zGMt96ovaZE{ZmnOI60Ee5%ih(AwFe5`VidMnNbvc<T3D%l3RRWjrivTjSh|}9*jOP z)=q=s@aTU^KgNn-bhK9O#OM&nAV0zx$)T<e(Fawce}CoFL&ZlI){#X2j418k>VR_4 zF`Ptvgw(O*5Q2}QgR)^CO}Pa52&qFKl=LZJ#^|9Ra#%YiALX~tpb?quc<|rCN?m`~ z*s~L~3tzT>%J}|&gO#NJUtuNbpI{~KhE7OzI&Hnc?YqtF2cb}-7yA*@)FpO<&fe^? zDA4P4f1ch0P^aPi^w_X*o>slp!f?Wx?gn1tDs#s8G%)wo7b)W<`rs+5)9fmY6V6zm zy??$hx#a-_S2m>VlnEL<@v6d~X0@}@?bIa@Lew`3#PrvSqAb+oN{>N8-3)R;gY2DV z$H&BsfO;m*px#mg8!6W9E>+Y$3}|9rJA<RNe-0|H&(hc9$X3vOgYuIqPOv#AC5XMn zT|yRs`50w4O%xH4ttYE&b8&uPSd*IgrHX~lI$ecaVb54USD0JJO!Er7BnnUH`WW@` zo&hOsJ~>#p>fA|1Mq!S%{2VAEJ{#M+Nn~(@9V*VkF(^p2XtLS%TiTunscI~^QgjzM zf1x_-B4VrvKFkw@n2(X_7=;mgPj!+|JlQr!Dza1=wk{wCzK{AXvgJ!hzzXGU4G?o} zk1x$D!Z=ISqtfW3*KO;q&iBTp*ASLyvSD*&_f%O{@<fDMTKsjqr%jtLxOoSpj&3a! zW2I2a)d4K3!W{1I5Yxsv6XofW$ca|yf2VL&TButD^X8KN2}uUn?akU&0^mZT?pTiH z8uex1YOV}#*_wyf*F_}7E;ZH@c0bX#%GL<$9osJybS|!jjkPmE#ddCiEF$B}u$a*d z>(Q+)ho|?vJgOCMifeqg6*w5FbHsa13OcTG5p*4LWahR$>02?(|0%3Af}oPWe{QLb z*9vlyn|JHD@)9M8@jqZC7F4zY6@jU}q=*%(XL^2AtkAwRjK*u>9(77`DToNRMo$)y zFhIvy#Uy!(snQ%<ukO4MY*Y$>D)~7Z_OOkdutu59!;iYX+=g82S+I^~EW(>=eqj8F z<A<1-X*<oiz7#TlQc-R_11wBef2ar<V75H_`R<bAWz39Ib5axC4ziqi`JL!#mKzc9 zb>@_e=Q8YJ^`3qgGZ`5Ht+LD=Tr`(kt9Vg~wsOj^QBs0}d!;}Y9PbV-Be}6%Cc?qd zXJK~o!)))Nt%#(dX28l^Z}*P$i3wTl$e1oPohH)*dr{%2+D+-WZk^wae>FK($&4w4 z@<QKYXJ-}@yueE`0pcp1Be2iT?J0Ko@wugRW|9o}NT|^5kcZ@Z!+8*r+Rn}<vf{Mv z<#PDSf_Kx!b4~!um2Zl(@142rmA2hOzn=)z%M>>0>md+ivSkHVoGKZ)`_g&e#L11k z$N}k8luCM<3D~#N$6jo<e?)RwG>S?sOv*LwW2%#LXrFY^MX3E#L~lH$?|%{GnHl*K zomIY^Xaut|K-L8bL`(|0;*l`yPZgky2u|0on=vW#Dqrh@a>hV~b@`U3m~}b_n@U4L zXPorg=mO-?V=%nfDk-@Omv%il?XXd1DdhiQ?#;H`M7O2Ud!Az5f9pTL=$my%D}m^H zbVn<JkU#>_J^kUsP1&yWvR!NMan2a7vMmEiW-5c2bH<EFoQiDm+P5wv(!4W%@ZkiH z;*{@@*fou$wXdvRLu8<`(LhD69|U;M+Pm8Y7V|l$EH5LpDmX@o`VY7g_K&#Iug>~A zSHeEvN(_O+2!fGde_;luXo8|}423_G5MVjz+N@-RpuxHeg9H0n;*(ID0L>b(O&xF9 zfWSQB<GxY`j-?RL-a%K6wRu}43~(W%H~m$%t<R7&*lU3|AwdiST85K9Tb#i`Gb`Sb z4S-&p292m>Th4(qAh{w5;FQa3y{nZst#oN6dh0!}ERSFSe>qxj7{n%G;BJ6aJ=wCc z<4w8#ORmK8KusNy-*Kh4tiELPQY$FToc}JA`btco|Ar?4Vgh~4OtjzPNgG%CZ+H^8 z8|c?ON&AlxFc8aHg}WF>ZN8tH$jxUuy$0ezA?r9h!Eb50BAP-?d!?4<WOpi%r+HdG zo@Y1jp2Elje^eRsRFwPuGdyGm*_Jp_@{@9SDvup?V~>?H-AO`n0^7PzNHE*>2^B@0 zj4Kvp1wln2oX<a7MEzaod#!$L5f5>k%h66uHaz;iMn@CguK@HyasFN~!;j%Zqu#Du z`3~ev%v-$FwoO6)$*`pBVSm-CXM%R&<GPY0o=+Z(f2O1H;;J-4@1*YSC+|IWR<V{{ zQY}A};39h^yF=%<FGyEIz|x{nDcmy*m|I?Ac(_UknZQCdpLbUyRH9StBZz(Ocd4z) zD@(}n@E;%+{*MsrS7-elu|62T5!eb;Fh#;BLaeaG;3!4XC<1?I7qR(PufPI^V+?S- zSxYpKf1s6Z`zf223CRF%#Vdf(A0pOz>RL$wD+dacQ7g>BTN>*pKW@+!M}Vw~+G4V> z1mM{Ui|dENf3_n79J{m4wE_lE3bu7n&|62)t<TPYi5dV}Vhe8B(!CH2{7N@eT+xdz z0G@%Yu1#{9)8PJ)1RPBL(uVOv#2OFP5KYfCe{DJI18L{~I$~M>zd|hQSBT{<+0l5! za({W<3|vnGPCNFhbH=DKI2uGx9EM=<Bm089ZbFvsbLHl8F}W=7YdDvXb&RNI8sj!| zc@o6Qjvr>*g);G&??uyAZio=k_3oKXWY=7OOiTK4OzBcvxm-P+1{*p?&;J&&9M;v7 ze^+KQivBcx)SMBs^PjY0Wz~N`EK(Hq7X}v~M%lfO;q#%s?t{CJg_Y{}S4+&`tbVSv zl7G=z?$<<$*i$5z7nNCfRHdX%#YrDT8fy2><?`09NMvOx9;a%&Kk3RaJ*Zoyzt{SU zHL{Cp#C}&@@mJ^N=}VV0$LqaX`29SBf4nZ^uk-u?w3j};wEjTWz%<O5s~;l=e(#j8 zsRBP7{+)j;O4BQKg$b0QD40Z0j6hJ7LYZx|kHClzTT<}0gdC?}=Du>26<cTmD9j5S z%<I5>ECmxYW;5-_KL)L2^JZPi)29qR0tT3p;$Q*?6M!QzDVXok^tN=IZ@S6^f8bI2 zGw)aw2S)&9I=*SlKV?QI+cd8L(gd2>7LsuQTIiNp1w4ATV^%O+?*>7Dl@3roV4HHF z0PBhC)e`#O=&zNU8PE(MUp;P2HpmKvNp|_$yuDOh3C1A*u@wV!RGMf_(D#lqXGOD{ z@xXr3LrOpb*+vd`hy$YAtiwcxe~7EUeD<NuX#e1MaUg!9f#lH7I`qi^E@0Jd(p(SG zU3=4^Z%$yJUxUDl;8y<Woj~7N{(pEU|JltI`hKZTwahW6@yB4r!~@44SxeAl*0KhD zxue&8Bh^QEe7t0weP7QMxo}Ttt@oGgaNTv!{SgUHOI|V7+>EHr+Z9|Tf2|;gsD&lI zdy+2cy(mwDQP0Z(H%htE)hh$7fLK}z_GFHo@CqurYb5iob1>Opb&(>~NSY|ts(ipb zF7<1WrqhjSLnqij_e2>#8K|~14?SK*+L|!wtX}To>Cx)(Q&v=cjQOR0$>dc$=|vX! zgq|d8xh4jxPE66_>aHO`e-A9UdS!MsOzCV!goUdJVH#Oi4@8byO5Mvu3UeZiA?V(F z<}1GH`@0vMrOXRwwsj%uU@rF%(_;H{7_Y_1p2_`HU2L!GlG{BoTFI=NWG|xQBOsbO zIMG#A=v1}GAGIFf4MZMBRYnf2Vudfqtg<eBkqLXXDJqQ;(LE_|e`%ST+u@Q2r<W>} zr!*$k&+cV&N4WJ&9&$yqPK8+OD?Q9x?|pd+aLDmX6qzV{2@dLG>=S(y>^qL!7@w@2 zeJ<<Qq-Y(BJ3L7!aAA=PgG?u7L#?pa7~9TePgyZz->ZH(khWqRoi==4dAg8DzKG-d zT*&>q@Ir;eY)EKofA2(u<amO!J7?n>d^+&Ydg+TE7Lqj4xQ7^A@Km9S_@E2>vb#v{ z;qW}3WUSwvWvCUSt~Q=cc2|z&v{GmI8Yafcag+GXr9(P;ZC;ThbMPph3OMA%4BUKE z{V#5=(1)?i$5TfLI`O;(M`l2~<CzMkd3Qj0i}+yyaE4xnf3@UsD_!>q^WxAP>C)la zUv7r`pSL1@^rHLe24Exf=XPA5;8iTb4}37<AgK3^{VJvhjfo0GzfQ{akcltqF;M5E zdmP+EtGMAzJiDUa&CgLpi{?lYvYo@v5ycOEIfaNSG)fOY&LspFWVlo4vEVAAmlW)B zT^}usmOYF0e@T7enIsIOA}`2kDXYg(Xza&Lc+CWiVj-T?ng+3Sc|J5Z<lrvu?A!Fy zWIN)HAM=2D;(7ACMM}$GF`=mM<>THTr1Ld_N#<}2RC#GQ2Xbt2KU<bFPC9Ka?^E4( zbi?7NUhciFv>!sg<7Wz|i|kkyBpx93IeKT!rHeGbf5~?z$aR&$a%k6UP98G7xcsEp znA49*lE%9tAMryT`h8DFzNWDAg}1bPK}|GG53j>XGbD)2)&9^L7wq<qn#S63VanAN zM)f>vK8`Y<U*!Rb4bltC;Z*kG*&~s?^=ifxA+D(Sg0O}Q?Sy&I=eHd`gBvB)vdF^t zL<!^wf7?B0`nbo<PF)W%lU&~KdN7HCdp^8w2##70xicmn1q$w#-Al4?>fjwtttN%y z{XDs5>*7f9ids*3)NTf+GVL!jW1P_FtmZgNtbpR*xvXQZ`~d;|wI(HZJu^MpcAB$& zYnCegcD!?wspQ!@^0q>hFZGqNZn_M%802gKf6blO1$s*7G*e#h?OIBE==oVt`YvrI zrm|kK-`4WJ88iB}<GQ03<s2$uD9KlmQ=eGq#hGu9v+}!^{=RHe_<vm6bKHM%_E+kI z02@_!WlQV7ZN=T;o*aMRR)28X7k>5AQ@&4CCcxq!N-`9UF(^inBu<kQL(>Ee0+vx6 ze+T(aAKEAofM?;!R!3#Pyfxn{D|BOF6bc$a1qM76R-W=D1^EZIwNjat<*Xd2fWg`8 zCD+TW%mzd!XP{n7U@%0aHb?z*a{+`I5P(entVGNSu$Th~AOf<UK_d(W{HxeL5=sE0 zn6)ZhuejbDOo9)LZ~icA8M@xhdRDeYe@+3VCj;()*oKemX9TZD;=e2rQ7{0!^I!L3 z3hi#5a5uzujX4KF48y%%|Gi9<e;fd=*Zyl=ZY|6-n@2vSPV(F_2^3j#<Wc_Y?I%vP zsQR`8`M&PSqT+nJ2cv!RUmZXDua2A7s>>3(;EkXBS2g6zYmhEj2ryuEgzwE9e{9kF zX05Ps1<~riWGC4uQm)5uW#pG4^$c#;t9{q!Io>P3k#P#2?No)2?Np(!eyUocPIG1N za2L718D8RrtA+OOEXL^5Xo$!$c9jg6+evjiQFonxQkfY!&{`?>1UcMH5#QTvxpIv2 zG*dh0UN|>onJiyAS=UI%>qGWvf2L76xCo-lVZJ^vnQ5G1$nsA_@($nDbA2RP@OZh1 z%5P;H9}QbP4SUE>^tfPeHIkf2{=~hW6VrdZ3D0xWr?rM+t*CdJe4sReyg=rOC9l=B z$&U7Dl=Ki`N~hkfKKt|)e<ybzR#AOUc(APgsZbA5Wezd$ZMO?3LZw3Uf9dV}uHM%J z1&(Nr`6Y|aOLDNUsn856YPad#uYg@;?hs!uq~3`d%6LX_D`b0Dfj5=1zHzKtuGc5+ z_O0Ch4vXvG*$Mo4`<C*PX?maD7tnf};Nb18s)`!3zJd19cbQ`Td=AgUm$a_rP%qgx z<umkS{ah<-FYaI0Xn(y%f4j23hF9-)p7Gmq*&S}j*y7Hvb*WA&PEBSb1<-hUMU-8M z-HC$Dq1i4%&s90~-RoYwF)z8?8q)FdYqKY+q-2qFec}u1r7Hr7JNi%-=!v8R>2W-o zsP510_{B!$4{%=e{}Imr#%Vv|{NGOb4(CyFEvYezAyEXV?-80Le@PhIa{AV)nqZjq z5x{(y$O2ZQGthUVVBl(v)1OPy6+GAf7n`gV_`t$I3_|7~EAbI9RRC8)w$T9t+HTAy zrd@G&EvbQQl>~Kfx<&S38`J}>{LgTHt;7)u%qfr<Xj@5qi`s)X8=eAm06{mA01uV5 zat9NQZPgJ3I49uge~_d=OtL<h!08CU_ACWw(OZUKmi#f!hoizF*z;J3qtK9ZP!+=- z{;?hOKaBH!@N1kGKW%vb4d>n4|2WP|{y)NbQ&h3&EPy0{8V?)!_DmK6jRwk+l<uM8 zWa3nGkmtv4D&xEIa#DKzs64dS_rk-YMr}4lxKfd$Diqq3e`Qm_Pv!6!-bw1sWqlei zySBjm+Hsokyti)*6U7<x5}>iouGXVhUh$veJZ-Udgh9sBY6wZdTeIlm<%I6v_n&0h z|BvFlkN)rCyzNK-AL9I6Er-)1<im$w8g9f!(o-GJ#(B{niRE_GmAHil6KXEqlnlwU z(7eCHo!tjaf2cZp?qP$A+=_I(Ewp8q{nY9#Wh(=FvSg{dNtjpYM9`9kbm9!FR}$z_ zXmETnA98iIf*pKzVJ-N5oG&jWu<rgEjbLIl{t1W=1OV;N|NejeB|p+1m)-yP<gb|j zpHBQfkO4s%1R-IRMo0|B85+hBgjoNhC<0~(3c&~zfBT8S2Zey(dNKyP2go)Tj!6LD z`IprQu!gnGq<=>5DZsL`9ONUcj}CZSmjW3KIT!-1bQ}0Jl3<dI!l20t)}?arB*}kf z7!D(wwFd@xJ4J1dYbbbTfbIKaON>A^KX3#*PQV{9(D7ZLSt<E>$^@QPBq%w+BZmNE z_m%&Hf5FuHj39n(@Bt=3=db!ci_dxY94{D}mV|j7RgQ1o^zt{@H$Kw$g&OC5fqlb1 z)Q{K$vlgoZkqgm`PfEq!zHBocJ-da@XuY|Kf0wf$Xt>UQ0p1k(D`o*~wzJ>BzlB2E zi-Np-fq%F9AtKh-CTU^)uke4f+b<tf<v9gPf9~2_bD~lC`b0v+ILO+~NSyyRK7m~f z;cKqyQU9Et0R1#RL6gKLrR^HwW2`iI-70ig>!L@dP6d`C?lNTcQE?zDm8qSvkU82% zvN4}u-Gh!*9eHP+LTMM5@u&lTMedq7Q%5*GnFrCb%9?Jii-GfM3&D%uENx#sa0j`2 ze@N8G6yh7XKY!j-{toIj$+ms&{9LGqD!oXVmmP@1dAZ=E=O2xbzDZAjKE)?oSnc5P zn#C(|gB*$7LMOE+puzfi+(=;>Fl9G)?x1F5rP?)ouiI$StMxOsAoX&%=2mP!ymCLj z@b$I}K?>hfqa4o8*=Wq;(%6?+RZV$fe-$T-x>6bJyC@m=dN&?$NC?kD#S`v~Err14 zfye&A=IM9fV>>JkGvq%)yx%zYL%{ptyzlT1VSv#siBc3!&<q8GsSysZI7eU%O3*9g zC5R8(86d=Yi=Y8R=FL*EK*7X2!2wc$?JXSS9)R9M@zsp>2fT|ZFc@0#WF?bCe+H;6 zwQYl~zp_m(4LoVLIePp_<h+f~SNz0?pG~|ez)(NYQ<wm*kn71Su7RD?&A1kba%gZ@ zOb(bU#Q^$@CZPEOZ;97P2EdWr3~<S<zmjggP4Rkvzr;J~42B~x`4`@qcQ`*)WYM=z zZOIkvv6$t@nr8h+n~&eaJeRG$e_$ST6#s&sZvq8jO}L?7sIR?vpC;hdN_AZk`E~*h z=|TivgtbaT4A&!qiqrq(uAq-@>v!+!4+0YS1vXtJw-4FAnC_Rn(2(4rDOub>aTOTq zt9W3~J;A}4sVUf#x8CO=m_qb=J-v@*XH@s5y_KE%Q0pNdrbCB4TGnXIf9U{2D8Eyr z;q9?5N~!yc%f%9Yl<@`^!^>rFpAST^ydJn<=fQ5gsXJ<r>AvyYyKu^+=cR?lOP3n@ zTxemI4s%}Z`Mm4rH<Kw{s1_#<t<e#8w9ss>C$TgwvfE|YMGHGmIW^o_gW3b?k}geB zRZFj|E@MQ}+zVYbA5a{>e}wU34BfaKVT4RuUlvST>a}FybDCbVBVh~B!$bDzk<}65 zW$Sycdz=SWQuK(T5zaUUGzbkUj{7P!9=?!;Wx;Xf^FEAbm@Fn~Hc+P>8H#K}H=Yme zRKllJ?1UF{8_K(evU0!p<3K<5gH%|W6pyFQJi3C%Jl!x4*;tsMfAcHHmiOMEjJeLz zXNT{kilNFYr<HWRFqvvcr2tDnw7>sGWTzk>Ds~%{^P7xkc>iz*QQD;$qz=QpkO_@X zR#&%|Yo$6iY1iv_Kps!ydGYihGRCBx@J5T|vDrzdBzYfEH|Y+FGKA1+j`n+}8PXyQ z6qVR(XWUHk(sW_8GWRRH>wg&~ydBKz@R%~JRu7a@h2@^IFRFFE>NylPzPa?*u{;h2 zn*>446nX)+4K?{TrVVF!JH6>d&*FCJx%V{QO9HwVZ0C3mjGL)K{iU&u<f)11c<9fR zNqz}P_`osQbG4Lm&+Ka|TqQ-?*gOr8TWY%w7Jp*p@p0kx?Nk=}PJiTl;O{S_FMQ^y zpm1+JQ>)UQ;~rjrsISTaGbh1RB1#$H2u4%!s$8cLI)rlG(D&p1x&HVn5jjhm$iL3q zo3_`t*S$D!7pl8Zi*7V)orn(dfq&o1YoWihjL@kgWa_<T#3*GtSG|5r=u{8zm~~zo zcjxGmMwYY-Z`qtq<bNCSEbI=H=ZjD?Xy@5}s7jE5zm=U}^(|M_(k?z1E-%Y&p=(^q zyv-B+JO&qw8F!`kwqAEwz`40<lt_7FXw}b+1=(VM;1;{0+ylWGWOXN=Z=#ZLX1(Re zeZPW(yMN>9BPs(9W9F;6Px^MxkZY0m*39w-vDry7e44wTSAPtt`|;o<yX&3Zy`mL| zGMG>tCMu}G$xH#Wugt;lUXjNLi(0L<RAvv*-qario12&uo$&}sc2C0<&qF&4FToGW z`1Y2Ztg0G3c``eA?^BfdaAa+wW%-Iz@eXRqexQhb;c7JZJ}6Il!5+MavIndiTg}Wk zjE0A&b?=5VM}M-n44?VvS!L#a1xLKBM@aM1eWtUXG&I-pqWb0K?qxGI2TrLL67TT$ zGj7N_GO2$2<c<d-J!I!PG(FlG5G;_OrzgAqd%R}&!3{3Uv+iqUeu^^Prc`q7!~<`O z)nnx7eymZ6JltO*f$jrayT9!A!JnXJDDqYeB;|R@D}M=Y1Ny3>O8aCHVx-p}v_$M1 zm2N$zSz>LVgc#TMhIZclHpv2HCoUiL{LL=>*Vld^Rlhp+3yJ#Mao;f~nx-)fLr4Tz zKB5Fdt<(u4)^7zt5*SWnG|HgJhw|pNYy(X*d`kg9fq*53fl6;>D;T*IYxw8C<|~~+ zKc-GF(0|&1G`^L!(0~&a8{b+f(n`O`&6#68U}a{XW;kgIDm)zhxfkSG9&Q2|&}1aH z(5tnKg*OJYay?*;od7i=nSeeaNr9`c)h@ibcM}LG5<ew3!|3MP27?fSl^c?qsxkhh z7o=o^4&nT@RJ2(cH+Sn#bsAi24RK*aw1|Gket(!V1)1*O3o~(3)_pd<*a(V!1!|Yv z6=RLNd{N}tpcaF&gVZMXiTK;P{QA2ttcQI=lK@Nl?zM_#J|b@psNg=+B;`|d)Ta;f zjTUFE2&Ih~K~Ye@ljB$CGv|*|lXVt_5N`j(;^77-$$dAgEM{wZ{OC$?nnUw&t<Kdc zWq+waZ{>r4J}#q}?$fS(?{2T1L1>l>4Ntpq^@fkRGd#T1j5F-lsfV#6%68~J#F}vS z`*9ktB{dmu#$o8iz3=bpaG?q<$ulUHW~Uv(njRatFBqzMJI5xC2Wb=rmN@oLdoofN zb4qkpm}7Y$?yb^jBm%$q*L|PB5W-VcI)B{I>D~GL9B9Tc)<U9{=`6vs_za@6il}23 zwT08;Z-N^viP*KVR@o`9sV)J%uuEL27P=D0BNqx5BOKOZ_9$J;GtpfSa@d6<U%R>! z$9uM29Q#!CI93gIP4sCd5m3rahY&fovs<JgMzlG6EcBK68Mq5KQFGE;99s^GgMUXi z{bbgpOo_VbqVfKWa@0Iq(0J@c_th&nOPu`nM9);1<#w*zGU8f>j`K9M`I->MHMZ3t z?Osz@8maGEY?YrY!&e~k&3Wn5vbmC0D@{s<S8(kl{rym{mz&L;<Na-=F4n20!fp!J zHxbv@-mBGa_1V^Aj$H}tBYD*dYJbe|XesX#EGj{-<}w>QM*}f}6=8#Fuiu~7e40?< zGJ2XM7-SNl+`!fI5envlu((715KOM$d5AyydxgeBSsv59d8E)vY@HlKc-(oo^vbz9 zxp|ncH-4gzyG661yp&9S7OnoqlFwNtIb~MwxN}I`m(!A=*dGJ?>2yD&CV&6c9e}>^ zzW$}cl;hmXv!d(j%&dipNRmAHhO+hTV%}4Y`N&f7vvlU|=fj&*V;?unnC+IMrMrIN z_^DyNATwbJa{pAKVa53CMO0@I#tNyS^&kj(iocvpu1e2O2C1>v)xRxBPjF#&C!58M zM=XPA|JIGJQEM{m+g2tWDu1!;TbX?l^~#nQ>_y(nxn!#O8U_=cjApRVD=J6g;gE~* zS_vWJYrci;%dqf4(=^mM?=Pf9^R~odwd6BoyGIS`>9yX{4gz8KMs>PXXD{)``lP=y z=w-VMyrW9_624&bgvFd122INyy4Y#`hKgX{9Cjj^&GHbc%wgv@!hhA)`H+6)9+Q<& zA4dqMDaSqWV6HloF7G<O@K=t0V2@)fL@pNXF4d^-PoA)QbTR6Ad8EI@YIx8mx~8By z^`3k3sbIJ~37EPU_#))5M8HaHE+_(5zrsk81F9Y2U@R_2g?p3NSd2OA&c0i}f@b&C zDQ9N?NS-%)3KIJ$On>oh2n>l3=;^xi4pbYI)%~2GvBUCWjtMR(PcuP`168!q13FFE zyETVc@hz8SaANAT!ac9G)YPBg>q_s>9_}6M1JWHVPdK!p(!P&{xA@KbanIBln)z|L z5%M^}q;_^S>&z95!sP)RVZ3o~UgTt`wkcm}xQTl#E07&3w|_^qHte1MNQehChOBKS z1H2RO7(U0L(e9lPTc}Z~X~S8czZ1F_;@pJG6}U^wpBd5$UvxYjlIQr2NyzYbr+Zv; zft8QyiT?e*(+~7T`~yn#w_|@oiNx<H(OOi|1cu-+Nud;iF)K4daRQ@A3PyofHUSg# zhrJ}=v6_Q5_<uA5oCqw#!+=n%Y^d0_sbUQ1TViTko@YJ|O4|DDEAauH9|R00!Dc!E z2o~Tg7`Sk{*~d~D(75CY*lbu?5}N$XybK3R6GQ>FHh^2%)}11^mH3rPtt=}>Homs? zu9$4wjEKQSiwx|*7x}-F1T@|0%}od<fogeWYbm~M%YXmcyo>>aC`7;ZlDN`aIxiKp zIZJmD8o!ghw)@-2lx05Zo&OOP0Zat?hKfj^ZDHs2cNjzte`|uRSKq&;3Gk*phQ2ew zwq=lG^vMK!tiOJg^@Dt$gY*qegMcaar!KuWeH4WBt=Bbn-{Zx!6jV_kuXsGQ+FEq% z<kR}#et(*?*yg7PD#xdki>{8X9NYBq)WRZq9%R>wJg&2_p2eIwIUWL@CEc~8<8CNq z=}~(fZ}csEp#!H?m|q*5M42-yrw7U8w5*C{qZX^&MnBqIhJGgN{K&M7o-fgi+6YzN zp5zXF%g6<XuK6I#m-J^rNE53bjGz>R?pl{_Mt_bj!@S|O;G)mWE^d}P^zKO^PTRI4 zZ&H7b9SJXZ`2DhSnu;vHr(YL&w0r6~BWO$sDu1@)(G|E<GtJn27zc>!E2Fe*Szihq zb9MU>@<Wb<5$h1lib=-xRocG|Nnf4!`R0CqKgu))+38=c5P25hjwAAh_GNEt?)v=R z{eO!${_p=$UE#;}`2WQ9zChcrul4<k7><)9h2abeQy>J6B#@7wjmkk81w0kEUDJGP zORul2<rPB%am9)q*d_v`0IYnxV?hINM09JiCtp&3u>|Nx@&fFDrOa01Q2=njpfCeM z4;)#4{Y>>o0WZcR1MPHTQzU{gCmbwH0Do~vvV{f50C2%_2My+!^ybb;;h#I`Yt0Fk zFbV*IIRWku-$Lb55C`?kv7iBJm*i`kTw(QdcBgdw;!v+2gR`~6_d&f7duuJt@kaTJ ztNt{(wzslyhx?)gar01z&jGT?D?3-qN%KYPtvTIz`n*{th_m8;<GwE>DAH#E(SLrK zyMJq^2=dQA=fa_y5KRyW6V_aBHa^Kja4;q`gs-oS(GS(AQ?uW6#2XhnO1fmhjQ-8m z7?EOjorEB5eam_BY#yIZ3)_CdM!DAO2=iA8VG|Ir&`tsb0eS9GXtxQ9e-p!QgT(n? zZI(H=HelB8fGG^_5r(HcJ>fR;GJitG^s_gD8hhg6D7S{>9){W4D0RAVsx0pK$D_z< zDC3w;RMmzs9zzm&^WAf%aKN<eDntQ=mecSEthXIz{cMd@&^q{q7H?S}nvCL3?FsVK zk3w^t!_g>qD=9yfW3@YRM{;=T_uYXztIMB(>yMjd4Z9LfwqpIzlbOAmyni8Pc~APg z%$d$UoW$D#OIP7^*V;uyr~ddX-xiFwJ|dq6Ozj?e*5DU;8b@ARulJl`14?o@YWm(K zoiOpO>f&ijVIRcc^6ZsPJ#p00IOeLYb$2-thbC|xUyUd@*O}Yz(>PAy*?)cVjr}56 z1E|J{vLfq;Z_oZ0>$yk6mVe#o2blbam-vE=AD#aF5D_CtlmZNoLU0;mRy1T_dS!w% zf@2s(V<=6+AFiO`+Y&yrIU22~_^AVi;s6p?w4t|bk`-!-EfJ3S*g+ELsW-J6l>k^> zL5N~NH*5vD6{yn9G7?zZ<{KHqz+&4<)ld*x_p`774DiR58-lg99DmFuSBAR&8Kpq* zWxTmbu0(JJ;uQ~93`_{%UrQB$V=~*O8@0uDA_X`YfPV@aYb&U(_ZsJax`Jl;0aN3Y zd%hi-gwLeT?6^+;I4r>Xu@ME*-0m80eF+Q5tedYmx3xf-Il=Z7&+*QR1xudoaGdYk zGUOtP;!C&b=(5R|z<&TqR8BxNXQR^)fq?VBgmhn^kpMQfhRcG8%xKb)O+o>0-nJFH ze12{8g}<lX{iWMzw4<j;kmI$AdrQ}=`;ITx2glPnU-T}P?|Y8OIF4_hH5(oe<L9h) z&gPw)&dK5z=Ux7r49!33qnXm3SFtlR+5UCLEzyeoR>#t8On*3P*Xfv}ux@&H?e}rE z^fMggQB~EWNZa^v7M`a_IOBVL$DX0C6=wT7JDuBJm*I)oEiPR9bV=SAW=9jMGsL@Y zj~IgCuj^F!<r2QHiZuJobQx)bfCxH!D7<b)V_PxzGvgq^!^EyPC%8HFj?;upQyM`Y zjp130<|&<Q`G2WiXM_{XxD_ro6!+BOV4jagsc_d>r6>8;_<E_eE_>Q>X)r%WY#-hq zH#@@(<=zJdI%D0e6qb>8cwIxpPNO>Kbg0I2pR)yXvz2{!=+e@{h_}{*lAWJT3xNZ> z%HQ?-URHsoV)p|B`!C#mLJzGtF&6ctYq-7QOq(wEoqt-|gY6u;DYIL}iiKC$%GVVk z1uMprV;$^I`a^4uh);$m$cL4d^v7G2?@Nul8n$xv6YRy`n9I5EiS?`A*zT=9qI2o7 zm$&DX3nBKLNea>~IX?GLTkR6Vg?Z_zMID-V!9sVJxqldul)gjKLjpH@G|c<;zS05Z zhVd<v5r2$a5FR!B$Ju}u^f4tjyF+;QSvk)2pzaudPqueAK+uW9-U1@O`-CI*VrBM4 zaXrHiQ@7c{YP6vUA5i6l!6kH#c5k&=tni7MEoGiD1J<67j8@LJ?=K896jErla(5Kn zthkWT-6tb1z^EM*94^OaUXy6@X!ZzOJn0iWYJXHi4zM{yi2hcm>AODK_pLDrqIQ^u z%J6l%RX3bTJ@uIew_Omi?$4w*-^+BMUxm<l&8b_<s6yVKO4PjLuO&rg`C;p$8N}xu z4n;OkHq{{tyrNmj)LNnWoM5m|KU<&0-GCEX);1HOr<Rh3vm{00HRU5h5j9c7PIU>z zqklSD^woH{S0&SKb1K>W=oNJJvd~BGq(cT@klIuZr#s@mYP2NQZ{C|^?k4%BDB=z6 zPyyZhkHhKO?Pd7sDFXYL<@@?nvTpaTV}{$_&pXnP=)N4YM}E8z<Q_H-x^$u=m!_eR z8qV3vb_XS2_D!paCH|tB!oXv7kA?lXTYuhHH?}Kwfbp8}_FTwcVumR%o}u>AO^2Lj z@*Y}1UCw*Gh{J)Oz1%2;<Y<n?{gCo}Z!b1yCSJ5cj->}U%k45Y*&CWYA2XwYO!P8^ z>(Bf&lUjh97g8T{_&r^ZgPIfh9=(U>i)xcA13n)3xtS+IF%T~akHx(`ST7mcwSS?J zI7almbolGs212mYX;)-RO$;9zeAcUQ=ljM57se$d&N*JGEw&gGcYC&|Y%kFAD4gjz zO}IZ-M{B0|o+<FBzG7K}1y2q??Om_Pdz31aYkKnQdv7Q@SH5<byhA99fpUL|j-<Hc z%$@hx*_Ft)!?Y8~rwaAwE>h-(@PCZMSQcJgW5hA$c$hkxr;aFWMPE=Tda?~n1=aiD zVC6yK7XopY^L=+dl%_l3j$;<iDK(;#_L{ZJ+pYN8G!N{&SV0iw%M88t<dX8^yB*~J zih!a2JpucjOZ*1{_TBnFvA&U421e2t2~!M%!3aZ;2tnf*f?^2FzyyIJAAgpoM82(^ zkwEoN6rj!n33T+fuYdr7KURRHdSK-ZOg}#EylsO=n%we`axg?Bx5Ql_prXJy@KfA4 zU{2fC0Sp5<*Gib;pE)goXr~O^6@zU?nG}#@VGOv$%JDw0%4Hx~o!o3FQxsIs3AwfZ zu+8&w+ff5V3<IjwwYsG@vwr|kw*Ip7#w&m_#P7etnz&Rh0_!=Nq;!yOhlR3F<lj}R z-jAKPKjvP5dO?4{z2+aem*!q(Nb`T>UYB`&?YFoWpkB~_!@U6Yg1&OE>f>#OuPRmr zQ!mOv_s|@9`~1Z2tJ<*{4w5hT(esrEDLc5LJ;%PfQaRJz)ug-RS%3Mo^{Cic^d?bb z3Qg=Lv41)p)?Rnqin^yAw7a$TTi{Ro{Fom&`NZ6EjF|Ghg1<b=n*;N}ulxn}kEah* zth#c7I$zpzgJ55-jFHs4*JVG+h|1eP72f<H(t*I|`eQHStBRF-(g9QKo|)naw>E?C z=w#^&Y%!tZw70WZ$A2uu;2Mm(zTq5TQ9x2KZtlDInXyZ?zBlBF)duZ~m}=hB7cVrd z@pMh1OprX`!CVW9gK(_E+y1N$=l8K1_cc-*!E;$;OK-0XVpPod2P#%)#j^6!{L6eN zb(wC4O8M(w?7P3_kN!)u?9)&HCqdWk^YLnVFZ)k=_wO5b|9|DxzG>V2{*}I)R?rO1 zP#BF97)Brzj^b<iN`W0Ol7bmzEoVvQWBtYoBIp)Zo}imTH3d@zgxoxpfPWm2Z~)(2 z7>G~dkEO4mVvRRU1IY+Lo{#2W%W5r%DFSFiKx-`l!v!P;Ejr){m4k|t`B~ALGMg4; zThU#C581S?$bWW|C<-tX80aP7ZetWwtn00=HEB$2qTL+4UTn((#izWz0z48h8i+bp z%%-tl_w<1DHT@>bb5J$(+P6Av7g9kWz=4(d`-WHXBWND2N{lov`ZBICxBSjS{YYj` zivy(14*qTPZ~8K}n2{|>6OMyykR=X@?o%7hoO=!x^nV=67c2DP_~5_Si+pXCeHm>4 zEeFtfK${+R`Q8y~lfy0mM8C>miyzACuXhXm$?bl0x6tq2?$7#q5NPbNtc~v4dz+L( zzsoWVA5-e7cTBic%@Yak{%s#P#;Yr4^f^+hl;`s~3TZ1(QyvP2o2c(SSa*7CdC->% zIaRFu=zownFB5;L9OC3QvVgE>H>KCQ<k(^Fed#;B!wE-j*g@p<Ff7ves1UK`zSV+4 zQo}jIpDh+p?@k~0?>73z3IqBLzt<o8dJk6MZfwOqsHYu6PyRrWULMx+9Yn15xwn^x zvz`ZBMX7ef#@!t^Gydkn_OWKApcyWUVof|oHh(KBV^0WoNLhU!iFSf{?MBTHbn-L9 zc)x~seRxwnJS;pr9bdLTT>vZ>#7ziL#B<ogDgEC8RQrDvP=9u{KL=F%dq7223QCX+ zhR`&OVi<~`6iF~Jy0T9kW6<??8l&J3?ej2n>&h`;^b5rM#FmJ>DbPTWC;>(t2?8*d z!GC_1jvQxO?~MT?;~WQ5-W5n!NG3NprMKk-KtR#WAAO^$1p$u4ewJRfa?NC8#w#sG zV<5JR)6dCT=+^OD!E=Sy3<iz96*glGFy<Tvz>RMS%-Ck1w-Q-mTh`4t8^2-;u!6~7 zwgF3ksMuebp;?46B}vfSD3DJlc;IQ?OMicB18&O%#gA>kKSovPXE^m6Fy#Q4g1&AU z{6kcQ{`4X6<K04ka=RZntV2K9K5&QLL%jzWf*O~jnDI_dRQ)Tx?o-D{yvs{@jGi|M zF_ahZtDn&u!Y?FQshN|Wh{0lHGwkEL<P47*o-&a>?j`n$#jY8PbRypMUOCs=segSe zkRx!{T_e%QXDU1WxI0Z_aS*5JSq0jwxV}lDKcQeDsgIQ_XFQYiYHIXk)m83j%v3<7 zkr%#R%d~P8kbA;BrWChmkMpb{#AnTvBqWiHOdWa*p7lo1nPXtY;?;9`p*vt9q={q@ z5XUj)o!0;d+*#E0!4XMyS=z@1Q-5XE=M{R16JmIu1>Q1J)ICSyRZG0e6EqBx-x^L8 zmr2tmX@6G<*oWubI~_6frTN#IbyM>B-rtezcK2wfz;AIN5LUcIBgtY0>p*_Q-wiZ| zjVSGBQ?Ov$Z3!09h2fm}m?|05bo5)SM2a^TMWEHpTe|edkjX|c{USn+vVUhsr<8l+ zmd?+7qWu`l;aUrF?;^#e$d(V%Q0(@8yB#VTE^^xn#mg+@JzjL@;tlQ28GbssvaP6X z;Y3nfU*|h^b5TwzUY&<WmECkrG*&}tMQ40YOz)_7Xz3oPuB?(CqTlg(E%U|^o9Tz} zV0%M)t^B^0k>V^iLhTeBB7f|5IyudW&zG?5E_A`Z_7v8?vSOz~$Cy2H@j&kQH)p*s z`1(Aeo&(I_rg_3(GOXuEPfyo?jDz5%V!}-+XWYD($mx8vu&IZZ|FC`FKlJFVgv9Oo z9L2H`qsRSfae*v*x36^#^rzbg-z^{TObXrC&xxx^t>%f5>{m>PP=A<~j{d;Dma{@1 z!#?Cv^m@ymCqj5vL>PJfQ^=%eS;6(ix1i`$D@D66?{3tJ>+?Hq5nmRT=)M;P?e&VS zleCbHaqVH}F0?o9zH{h;!rl5vo!TpbLKh)ou57Go^SMsacJ~<YqLMCnK0Srb+R@T< zpomMNMf)d5H<E^4I)5uBk0j!h^)jAWNE#Tbu&=pcpKcyO?6jL~wdKJE!d3~J#X;F= za9$NeeIWE(y1%K9<G`wEFioa#t2juBjFhi-d1q;hcx@gpGc6O|wOQ|o2dueMf=jt_ zkJvm;-Q(fQG=7}U_w4P;DoefE5E8D{ptdheT%XbL?k2VIaeqC!q+Nk_j<>M!I=&hv z`H?3NO>-B4shU!{7?(L>?ydALAvcRT#Y)=jX|epui553Va6`mrlyk4;mAC9k^mk?R zihBv4SCr7Z-Pt=*2Qm(Y-J5{2<AXT9#>^j)<BQgY2c8|;U3U*IOb}6i869wTx6>Y? zqdxZS-PFy1Ab&N(PG49wP9y=T!^yU#qvg}Ert7{eC;LR)OLxW79r(eP>km#baT6k+ zkSe`v2Vuk?ORqeIWJV18)k1K1CSiZM5pb=jzK6vo8yoFq=X3edH|}=@;gx)52XnvM zH&K7GiE4ksdd2Lt*1oe5WIem(l)Ttea&ybfkjtv(41fB;v~Rdz4qSyXa_?J*-*;Gd z9QFT#ob}J!mS2rUP#6V$zV)B~#W$}Od{I~S^doiq)04k4wf}J9cf<^MQV}S{pg2ln zD7tbnf~GNoB4K>}6OK|SMWe)rTkSNcWYcYp9ozgzip|3_BLV*bIh@;M6S#HK|IXr{ zmAdO8^naEgLxBYZP^7Lz3519iKsmA!A{Ybo3PZqR#7dXei!s^1)1Q^P^p-rcGCXvv zVH14wT1htJtgYzANYE<;Q8ol1X;}vN*jn>uTa4*iCet9D2JpgUvpG(-+8eCjqhL4x zm&6S82eH3O-GrjzS7<Q2D3m3P?hrFu-&r5dXMc^476twyW|z_Q5M2ZEj`YC|>HU)$ z<r}$+014dWdhqz(qSXJ|r~EcD9T;4E?hOL&b(wV_U0JMItD6-=wp91aWPEw?C!6gD zfepU&1_eZS-Sy%Lri{?~zw64QhNKc}jn}@|g{I)v)%%t`ziqaAwa$HKxdPlUR#FT7 z{(oKrf8#@Ct#U6;<l=}-P;chpnvjb1k(uJOV6)AQ--Qf5$>>Rlltj`xPSPYyw_sj5 zMbw*v=lYE#o?@0!R8X>05Jm*`<bKp&`<c3y`9tWP$xF{+=q~3&;)iM0^eUYk*|-{H zhLx!(pBjhpQY$gigAbRF4su9>QWKikCx3Q{wP(so<Fql@Lpls2N2H1oN$_i*!htP? ze5WL%<HA~~jh8OY-WL^v%*P~#xTe$lV<sPN0q+YXA_XWeb8~hlG@Hs0-@n5i=B{M$ z%)R{bqy>?Ae9FnmTi-}3!%)OE>eOQ1Q>Fp$UU<Z`y@D9Hl=L^4CYFg}4=U^k2Y-R& zk%7w$=h)lhu(aXfhCwzOFR^*?PbN>mq~$MCYrbn`Y$ip*(wKCIfrrh!@ye`E9(SZN zC#urKz%@v=pr{ebUIgb&^75)1+<Fe7un=4oMn|m=US)AM&dhzX9$u9Urf$7d+An<f zI;GSo=n?I!4z!E-8*Nmhfp_?Fa(@rEBW*rRRE1;HL>uD~r?sW`e#&L?$qL;hnnY}j zgG+tu@`{TbC?iQvKX!RRD|gm#o|oP8bUR^&ezSGhKehw6U*b#1j7O_b?3(og>UpUp z79-vS&o>8%?Y-*AnE{y%$@JpL8S5VR{cu6N=tkL`8o}?Hi+1GtHhQ(g(|^WW#ToMD zbtba@UZMqbS>fTlsFcHA_NWu!9PbTZ=CPZ7=+7K-K6N)Lh~qA!)Ue)e#xd$`h<Ta5 z7yE{E?)uhXY|(n%zVA+(-CN&L)%Dms)DAvFdLk}`x%X`GMEt~K;it3PDyn<vDtZy^ z?Uu%1npR`8m%!6!G#>9Aihn+5`F!p*%1S)zVe@2qU%vu8o3iJ+go)&W51j^CeQ^ys zCvbgNujp3r_VDv<yzq0{aN`rX#B5v`c59<KuPeh%b)UYggiY0Qi@|e40oiytzg+5~ z;JNBVWAXH|C?}G+<lyM_GVS*A^mhH=67J;`V>d_J_ou*31B)doY<~z8NHC=i-e-1< z;0TiqtnzN06eio-nZHZ4-l<FFHQ33s=BdOzBGoFCagXrXCRM@7^8hmOT?{Y7P0UJ_ zWfru*bN=BJb6#OypPc_BcdSZt)`Yz7n(aOtQLo0gQ@j|uC#lh&q52hy6>(lB;~J#I zOvk=d4F>Z#idr0E>3{h+Rrrzj+MRblpOV-y<CCCM;^h{XiDuMKC{eBIhI-<2IxfN$ zaTT$4kFmIac%>ue3bTGy5rjg^wK&2bHeQ_e`1L(CZvPUSvx$xndl8O_%$I|Gwq%x> zFaBPu%^h{mWAdgA&s`d6<CBsODZVs8pT04I)}=Ij73}`iVSi8PMLarHvUin+zs342 zQ|pha3AcHqt7#_0bnEHWJTgf$Dgl4SZ>n)PT%u+7oYHRBRVAcd8*-0(@*EijeQRY= zvN3&!mV?Oe2#$l9ot?a?$5Iw|)5@wNA+3d4#O{vjRln(`2QjoUHX$j_kF|Pr=Ji7y z`21lT_es0=x_@t-Ya4&v**|EqeM6DH4P5_YV)k1ne?-iFJn>s%hLR*qAP55z1Q{4- z5Q;<zm?BV;LKqyzF%n174|Q|Elb!n9vnRGSgf!i(dUD{^jxd{`m;!5|#QIMkH_Vs< zjC`{#ke$H6?6e?2yC3Y5ZmXr#mY=+`B7Ae=pf`PRj(`3eV}3S*z<`-tk!`+rB<RJh zH<lrwlLuT$F(C8<O+IE*KqtU?bS08-4A@+fY?d4;Xy0L5w`~2?a2$MINCH&G`0tJ& z{xX@9SQB~fJFJ{CHrOdb_YCdn?<0s0-G=^%lL1Nw{lLjSxefi2lWDN=m)p=6PNu;? zEB7ay41Z8E=yy2TUn=2mIoYq=hD5K`Lz5g|muk7%%u|*0M(R+iY&wtDFT5Mf$|1T# zamp`scFt}$_AGR9En)&pO^SVzu*afegeiyILO9m!494xIL_tt6HJ@5%c`~{6qL7o@ zluHZ<F;+N>S6E9Mjx*KXCDto#{oed!C{S$e41Y%xA@BvdaE?;VyNcxB7fG@y8i{eG zW*!FnPT?PVA7w-j&F=DQINQtjdH}uob8%iS>O*IL;=J&anXVL8a-FmgLdds3r1M&k zO@WosaX`CFPz=22(#u+FF6I!D^RU-3Y^1V0&w>WYYDL0XFw@O-D1qfmwL*dGKl%)Y z+ka_&RNH}bFlym2o#6hq_o5<H`)9frUs8&m*lUd~6LEf!7xMB4BM7!{x=)J{dG@KJ z{~4$NXaVqA0SgF)!Erxe;~$>-1sQ+qwC{JyXbQvd6%Y}agh>KLVVpux0%H(*t!vkM z7Hr^u7`RGqGt>;=$XXr({~iXwO`ZXD$bVWhuD}G`*fs~t_~Ud7W~&RStyaai2?CI_ zV8Aa8?C4XVCMMBszEFUD_jsGKChIFdOSf46t%$+K1eOD-&5CZ>=CR3ad;9P<Zh#RW zRz|5!LY!@R7c@94-PXx~Oos+FI0)B3KzY8Fy4ZHiuLD<kX)|Q|OWI<dFI0Pvn19>B z#ESA98mv38e}_m-bMO&TKR4S2-siFdvwT@!I0yD8x$QT&@(E`E5^l&hX%@U<?B|sY zfwi{_{7?(8H~h__StXB#E2CPae@eMnUxPF^{rsX5YvMSm72AE$$ta!#Czy8m<iBax z=P#@Le}z{_5CCOgoLm_=e^%loxPPqr1y4C~yTA|88MgH8@)`8e_F3JWqmz)H&J2IS zO6z>ck2lUri+YExr<@3;vnNj@PlCA*p+1bMCYaqsHp=)w)@XuvrQ&mok7Zr@6e8Tz zMwNh9vYfRBadPYAa_4LAhR7D;SUAL1%7Z>zPO{sFQ-;6y_MQFQ)%}fSX@47qPJ9>? zId{K58l}58R8^Uw-s@&leH^^`<MJ8wdHZY(h+9h&<J33xE74;n+etKmNV~W2(wpU6 z6lO|i3w85OXhSf{S>R(s6Z$vA^Dn+dUNZ2Wyyu!mkf!irbVa?zyo{c+*<w9$SQ(LV zh+-^l^S-++sLu|JnpWJgfq$4IvX^D2wcb|q-@_efp8Z3(`}ovf!riy0eZNY9tgH`a z7=nUVUWkwwjiD6IU^s$eEA~+&Nzv4Yc(;O3h5_1@(12K_+tvZKB}%U_hQ$D%KBe_w z$iK6XT}2QY%tx^;&~U{fi~{q~6*JefvjT`-*1vvQa3B&eja~l$e19YHujxR4Z`C1S zYi)%p&~YQSWr@vwctu`(+bb(*V1iF<#E{wY5rL}+0R$*X0rV^R=2;24klR)V@R#2l z-VkIZo4+<>1KlL}uT=_<2~Fa9clmwy;%qe<%V*hsJVVtW0D6Cdd7t`Se_<X3K<^)5 z-lVyomTRHy#ZQ<wX@5ff$CwALptm&#{}}VuXN6p`I`sV)M*Q44pxG_o?Q+_>2z|pE z)A7!5(ZqBoBXb=^pZ6SB!^j!J_2bTO=8*1lrb1^-#+d?1-N;IZnY|Jdkw4I|>*Q07 zk93LLC)`TTQi?x9ulKuXfTDaC?0N1m<b#(l^u7>NoIBl;XMe30;!?Ph?Hhy7&@U3H zChU>&$H8xm5DT!NyGX$qy4?@S;xu8Ay-h}4Yx!$-I`h}N<kTHING~LLRvI(KXEK%W z7gP7wcoYpx_XJZ(C82{35jjqK1rE?&oE~WKK40V@Nb-|)NEp8D;mIR!hxS1y_Uqi= zt|a|BM~~?sN`Fh9dG!N&+*D%!2-=3k_SvO<ScWMvICi|A+w<doisLsTU5V?X>;03d z=ddxd`Y6){?g`@SK3*YAtanBCoT~su2SOVff815{tG~!ZglB!Y;2vtI%G9!V?bh7n zQmiKHphaE%)`GVwK=715pVPfkx#5PSNqK&}!prr3H-Dz6Q?bJ_goC^e4R^eqIN3G= zgg~hqdhvHHdOJqT3|(?A;$E22zfH9CQDqlTVw3i--8Gpegk=={g^fGsi1^9m670CU zc}kEq@bYHeBWu$Tt2@c{X|sd=NL?x0y<X|k*93|OfqL$D`rPNyi^kErJf|1pCZwh@ zWCnH+s(<7$<O>I-WbOss+gYEecUF_eCcwK>E=B%siVLNFDY0W2jR4CE8ZTYi|G&(= z*|wY5wk-P2SFCs4eW|O}+Ic{tu4@o))Dne20)*h}FKF6Mm+8!vtL<~`W?N<oq-moV z5u^7WaY$rUi2{~z1HFeD3Fv296C%-~6UxFw?tipt2Zs6NV)|Dmw1JFkBd9n)d3)ir zR6mURs;$+wGL_{oq{4Uo1T6E>Prz7AWx-l~ZeAnklBX5+m_+k~t6CeV`?&5S`c7Xc zrF5H=0d5ZZ5MWH}0NAk-bNNbAhu6t_PJJ(_p_^S5(69|Ko~##pfwj&lK+>3%Sz7gh zv43iT%5DmHi6e6WI>>wSG6=h;zOhhAqbU<>&YOtCyz)H4o<~Cidc69vcVKOFgBbXN za^-R*VCf~+s|?&Vk|!zCe&ojoGc%C<usz8QLMD3^0=TdLA)y8rG^h1`)iom~dp)b& zV7>&gJS{WuDgqJ;duVP~{gwDRU9WAhAb&@MG*F|@b26ocp<(EYaEj8zTSq_R@mdx) znPd!1sR572c5!kr0~RrvRqV{OY%NzhXL$r_NUYSE0(u?Q3pwU9;@4wbq_3mtE{FJu zbE~)!s4z85Ppaf$iPJW-rSPGdQqU1s{%+7?Y$_gf>mQ7&ADG+eR-$O`L9xPEPk;LE z@`DXP8(-=k#I*8!I@7+F&&vcqY9YheD><ieti2*Hh7X~TJ*Mn=JwkFaF&rPTjgG6+ z8R$rEA@#O`+k+>86ge(UgO^25lIMt+ij#4KmaEKA+%it9ad>3&8Oku*Oeg1fF<TtS zSU1n=gQGOYOLlLGX@ZS;KO1;3<$oak+&<C{yQwB<AZ|I&p`aG)e7d+q<J>f^PdT8g zu1J-zvktD36jI=@Sc!SwUX4R$zC;4?tQGcfN}iq(;gl1o170lF-_VwC*24_f_`AV{ ze+he>^dDb!^Yn7-ny$R&!%r=6|IvlMgeU&`BHs*BkQ?D5P#D7@2tz0wC4XUprU@K+ z4^dDsifwunCbk0zzI}=OX)oJbD#FRXlXsihMu7G{w0Ec3#+a~TZ>P=Pq1IWxah;#% zKcKsNNkZ%+4x0`{l6@O*y8!j(QE<9bv5n(xPuSCg;9WE#*iP`$U*`NkJ3B;>eQq^R zcJj2p#@pCrldc85mq0h(hkrx6GEU#axbQn(J9+0XesX|&djo8Q5`TNCKp(Lb8yowr z?&SMjcVb`6OuWcE8&Q~D;`>03#+|>Y)9adCUiJKA7YVRiyam-yh8O;rsn^6$yvz#q zJ=f5S0=-LGdPtD02cl5>!!eg-uAk|X0Dp6mm<aUs9L!%!Mp0Xt9Dn#k&|s}%<`3uG z=Ck_=thx1<V;bLmN2PRQ-zWq=n-#Ff*M~{L#trslMPKl%Siqm#E8pV4Uw7B=57TgK zmdek&i=zj8ebP@RK}vhoe%izKxrGh*wug;nw6lDvC&v@!?jB{Q!|~pV2Fy9c?Rb1l zDw!FbQYG!`>WewPbbkkMVe5=JueFIpKB~t1O1!G%oVk=)yo@9-EyS2#jGp<#?PhBv z*PZo-ZkWRmV!_1T6@VE}fjG1J(Gj1=7R{t+Q;|?`dF5AT8I;iyr0_Vz_orzF`155I zZFCaXMeT15eyJH4xGn6{JWQ(nI+)cEde!4RES|+-w9RHN=zp68m98Q#gv44z$CE-r z$y}R^sTPiOBDo&8-w*t~Zq9d=t~&N=;}5gVkcvAvU35_g11o~(HD4MPX3Fe{Iaa9U zvVK$6>v9R)kO7t>EXW8SS74(<sEkJ!qTnOuq88|Qg`;gf>}B&t_nIyDuNR&!x6ZvD zs+JBIbCtCLxPOS?3ltty3*{L?Ad*?=NamWML%7lvyulWHF&C<2ed*9#!jco7`noY^ zQS^iYr^^snFZYy%9u&&ioi9{Z%Uutu<Ouq@>gIB|B8LL?8NCQH6RpEayrS8ipM3ce z9r^UeJb~IrF|+lV)2odZ=UfJDb<`8u>|ybR9JxnuIDf^rYe}A}P7x{Lh&$xmKbpQj zUp>4XT%e%&+FX$|<&viBd@_)%8>%^s+2(OO<gsR9GijVdOAaGDhmOtt4zi}MLV<~L zaK!+VX%Z2QBt-x#;P$UFIvxqWUN;u=u7MTUwa24ncH&(rwbJ5{Td!$qHZ@RMA1!P^ zhiLQpSAXNH-^Vp_Y>k^zMXr@HD0o<a(q-=~g%EOazS|mMaKl}s1BES*)8wPzBDqYU z>l0O1zw(~=Wy9N<^m1M|Ol=WV2MQ;?2?xh>Y3Fv1O$?YdYGo|7pNS4;LBZqD4AmoS z^c$W_CQ(qY<+hMN2Ae`c`&ghDx1O-@MQOLbU4QsH0))KI`*6?0Oc1FHR{1$z#X9H; z==H|+mcWRfo_21pK3;J=hPevg#O|;<7bHnW*nt6Ac|sOiCC=<}35~X9dp;<Iti~dD zy$~!mxO$aMntlmoPHH?*6xSY9OP}CX;$9IIAh|W1No^Ys5`MT<=7tD2oQ?u;F&wpF z)PH;8i5h5OA+dcJhOtS-q&sy1<E&>|19KnnMXy2b>n7*qhVWH5G$E|9<a_M0yvX$7 zk-)C~X~kcjfKJ9zxKPO)EROTjWjJht7<h?+f4m<-=nUP^#OQlb5y7Ta+glIuBNX(3 z)D!DHK&9yx^Ea13bK5AYaG_f>guC0u1b^WyJ~r2VlaW@l#U}Bjp~&SCD^X1#-h!J5 zSGN9me})FlfLuDv{1wMb!@iXcoB?Eb#K!g*Jq$jx1H)W&dWrexT};X&6_O;5-B%2D zN`twTrjDPeK^@ipxr%Fm=wnp@r6Q<n)YL4c-0!5Q(7|@UV+iNV?V1dX6OEUdGiNzL zcXjqI&(n0VUhCxuic(Tlo0tU-VllMD^Q9tYWHn$JVSHT|La2hTNq(N!gKoeF&A#4O z-j*$yIOy;r%KXuFSt&~T4&a4gkmnJ)J{9r5!0xdB7Q6ck7y3QB!@g#B7!H&B&X-NV z2OfWE%nSaG+1hvb-#hUcwQuC-@9ct&VnNAH$kLBA#*IEDKV^3~`8Ha@|4qO<nWEmo z205{lEOehw-6L2CYBxYZ;r-x;!w9{P4g9LA@U~ytOTBLjw?BaPDYT93;cqwK0^4h; zdpdWv58tB1UO?C`y3tYWv%B3snn3P@4#j`FpKhO_p!ews?6;ZOCx2&m*_T~5&t3F- zl8Eq(lY)nd$E+q*<!1Tt_=A<%4Ez&jw-Y<yKV^10`?2!|e8cQ2`A25AR~vr8>~>-Y z{1axkp9B0kv$Oux%ZXv!7cfu|hM#;E&#>r}OvgNy{P5*hOh%sWG_v-W@#bJB_JM!j z)W@+%6g*K1UiPue<6`>B45+WObhrdmOO7I5fz#dKwaN@#{aIsSJkD*M>R87gkJBh0 z5~fF8bMO=&%$*jK<m;!b<li(qeeQMYlTm3XVpY&Xtz|@5!YvE*E&W<(?AvB1B_N%? zFotCWdsfUFOpi(;dZy_!|CLe2F9v_ajyW|A!phaJ8^kb4k{$HtQRh5xU9g%^#j6c? zik{3`56e#lta(T+yH}~mppeRahnw6<<6mdCiI5epSon3)rjD2}@>OSE!TS+g0btVm z#9VM5F`)Mxy9`%8IZG6+Jw;!_-5~foxiPyBos)Kb{&^JZ!@}-!L&^U@Z<Bw{&rkZ^ z(DUz}{Ket(w<mrLsVKE+V;Dmrj6_MCL<xkXP@E!R1Vcy^hNw+16C1J;`<U%7f^pG! z?+o5umC{{LQtyDn4U9JQL5jWf5F>jBN%HO#fq!n12fYml(szgQhR>Tw-X8BESJYm| z*nZET-OQ7W_qxKSyW@Am%yxfa{8xoqc<(sbVLgj?(}CR#G~Ye-_TH4@-A??LzT5lU zu1>rwCi?=A?Jt00Z@awoeIm454{T?l=>CSb6=GA-)b9fm#kU86@dZ*X^mNK7S`S_K zwcN2qT?>>JX}FWIA0hQ;og-h_p=&4aZ6D{J5qgeX?xCIP&kZA23!#5UAo2AoL66N> zgI}M$^}gzBf#z?VL96nmuNthgdJKHA2K{nyFW6$=Lg;tSpe)n8efT^?e*&rCeQkfI zN%dE6**RKSAP;fyx$<Fs-Pbbiex$&kL#D0jQ#<)E(WQD}Rj7T<C_T^bk7m59uiS)A zK+zOg9kywrHqMoID0P2!4wHr=oX+*68d{xZ&exEMCYWzto{`S@SlyfxYnErVuVoIn z*uu@Jx{=7DPK7Rpw+y1Wk&7vQp@yG-vc3MbodEFdeljO~XO(9h5tZ?DR1mB)W8W-} z292o*eO>!hzz2S`zvGx2YZg|nRd-H8<UWN%@%ccYCwA|~)wh5B^dXi#OLiATb4mcw zITj62+i+2?Mpy?!m1Toc>J<_a1hcSs4ZA~)!+3KIde$!lB)p%aewIj2@9@b1wi~7I zq(yf|cDl}_YGb5T+1!5-1(AP(g5RI~mr?NBK0XwKU>u?#6oM&egE|C85ftBuB#e-I z#1>6rG=gluXc~W^=${iydWVV+i@2S1WqTbLPxs;|Oz-3mLUstu;C<&X`FZ&kP4|h@ z4S+U=3B5;Bw=2=;4vqACY@>L)5)|(ym<^$}i^2Pz;g^xxA5d^_=Sy~HJL(+<m?S&w zrSAfB@?jK1>~6+;QX#QZV)(r)7*p?f+w`4+``%_GkiCD73uAjFc=tDe_kJ8K{yhq^ z-=d&mBGH~G+Ls)$Tx?Q2fOyPY*)W9uZ=zuJJqlWo?%Lm@;C@j5-$X&_w<w5dlGvSJ zWJ935&4Q_Tq&mC}Uj}S}+6E&kP$BWg^bYjv9MgwHKu4B<j#RZamAI0zZ5|ZesGQml z)XGm#a7uqp3Op#ZWLtOo&b1!!#B6rZC(bFgN(i!hLh;j+IpaDlNYe8kqoCUgVhEB5 zn_m<cwud5jny}KU2&dg2QSh@_K$}7hv8npjDQw!SK+m$AspJec3IqIdW-x<}p15oX zcMQA%*YpCmDE>HIWd9s@HYV05O)NSs6b4h|Sh#<)TJhpw)H;yp1V^ASYMMw#YaR9} z!uUX2uQT3YjdbzHxyvF*RgC!Wbn5*Ak3LU*{YPBz*C+o51;0A+8!m`%`WYfo6d^b0 zqc$u-=}l52G({sD`NOGAR^tQ$p)j^5=KR!;3d-LBg81Hcx6vj7eODW@9sD*R$*_HN zY@>f%n>ybA`tzo}P3~?)aU*G)n%ypjyuH`A2h@kv3HfiD+&fS<w)tV{Uc7Sx3*_H# zVE$=}DzZ017xX)MHQveJ#xd#SZJ1KL@$;S6y|>7~JItcw{?!=WTUK`PgLhtwy~Db9 zeoE|bXoKPue&g_O?UMQA?FZ_7K|!<B<uZTtf{YamS9$uci0PZb!#N%LAs**lJozU` z2<%Y!CrJ3!?AcHFU}eF;4u#(!;db!vAR(|r;on8VuV&B2J;(J=izDC7_s@<HRICl3 zFq|K6XQu|v(b<_Nltr!@B!me<JYhe0Mn2<fCN(Tt108Trn^wlNgXW1Oit!>d=6Qd! z+R<N!<^l}Ub+p)pm^@B7dQ>8JfoYLGGoC{m7R9uL#Fef?u~Zb!lQUtxGulfyZU6^~ z0Ted?`;gV1A)3M<F<6k;DbnxP4RcbnoXYx)Niz<;knuRu%qCV5W)N|-M)cERbyx#6 zDj>LW2muv@a4e#!O-?o8m>`eTkK%u2#eFb5Pu2=0h*V$+OegZHbTsvtt3~Kb@P6hS zz@!B2ng)R;cghO7-om=$%{fq&dlkF`5#ES)IpbDXSzsDnd&+Bgc)?qPVBz$MV<ezR z$Ohnt{)Vbo^)#)h5QhxMT0QR=Eew;kdv4*I%n}sH5G9(f7qu<iYjJe)u33M{9`Mp= zI6B*UQG&f7pu;no1z5>snz#5EnP(~=SUB(K_?DcOSJycn3`Pb^D0IJ;m<R&3WzdcK z-1nwZtu^%<Id)|tZ~;LfPqdh}l>~L7(IIsi4Q_~i^U#ywp!L+LSOUrhAeOX&S9H`n zW@?`EPT|WAD!4=+NAqQZmx_NMmr6@pX2~nV>W=r}v}nPJmF~RBmGu$eVhVyaFnqMC zd$e*$3&WBP;Lt@{@!;qEbP17#m&Y!hQt=Q{xl+PyPak=WXhx0jfYWi7AAZsxk4SFo z$Nl`exVO_h&EL-V|I*qM_%Ut$+b$O!M%wmfE%Tt_FXHTTe2d{G+Dw1K2e<&=g>QY- zJCwt1rAZJ|;Zx<IjnpSaZ1lA2^faVE^rnzl97UPSbRhseFkh=AO0V+Ssf`5pN+j2U zZa-LOIJ#rV7&rGknvs(E-t#hBb+jx=fk$9?Ly0D_fLnX<td45EN!HNi5cXHlIpWuj zLXMe(_h%ogp5+u04`hEiI=Et5^4+zYn;N2JFa+rVu*zql=<WQ7%JNz36y#&^$xiDk z9J3~GA9HYe1g}e7n$w6e5iO>DG?_Z41cl&VkS8!cd`P*)7ZV&|O+I*GH8p3`R6}OH zvO`lIw7THCu0`|oRvd<=@9y+;f)Gx=zXA@4ftaI9-v|-=Vi<o{WuYWbavo`<Ttg(8 zvvMS&GfOKn#HuqRCSag1{%sT$^??O6aqU>3qE^!6th$_}V{eVoSUI{@4i1|SxA_Sl zVThs9`n*Z8^*)v0>)2}(jV~Q7FozE0Fd*zOcTTq33QNIy=&`%KJRy}y<O^{~jTb?? z#+hhVP?-nLt<8VzjABL);TVH^2Y7>eps_hiIT2vTNCyzcBWU)F#;097rym#EmsUG& zo6fwTq$W@l=OIKfh|-wVGNY%BcmvKw?Jf5MTakz7VD(X$y;At#1P^~K#q2&cq=X|@ z0%s-T9)yb~B@F!1U;Z%Mj+Wo08!tO)3F?&bs_Uc0bzpytj8{7)`~J>7jY%U0`z0$^ zLMdu#xMqRw$^}u~NG*E!Lb}fXJ^J+@h|pi3{Fmw1HzE|HAc)vBCQYH(_9u>z<n{|D zNP>nTlElzG+a9OLy`6=^=$~%IZ9E2{-$Vy_J8csCxEKBQ)hyohD|-veIDcE4|6<%f z-ZkcS9{zvsf}!YLfnw>q<>YOCl)dHS#=Q_^Kfci<D&7@q{HqZAqS!~<b}uOMeXV!A zG*;}Ja~rAJv*^=3gm5?c%=Ye@jWvG622seK2DIlZzWqk&Vt*4mV|zOR<ZqAD;_uTh z{}_V?is4Nt=uq4-{rRjb7)=~K?Eg0X`ay_t2(W+e$9*eAIpptb$Ne|xm-IL27kfFB zBpEY|vv8>YIH??<TO9fF-6ja5st>`(oZ9zRg_g?B83NvDO(4;-qeZ&y_66471Vp0X zhP~;s$7&A&0_sJg`-Ac#^N4h>gxJo|npuzhUWK<}@{m?hCOIPfVO4_uG@iwY0#tot zcx6q~b!^+_#L2`?CU!EhZJQ^yor&#aV%xTD+y3(2^ZfaK?R9lmSM}-L-TOM-RkeCl z{PA=E^<vO`z%jm2ncJ7eT&Ik|WiO#Fqc>!Zd9<aLy&>J{Wy|v`?#W`tiRDVZ3;i#L z+vXn54@<3K(}^u9p}#kvJFf{o>Q5=whVSTL%7p~{_56-wk<{7*k*uKq3Q6SOHgA$n zNuT`{1yZi3E|Up5@Jc{Ni8M?q8$N$<<4|P-xOVr*^Jr(kq(}05L{%->O)ymN-ZMrJ z;6^$D*=)bq2`|S7mUSMszaQVF+GXM=Ww${FXcfwIDLXE^B$?akpZ3BjL^S-?f^dTv z@&Iwe9Hpq$;As|Dq4Q5P<3qFr!=)U*M@KGafNUv6B(p-o-a!a`xJEN{cZ*Xa%^=<d z;C{>!yttfIVW2_x9h4|n@eVghdG|+S;P#QaUEMGSyd^lmB_xDrD~1H&pRme$ne<aJ zp1GH!#ejL^*whtdT**t;Vzis%PAR#cDw~T-6~joJzqbzU$MgIk@9gABr@vZ4y#X_! zX>c%ZY@_kcTlOy|NC9fNF8^bY59sp-oMzeIJF6Wh%&dfrDoGM(#zB^&F7b>O0Cz`l z=PG3ArAxG-WNng~N~E;<z1q$&q?_0V<bM<71tIg(Kq|TwIz01)Y-CR}P@h5~dY~X% zOLlB3CpeoM&i(bKlOSF*Ci|>yHE>qE5)<Im4t<>=alNSR_<cIc+Pb#!#;!gI;OXF^ zzQ6O`s@{Q-3amJh>F@eQw}`vJw(_Svx4Q?kH_YzHQT5s`tr@;3=_!_IsNC(8F3=Fp zK2Dd?1&6qCyq9+IK6&m3w<m{XZ9QjqYEyzDSoP}yN6iL&m4jl9A(AD+v5qmnWKk@x zp2}`o#H>MpX1q`)9~pD`J!u&Oz%_@VuJ~AOGHP`=6;yO#4rf?w2TQDI5%)5zr@F!^ z5cd`f{@M=Xio&l>mQDJY>9KDTJk);;|Gpn-OekgUrvC!Bm)C|J@?{tbaL`)jnKv9* zEK4~VN(}RNVE#9aorz8lFe2QdF9E2^<HjoIk9BX6mhDE-#)RoQzrCh(0B5|r#$KKq zK6A#?xAP~TbNd|L{UaWaSa-!}ySY1Igj+Q;k_;a~bcA=fQoGMJYbPV^1@yLZcI#2p zl41YjaeU&QxlT#K8WHyN{6ufbDCwOk%V7wu<blu{84@h>{sHzluDbY%@Dg5~@%gL$ zTOur^Wr5y^=peVCP<KK%0LG848lt)4#2S@uY@u~^Rn&!ZFl#NRauiJFSAq=sFk4iP zM9si4)v*cV4lD)ywnE;BaU~9_&+NsS%M4GJcv4lk>5YMB`KB3`ds|);4XL)d-#LaM zy!2dm2$S&LTg3a>28DaVlJ;9Q1G4rO+#)Fwh&5dJM6ta%!%LSBz^!qbV)x0K@+*nP z=Y;>|Vxg15f~?jyx@VpsM4XsPh=fB{h)Tq8_)#^LNItO_z_pvy`&M$?SxI<>S~@J& z@zc{2v*QFUy!|Zex$V!NkHjo%U51j(IcqYiqOCfe>rTR;ZqGgmj4M)A?!LUoa+~@H zAJ3L978jq3McIyYfGC@6QirZLd(_lZT2;q&t3t!`7B2O=n(?1=#71^$!?#a7;12H8 zTz2qr7hI5^8?Y$Qw^^++o1C*<h^Td&N?#W#$P68=kV%UL!b&GdeWumxhydnU4R8L3 zF75O-YpwIUd4LTpUq+Y*I_&1=1k<mc=9n2kCN2et1d&uWAYp*-u-}LQMn~q|1>~8u z_DoFG_bGB2&Z`zIm7f+E>-W~@JRdFac8uX{SsSa>^M;pB7kNC}Q3sH_@gIf`=7tTU z+>WfcF;R>Q3gmY&e}RKFK9<Bc=?W=aziNax$T2XNKQ9qiSCC`AP1-<jmF3*m0ouNz zzIq9+a$L=GfNG_7it>p87<|Dp72E)ZVXJxg*v8V05cL2EMw9Rnf1Jqq;8CYyVGzd# zvl+yoR!m1J&DH(fcn^BPig|UgWJ(Vt59+4V%^+E9OAv5pW7$L`gngi~`3o{6{u*cY zdt-E{u7_L9&uNA#`m(10!3t~zpzjFxkE5zfYEUzOfb;CHt(JKA3Hy+dmW=t_I#f#( z%|fp1*f)C=tupsn?*eBd@9vd)QLVkWWied+>TXH2GBwFM1Za()vqP!T>cOby=sr1F z7F$H7C$qGMDL$Jj7JklH^I8$(G=_Fm2#@dr!{s8;@^@2u4-s+e5SqsTaFwq|^e;4( zu9j7Hz$@lj0loh;M=LIPEMugNVyeFcPKX3do?gZ;@r+OG3W@=~$v>2BCr}U<#PztZ zao60X4;xHkCRgfFZjgh!M}?~IyxwhLs~!EZ7Y-Pdk3wg`!EFiJohkftuBU3BkFcS2 z8_2eZtZ&LQ#0;CAo+qF4WGgdH0D<#^jf~YZKp#`>5*wRHTL;;vtc=vKwtYS!D(*DG z;qTfu&~sL!TV5flivh&&XZA<Wryc#6?13*knAp6O;zM+ya2}765gXAM{kl&lhsM{< z{YzWnGlI__pE712lF%$;Rh&z5)2$X2jaalzBfBgGfIvgNHzw~7)_M@`#!pl;a6X!6 z0Dr(@HU919bn7Rw3dH*&{*>?bB`qTw>yMf*9(+ANZ!J_D*x`nHV;pf_jP{&Q3H$z< z`$AyvQn8)$a~r1iypYa-c8+G91X2QOpJ(}d+1>lM24FexU9>#J0$zPWJQneRu!8)f zxI`JH;kTk=dPmAX^F?TSUCIp9vx0O|fD8)b8{TOym*U^jS2&<<V48?eVXV(iNu<SY zi^hvMLaYyNts0MGu=*UTS(Zrr9MT;4KcrnLW+Ld=@v@6VfsTaN?zFIWqbw-%|M0`3 z@>Nb@%;BvH(KZndUnJG-tU_uFYyZOFIn20k4}0{$S}z5QuG+-s-T7&|njlfG2>>TX z6oQXq#cE+<s6Er~7z-N^mnlLl`)fvxSs@pDNDkT1^9CYMoKCvTaXisL|9c`>5r4z5 z^B`kJH?5W+o+fz)^iObmDDn7;HXfNHi&gl;S->x2qKm&rWwNLaf4#00BXBr2hrm_N z<{lFLmtR^TxP%I{-ifa2Ac+UhA_4PNuXtw(TAn7*@~#K=JaKf&QxLl92f{z1EyPC^ zIyw#&N~jsTh84eW*bqzaQdA)i)6Z#QA|OW#4i&otj#Q{RYUNik3``<kOZjNY4Dq;R zl%LBKYO!e(Eo;b3jY;AsJ2NIsZY8vip~ZZWbBz5V&~w&Xw<-5;sX9y%QUGYEM`qCF zR^Smtw63rxV1B^KrxN7~N(*}&7I-fx>K@Q|$pL4+PtO!g`%RZ_{I3h%)uBV41R%!X z!YRjLe{OrMJqf40M5Nm9n|uVa&wY_{hVBxuxx8SD=Hc0QDXjFWJ)<6{h|UwZH?=*r zUm+2;=sUshdlT9t#5%t(0Rh|mC3;L@bCum13LG^c8o|z9)q*rvKkpRh@?XUh+1BrI zXiF@fu{3>Mrc3>>PI;m@z3c-<<^5^;pB``;mF9US5s!LFO-pQtP9MzRPRkyFK_OKr zP;iT^q~rXDq5bFwGZ|;ahPkFpb@zM92jZdAqwmd%V2`QroDq!iI{**)o<PEV!NJkB zQ{?Hu{z$s9b|pgcjN3WKULA6gE&?cjc@c2&XQv}MpGWa0@LUGGz5|Rd(N5XR`PLLa zIe>~iGodQC5SR6L+3y{<-4r4k)g#k*wkDX0Vj#|$8~{8SI}e=_^R4|0q#dt&IOW$+ z?Pw#$Yd`;xJ^!w+TLd)zqRy7zYRo=~h~xQtmq7Ict%+ZQvWEX;;DWCy%vCQ?!ixh^ zkD`pyEn@G|hyL7jJZ?ag9`dYylUX7uIcT2E#U&MTt15cnfK4BvnP*bep$!pPasVdB zfB{WTG0TXZLkU{H+rWY?0*(KEWG~ku9~TN?LPv&~lMpMC7#C2V$Mg)R2&v5f6CH_! z%maqgsB<_Cv23;^mqxt|Jf-6@A=@!;>o`eNa*bSe4`pb5#>cOtMvG82*nknXIZE`1 ziA6)Fhnr%9WjxJ37B1()q{{qwwWuEifi#++5bu<VYz7~e_ntDP?|H7AK~qmSWwBPS z4-A4pRDvN$z!rd#Vb+Su0s79qe&aT>h&9LNHW>$(sjxLk7QFL`cvtPqZSy;-i*L0V z;3UGQ)uy@r{+*cA(Ccf5oKLBppZ~+sLu~t}wER!Oc%*vX94M4LaWB^(A+yL}dmLf= z`{GRJd2R-7pYL}(%?%B*s*Zd!dEK)$DANzgyUK#+Lja(^iDp@A#t8F<kyzv2aEaL6 zh^HQN!QFe;j#z$EE^&1n30b?T<~oaSy1E%$V@q!2CA#f6OUrS+O)d6h#}7PL>ciDu zgIAsYNP=;|Jj&LPzSVBB-5MMG$xhw{ZMw@VR~s_@SvcA1r8;pysN=4Ys5AUbq{$A` zIQc^EPYR&32gpy%J(Z<Bsn9P(Z<tyft>WU50rMBrZHVp!GeUKS__rj#g%lImHJZ#- zyH7nf%s5PqJ%;)Hd~B~gJ)s2@d5US-d8s$O=kEz2UkOH?EWX?HyMnRJ8nymq)tF?e z=QaO?N?)FzD;oUSn8m6p%-2gXtFGCwlw!E>WeeyE+NwVd+swsnUKZ6I+moG90N$@3 zT=<mKYPfUevRr&gc3ky;gi~?Hvdl9|MUWlawBKQ*2#aW$?HG*75t>Ik)Cxn5rI%wm zg)H2aH7DjpDzg~XCsS6eJl(gG>m_qMGi-S-RgG@G3&^IsHf14r@H3sdmfonnbCD_q zl>_|vFjSv8SNDlK=+;abI~)+YpHG|-fN&1sIUM6Hg20jat9u>5;7We2-R!DD#l~p} zUxCkYdkLPyyYWgdY<z-}{}`c!{30uFURv}%W9;DrLGQB`;o7wEiLQ&^cfPavJx*0I zs*w@w36>OhCHS^S29nFcgNiBbqo)>pc!1z=Gg?&N>9ERAyjvx=r*ZnLti>$>J@#dw zD<?jO$CNbIy|3m-G{)Dj^O%mWyOZlDp*P!%ClT2%yGeFYPmW(GXrfAB!SNiD`dC3o zX#Tlafr5<EhG<bl!*ZUF@f`r0z5@7iJ0n+@tp~b}H(#4pR!_=r&YiUb^Mo4y7=Z9v z;%(N|oh<j76O-hHX3YNEId5agYSP&wN&4VKZpV&WQ=`Y*FlyIm?5_y??F3h^XNV=- zb)UHIk+n^|wqG|YPrn`)Pp9d#=bE1833RZpAYCJVI1wH#yZ+ju-aTG>0s89L5qx+I zMf?mEHsmOoB|98Lbk)}3=V0kNr~^E7cs;%+IX>by$(eK1c6pn5g|h#a<F|OE%$6Ke zY~l1wR|LMg7V%6Ck`w6fXp9i>l*}hkZ@BVe)qTYNj<a1$xraNe2n5akd$*T!=8bS# z^Lfe*>hrMpe~-teNA~c+Ucobm{<5)M5X4J?R3M1La=PkWlzP{EY^K{m0M|p^K7^V} zKanPy2yGSPZZ?}v*CAC|!9GPKV&7@r)Xu}M-*cKv!-%fe=$uZtO%V1;)Cm&Z2K1C- zg|1joO3{l`kxqt-fnnYeDuBy0kHBaq)*mRGJRCF~&p{M}37~gO?Z{oyoH2*VOYxnZ z8}2P>*HS1B)wN)E59pB@(0hA%red2tKtr^o@0M)4qM<}Z;orpKH;%exfQ0Jo(cSur z4^Xdr3kM~nk*>@sscP%dUIAmOrx*bHecaG^QI=8-*`a-E6N*1?hK$EIsiyIE)I&)) zfAS|Xwak|q%j_<fW80l?S;`~*6$EPNB>MF`pII|K=N?B8gsih90HFkfgk=PSZR;<- zy9xRot+`~eCFG5}l7+&bdn#X93STGHpN&-;&1MM`KjRSiMb4FJXMf5DSKpfLt!q>& zkaJ33R8bG+hwEl65|!Kza?^rx)R&`=TTZ97Sf|{TS1050ZqkOtTH0lj(EN@`b%UWE zB1Rt#E(l@lkEkDR1w>Q#x%f>2>*`>S9{P#qNgN_W<Pj=Rb^LABPVJkYT6M~K-nBDk zDD@wY>&wA$__Korx3cE}Vi5<QIhOQ8QlB8Q2h&nY&2Wt@bs-CLu-B`tg_i`E>rotT z#GWXNcsDLjC{jj!1_CJS7CCAycNeTxfdP^IGzRILr{E~>1`v|n6LT1`C<a4tjfrOC z0?ko9{cP#~&1$VsVhlyvI3nh4%)D*M>&^X!siPHh^oTSVY<-I4>AoL%sd~cj0S6>% zH-1soTpJ^xu^`!=*gw!h<}oU57UO>EI|wC(wk?74o0He+Z$aPa9*)*R;4e%wFwtCl z@+HKbsu`vDO90LN@60)JQJr6rnQF8O#lKE>&U;~Me(=^l(+7v!DRpb^A2~aLLY)ri z#M~t18fHL_Q6cAqxA;}ZnxGBLh*MpnF;@w<)S$5|<204HF}4sa8Dlw!bOB$wXg12# zL1mMQWZQaBm}1Hv{{r7LEFqQm(zllAK5aAF;NmB|i~;~uPFOXvyj>GVkpIfjXW7G1 zR}SU}w$*9Ti0^VUc72FZl*tp!kJaq4JB{og9`jx5A?ju+qVSr}W|qCdQpxwukSm&i zF<UGDBqt9BsG&rW?701rN%XO{h(iy>ah;kM8K_DC)_CfJUnEKgkfZ2n6J2%2VU_Sf z<BKUv5dv;}#-J?YU!=YG*o54~CFQaRWWBTJO><D{;V4Z}7IcA)DuEWBU~f1MO{DI1 z?(h_W{TQ%<f*0~Oe%2QKl#U=$(9C9X3=y9=Ky^cSE}^It=HFxz08j@hmM$dGGNdp1 z{91fX6ctijR|x1C(&xF}t;<F<F^$iP>8(kVods}8XA=<`HnD=~wDe8(>@hhHVObwC zp`%(cf!qjfjA_!c&5E?v#EL}pc!aQuD1WEH%CBEQRGCNR0giO@pJMZdzhw3djBerS z2gTlF;Rky<D5A3I{WI>aCld(wC5yFpKb&dl{x%-9?W(#NzaMk6;Y>N??e(@)`Ud#Q z1^`M8A3LIY01lt3ei8f`Z`Q5s5(;GDJ6JFYNMYx);Qf38H4^&qzR+MKdLA*iD<5&@ z=f!w*<d355GQudf^mVw_ByY4BqTh!RcAa&l-;tlmpy{9QFIwQ(SLBBmEyrzmAe5df zZcUVK2|ps&WkA_BBoEqfCT_H=P_B;9mI2kAIQKU5Qr92*QTqDJYnnUSBAffKqCb$$ zQkvJ!E=FQTsshcA=~nNwb^2^o_wTLyGlZ_*uUzH#9`R_u@Wl-0v_aXQ_@zs;*zKcx z^liXG@1u~icjwyS%)Q@xP7d;GrX0KIa5&)a;#CfBOHH<68ltl`0V90Q1DtgH*#OXr zdL#>w`8kC#Fw0jinRL&UYzweXT*#GsbU9CN^Gm}t8M9=T99s_+(|knNeK|;e!a27~ z?l%9;Sv>1xKC_hG4iAiB&=v_>LQ!axC0#|oY?pM;2d<PVvn7iG<5m0TV-YX&&0<Bd zJ>m&#;l*l)&W(<qm^XA)ZXY{V8^DppS`Dgx0bXl570BqAUueJqiXDU$lEEk?6%ksm znvjuFV;^suhh<oWYh{}!U7*6m{;ZBeXtFpzPmc?jhhlOfr;PFRQWnXcQDEPjh4|8L z{v|>qK4Z~j7`$v_0u4ztGU;T#XPUCc_C3BXi$E}|6H)8k`aInTBKS`c8Gt_Cx~U6i z$aq2z(pL);4f*hd@GRbUjL*sR4g7&df*mI>kWW(0<~9O6CgCJqT#oZPyo*B{7@5;< zG!JIBL$)KwpUuv=V%wE_9<y7=KNCd0S*dVZY-k7u>t0T&8{tzAU^Gv6tesLTJ+a#O zwmNZI=$pWgw?ew^>cZrk4FHUO{%Qt7ZmRAejKP!%>#cS3_x6_0w$fI@{L!ukA()3K z%!<0oUfE^EO=yJ{R>iwUuXnN~7&qHg+tYO~o&@V$hmr{AL|GOUp}A+gORw)RaEvsK z3N@U)&d9w|G6E+L+T2%rig`FQ%`vl=B~)J2{f3&VvZWR@KNbw$KLF==*xsKkq?CA` z2xPl9@eud4g-be(Kj$)Q1Rz%TN?X8x|BRPovGpcYj|icDh*&Ik_pC&WVOe#lwl5H7 z7$h9Bxh<wyVascWNRirN25LC{fg#<%lR6b?8~jecv<H4<l1jJIP;^!Gatb@=4gvEN z`}aPp_hwFJq~N?N-yHA)6n%G;vgU|%=`8MH6)LYxbSMx}a7g-{NUP;{r%XjzfZQuK z`sbbEpgrx5vC5DWkAJs8>XKO&^B?uAP6V#LkL&m>t+03o*>~<iWPWw|zkue{kH>d@ z_3^*9_!#!_!QwD|`hlWmaOCEAs~F6w>!zRz@&5ARZnpQc&jf(p&$@xgz^6*p+lzv( zx5F-Ni)%04ot&lw_;(l0WpwY?tMj8BO7+IJo%0F#eVd;*`jQ<XMS53m_pYCK+U@ua zxlMa=x0A|ZpHb*tYU7Q@a2}pEebb&pzMGo!Pn_mnx)2F&)nh}~d><k0QQqNBy4SHy z#^6y`>K^K^o3DVN#|c!ht+o8&r)#}V+t7EHzMYQL9$h+<SFF0?e8FS$Y->XCv{=Km zU{MP!pUc0`U9G9!kDD)sOq$sH5#@Pr=1gx+=KxQpy3ff$P{fT(%;vTLAl)#Pa>rkW zox2j<RgO7%AL|?9sUq_su|s0cva&A5XuFY}YQWw`(FK4vSs?0}3-iS8y14LIkBxYa zOdb(lHY|U<eI$Z;F^@61kRS+!zC;lZ(CF5u%cG(d45fp@BXySgUJ#^?_i)GZ;^w}Z zdRmL-Z%<utZ6u=t+pc|Xk14i5^$omvxzf=W2G*5(%OuSPGb9U0-`T#3l6;~u779Yy z^NFlcN)5nk1{1W+%8(v}&8akBY#*qCL!7uFatF-Py-C)+sl=E-rn2jvdp_~btq=lT zg+PC)&4~)nUzX9zKIZVJ6BFo={%wnu24Of|@V8W^1pX&#L*VHfaq>if3yP`itC-d= z;`Y;x6TxI*<i^7~HeJZ#^B?8<h|);|^UBI{ljVS0@*8X&RK46+o94C^m$w~Ly6Bu? zFY_VUz|aeTkq6fNO1x^oO*}3*M!rL9zokbL=^hH<7<i--P*Ig7Ypzg)uHGVI+cnbj zl6om&!y=7X?jXbLxEm>6*YzXD;uIebzsMCBxw-I>3M-JjotV&3=Y0>vxf+zQSMRJl zgePFh;TX2HA#55n``2Z;lTxBVjzoy)RZ6+57NxL3v~NDSfk|tx@ErC~H2sRXy9x++ z!j7HEvwpq=Lxjv#^rL~U!$u_4=>ivp_D&xwkhLy&>Zcb+UbyZ$<SK96&aDw_B!l2W zZgXR+O60@Q#%cGF%fr^fVN8cYCQe1f#yh~LaRw52(y%s_RRDRsdII?%Rboto>Q%Yi zlqMA_xP)W{OBYwQ^@c$<fMXk(0B6m#qGS-ACRwfjaH4(G{wN1#;YU*%>gC%4QQf?C zQlg()1}uCJxmOuub|MPV6xA&=e^uNnlT;gDL|iG<y@h{h!tuFiq5t+O3*3Zu6wx=? z)<Gn?5c^Slx~cc#1O4AA^w^Bnc}UXJmBiNA6dI7)faancgt_xk*cG*m-j}#Ld(E=P zIIH#yyCshd`ha7g^O5NLu?~6To_-zW+2Jf9(ca7pTl2Zpg%>NB_x$oA`-$)|2&7nl zBA=mr2i@)!V9;2u{J1hng7XQ?weLUQ^ery3ajGbiLus>;qOoo8PVeVFTTm$WUGUJd zC+v~V&8SuG_}%Ocyv8!w^sbbu)zLACD9XGz=!k4~qXe@v1nvQfStRa{b>)Ftf<mFf zw;mk+qB{(}{y`aK6>>eWc&ZBwVy}e}P$!9vS&ecNVT^NDO938zaUS8-zySb0x7*P} zoDE*vlq{fHQg`AIRjWiBwsx*dpZfBUKVe#?{bAURffp|g-bY{&*H)y?W#!q4LO8kp zI}YQ^Ko(C#vI@3D8!a@upLwQ*GYrN&Jr-%{Df$*EobL4o91U@o7z`|b37%5@EW)dP zb8&!B`KdDR9N2^uNxFSIBzpql3JxfihDzH@J*vtS8z$X7sG>=r|D0{KHQf)3R4B?) zWrEaJN*TJ@eL{crei47YUc3N$yRL0PM3F%9<nadWa7ft^M5nQ2P{9!Tuq2Qe!!EQF zko*)Gr4>2u=lr=MFC9Gh7^oX<N&EX{)!2eMnKJKr_c6QRVOfldIk5mQcWn3U<awJW z%xe<3Z#`QQGT<HD+(-kRs&R3ib$dEp?wTBWH=X=~pQC?iD4&+MGYbT{vwz(3{zV{v z@*BM0(E3z(3mNh50}*@5lYehC&Dr8~TpK$tCB43MtqY;=xM*T05LdW*Zr#G1FYIt; z_?!*Cyh)MIIEh$KaGL^1x`uswD-#RL*S&>7?(*;Ga6f>2Vmvu?9q2%m)A?SCZrN3T zZ2O#7+f`?J0DlE~ekjyAbZpJ{&@G_id%xL<S{rbg9)?Tc(;F3ILW~#3DS<X4&AB+} zmC~w$?SU_M5EXl6mx&$LE$vs7q=s0%@obg$%O~KmDIZgFcNhXd<FE))y?99m#eeRS zBXM+uGAR@iTJkW%=KcDEmW|kjw$#nQJ=oPy;jVV#4Dvwq)DGj5l|60D*^+$c1pNBO z8B`tFh7=MT0m1%CO3C-Jb|Q|7SlQYviEsTREm{$^44Nv@RJMSH1t~Nny?rvupfINk zL6w(wI^3|%NzV;n;D@X{A5X*K^?(0tPBEYpl{P~bQ1tA782|NBmhsW;h7`)T0CkIW zSTxUbUR^H#2>6fP3IB^qfmBzc`|HRs@r9CZAFx+HmHITM)CtUow_!vct@zVl0nN{v z-yDh(3MAhiug4B8k2>SieEE>?+q+POE*)q6mwNC;A)P+QN)ykHK{d{mHUEH*(Oc$g z%jU1zj<eAB;RP>GH)|q^X>JXh&_wb!D4Dwyz6(eU09}dqHU*6fdPY~kZX?T+sz*qU zsV!JkO~)0UPx;C|zG>6c?zU~(3&QETX=wy6p+!QC`0dsClhVvnM|ad0vVT@ppVvM; z^lne8$J_!r#@ulT2zxzdb7A&F$lI#Io0jO}IqJK6_lpv4^G~U9Q!`5rfd}iMCug}` zqoZ^Vz}T5*#bNoPtu31t#N5VRS%+p9s}(<l7w52RUEe2%N?%*#8@9LlBu*EHm3O_H zF7QRa*I=C)piO75?eoO#mTzYT!4!q&V|wu;4H>k{>P$uadcVnK<X+M$!!n_L)2@<C zRvELxTsw0wAsu5BwAsesFKX>!#h6ayu?xt;cYYX2k`SR~i-Add^9-!snLzuEA~&No zelfJ7_rmKec98|n95}HzLE$qbEvn{0)@v>orPU_;Qw044zx&8=_Q%L=6&1e5FM_;w zxP3eYdLActbp-)<Qs@naY4iwa>K4I7`!N5rhde12S}_pjte&H5tF*qlq6yM2qTt7N zz&Z+_>*5|SSBvk4t_^I_7I#+Z>qA)LWhGez>gTUs%)F@n9f4}NAg21zU@XMAnS9Ce zp-k<+aQfXXz_C1)@tnD&)3k!)xmLG8cPSA_YgI0^;b8u&#6`)FRUTyx%Pj$s9=`&5 zj(1Ibc-#a|n_ce*6@MEM*mMx=)#9`N1r(m_sF6VohDLBR8oB26fwg{<jb?T9a7avH zYV*~>+Jfj^e_www8qtD+SGO&5BpjGh!FP{3wf?0*<t;^tkC06nm%KRX(J^qH$(1h6 z#Foa>t8MZGOj-xx%jYi~?or(;I$utzS}UoCEh1UwblcIL?T|4W`J^(~AM(Vq0Q_+% z#W{iGXQ%RbbnPNM23(N+$MJ<I16#}!SS01JtXHGbnkF8Levj{AWTNIj5{tSfN%$kr z0XN!J3xoC)t`g{vTv|#g*PR!1p~UcsWN39{z%r~hb5bzdk~(iMsm@2DuLV)r@DTb= zCv^s9rI=dLPxa|a{tmZu2^Ir^0Dx^^CL+mQZL{*1R=3swFgjc53fAA0{)E+iWL+^| z!7bFd0y74}CWgV~(mN$J-l1jc9fJy7Q^QmqVJAVN*9;<p7-Jr#YZbgz3^xj8d0PPY zIwOkB6@7$lUA_NZOj)J~rEs3l4@m>e8(xJnob~th>n8$r?j7ITjUF7_0ER<XFM?#D z|4h!QG@~-<hr|AABmT^Bv?wqUW8@!X*yB^xfI9*g-1HrKkdU5Av#`r9)sI!C%O^2> zJDCC3XIhrllrCi!t2Cf2@x+g%oTPKz-L7xN*v_{E<@+)S2uc;_=-5(zTft4d)LmB# z4`DuI!r&QLuFOLikFT%d0~iw**cBbsjGv)vH;BQM(5XVYKG9F@jq=)=rV<vpP*)kG z40>Hjg?&*GN5!96&M&SsaB22;@Z1sg&`=I5*#fx)PKLDgohi}lrDJ%VR>7$<B;V?D zwjd8-nD1ETq^j!h_7S<H<0t4Q@BEDO0_GFLXjW^zwvyLxai9|~1{AMNsL0njz5L>M zwagR!w1}XOL6IGvET}(rdbUW@%mjmQRD=L7;hrJwO<1W83E~;jSaG@T9kJ)Ujh6SM zAoPSIUo+Y_%H2sbA@;wRgI~nkCs7@O%E9jDOJ~F=Hd-v#d19`P?s>d9^6HQ3DGqJI z;2?IEG?I7Rpz7aU1B^3E)3fsrf}(H(rSNU9cx6Z}8^Uhv7jpS4>G$IwdNI6lhsp_e z>lkNAEJ|O|9bbA}IFI*H4;9=C3JQ30*Az1AJr$kEU;T!?HP1DR(XI*;n#~QBsMq9e z!i_H(#9fYF@pI3}73L#kbP|%nZXO29;tA=DTCSRp68hvG00dOiK6Obkd?9*&d_5Zb zI6Zt_qHLui^iB^R6S)~WUIAOc2th~o5<v+u8GW_bqQtG~TjhH^Pobu>IfN*9p6J;g zeA;aR){1O+qMnbX-oGr`;>o|G(p!#bJQyquvo>XYQz?m&a_~Wzrs|$wA&gaAOpF)H zcbpl7C`iFt0s6%F>sL2>2!xRiLt@;pg}u7FZAVN*-u>5u$2DVeUcD><OSyDgemEZ^ zmFvjM#KM6wL-?!^y7Q|5GCjE5fJ>BYwfoFjf=E_AjJmlC;cu@hqP4ST3>gAA*P|)! z{7pZC_t)i*y%4+iVe3~o$Ezt<7rfaJxA;~2v+NjUfF)$7XJ_Y)dPk$u+FA{!GW?rq zn-#}9hUq=)w9moG)Q6@V;EloaRqBQF<&@J9_K8sLRV3p@m1*z6ELwLP|5%Va|NRG8 z7yTA~z(&UR5vUO7TidylUP$iFl9~*~uhIL|m%BJ850bNYfl+2JhLMSZyUaC{EyJFz zN#M>bz`Y|z`^Vn%z1-ZK1x(`;FxcdytayRbdEMv`f#LD?LxPS2lFJ(z;h+?^qjXg- zMMa$KV0x75VAh6V0JKGu%Z>(7TK(jP(P=PZ^eqw70=pha-(otv@@IKyuSxt+0jZ^I z0B0-Vbk*-)Wy`_UxuM;<Eg|Kz8=GhBO)D0v0M}qBm+&oWKN+<l@Xy)jdt2bsWGJQ8 z<2S@GjInK>AU%N~>ddM<V*g1$)&aHdnBaC2HS%6PLB)9dRqlC1C4|AGCFGnctnU<s z$-!xFyWFsh;f1ch{gO3kW<MOHsGg|%!RFg7itR!7@i>O5C2_x!bK{85Q_l!7$87UQ z0AK1g@0uwu&Nc6sDExMMAiYExT+IyM!42>i<Z=DqWKceEJvDtbgn~Euu&}~!eGK0j zSHY>@QzeB7%ijzi_g|pz$)oXmNOS19;`j7kb+4;K3InDrV?Jp<?Sm1bZzUmzD6|C# zf>J{fRoWAFuIm6zOME}M3WRP5!E*!%0e4>v`Kvuc#z|z+t<6il`96f!YXOLaqKlqs zUj?9n*)Z*0LzAaZ=;v4V@&>PZ!_^FyBr%me&%+CEO3WE}@9-m?=u&Yb9~Vwd6HQ>6 zg_pzYTxvfFFSo|{Lz~_ZVGa>*TmUMJUD+I;m3kmz1MgKFd0kYg!(B9~-Kv|PfEZYc z>kWyn^aDPa!>RL^r+w__!7SE^%+wl7hQ(kp!~hk`Ic*aN4=1<_94Umf@p=}j+)E{h z2O8x3HBY%LppM<D45QP+nWZy$goOe7DY_yjTD8~nKI)_KF6QCsCdPZWxk>1z+tKMV zO+%Q2xfDN|@so^@&I*<+Ef|ktfH#!d$_ZARrmyk|h5x{+#sI5UhRsTW*sDd3O`tlH zsWa-9M66oa6D4$Jp9VI=OQ~gqLq5)zj!sHB<lO+<+;KsYp{?7~C>nu9!-4J4pqN<s z3T!ygO{lKU)JZ0bCXnD>PPE)*p(qaCGB|T*5)ZLpH1m(_hjeS$ZYAs=05X2xKyMe_ z#1Al)jUPDCq&HaMy_tnnd=t%FE|(WsY#rps^ATd&jK>3%<m(*Nei+E#Cp++QC4!#r z0;2rYtkzl5nfpw3#3+d!;uvHMnc^~6WZvr!`wPs5X~l9#XdSAY(MZbl?8T(N+PXlZ zH`~dJ=xgE6^~b93tx5AC0X2+N5jP+Kc}2D!?FHjJJcmya{&3M>$wSD>E17)R4&7Bs zl?EGGWi=AJ(8-8d^dy-OJb{}hv3t#%BWipZFrzgA*k~uL#RLWHu;-^vNT#4uuv}I8 z!8n+$YQ|D^(6U$Da<N<7VS@6&=39eyE`--~HH+!T(uWku8znr?0A_fV1Q46~w?&HL zUU$V{8poJ|nd?R#>*DZcc(i)^s5SYf)yr**z@ZoLI@G%kt24w7aUVt&1=O1B(bzOP z;QPU$Hmfp{P&aUC>up4?CIH6~E$?7-3sYP-*h68RLieh`N2Z(--`{h#s`zvA&AIJ( z`MM2<3PLA2R93@S0K(;_z^PN8bV5o~+mgK!MY{>yFxQ2@N>b~cr-e@oOTW0>I>Q?h zq3O$y@_|pnc8gJt25XW#d_!IQE>4NHG0We`>|(zAl0l92u+%;B7rKeEK<Q=_zu$Np zQwCL?F0sikIMyglgfmfob11SwZ5ZB<i=G#P$11l%1iOu70m6u|ckULtmOzj}dN?)n z*KVFfw1#T24>ZchT6$&NI!%~0eo_c6SCWE4OXgDncBHT+g&5Q%>Jo;Am<wxs+iYpD zs+42kWV+=;eP<0LVw1@-ruHgIQpW8aaysHm$vKLxwz;DOS+=A@tDp}ob(sd56VozA z5}Udc%YPVY0}OlNdEE6rrKxMrCKDm*mGaoulfj-Rst4wVrG3QFW*4|6&}|t?kcT#{ zkgz`UIBG*`=eSXw__~@|GufOCKsK#@JUF`lk)$oPmzOFn$vA2Eq0Q`t2hBKA#hbaM z2TX$xb|e=Q+;Ng?eXQM4OO3%c!&d~_Tsn_YCu3Bg0q_mNdOA{)DSpWgpC>g)aV+Jl z5g+xe#8G{<p4t#Tj!d+@fP*F4TIolo$jt?K_}E1@|J;^Pl6zd|FW1<5_SvDVdED?t zxsIxWuhK9yc3^C@TcrhnQu&)KaB!(IqHBx9kPO17=xHooygq3nr@Zl7h`a~Q`D}$h zpCl6M0k9+!!2EK5LJ%{8?=bq4V6}gpBb`t8&4589Tr+&!oWvx2Ozyi(wk8N2@0B*p zEiO@}?;T70f^-uK6MlVscUDon#_o^ci?=4>;mYJzj2P*QaE9dWHyytSB@m6y0t>>+ zyQ1Q2w=X#&%=z|B!><nD=XBYhA@a*Hj1wEj19*+AklvW8aq)DoBVPs*jB%e2P79Y~ z_J!E1zxM!!#rmTF@2)Ps&0jy>mGGbBDnqvq$L#w`8haO8QcVz~A*Zsv2tKbgkzJn{ z>PhS-_-t|B4QY1AdTzfO(R;SO_EZA1u;EoQ)fFEzZZ^24{bdO<@5(uwu4CK8p%EQ& z0YE(DCFOjqsf`tLIh{@1tI>KW58?pokL9|O(`fP8fgPk^)}CH392%Zx+i5u?Fn}&l zyz}Fl2bhJyBiyJKfn_BVCvN2~RA4A2h0f(~fTtxn<ZDb)&7Sna#kq|_h0-y`7YO~T zqj;Qv?0V$#GIgyyU6ydag-A;|`Uy1^V7$HVH<b%V;~t0Oqx8uwK~+kUd^tU_ui{T@ zL8`<xFGgilFL%mkidiyF@(V{#vqBoEP<3PCMLBO5hmjvkGLRNkjw3Yb_h1#X_5qi< zKkP-4pMZEq3!kU+MC`UmdK|s6+i{c`1C#~5fd|#flz2crlAwDUL|h;pJVy(!0c<UQ z00e(<$&lIEC$!<>B)X1hTTmk3aH97vP)vUZzwEl{wDqm8RP<@d2oY7sd*QT9%&_0> z%%#Vb8(76l#*ZAXZ>I(Ej7`L_2)RlNLP&`wD%0pz{noET{H(d$9<j8jEFrm&ZJQCX z$MgWx#M?ZHB-*D7%E%onA}%@D1cdeQ>w6nYm{g;>#M&*BW&74WxPeKJnv=N-FF4w5 zw3kM;F8<h3TD=}$F(-ES7?MV;jl@TfOR4t@yS=yMQ`gf5I!TbyO-E{A{jeE+&8sa? z*$@_Pt$KgaPXgN=wk$M0FB%hxlg|mp0Tsh?W--)LxbwXn+2uDEak^V;1u!N+p}CG` zX4Sj*y)31<S8LpTt%tk0#Q<i*^)A?V`jf~JltG4m?5$e*aA}iafmX4wpr?qBbMF<g zUdIQ-n0}J&S3;!`;iE|W$hMVey^O8m8|PfCkb0kJ@E@f1r0Ms4WXi%*5)B>PNuo7C zQ`F1R)2%;lTxYCyrF@dm2H*uN*<6o;Q?R}iYHDv4@%8;Qg3LLmo(eXh(%GY9ptkNR zPq+Pz8Z>>Htl=u|bdWJ>M6W-kA&1QDa*hOs6@<K5&aMcFB{2XJ;7a2k9?DW<2Rr*Q zx5QjeooV+o5KAWtO2TW6eYJ*Q8sAX>K8EqQ)1fv{k^o8ansps}3!vn=x4j6#mWK-K zpE4e<_2+rs!Jqhw>L|m$fih2lW$+3qn!wpE`;T}wHBrPzF<Tdmx;qgm+6o!O7^ea; z^m>x<*tMs!XC`skcY4pl$zX1(F?^I4q7q8TYIR%zlg@$PoOOhO&<4uCdrE9NejH^` za}90?_O@&35b&>)qXOoJ{g-BEx-{FNv1P4ucY$Kxnt-YjPay<NkV<Z?XjkBy1_U}i zZVIqq#E8!3`&zG~ooSKby-|9bm!v2UvNCNzBv3tDGi2GdhP-b!9kw}ruPBJM*%x`x zbDB*w6nfPn_$>5~qJF_Q?zA{vW!Bgo>4V+J?}tryQqGfIy8$?Ou)Sq>Y&yYx0_Bkg z|GGt@!;c&P^-wJEkCP{Kd{}BpA}Wwk>NZ?lK_?slqCYokMSgP?im{PmgVQarYPw7D zuH0fo4<!0}*!ptb$N0F4nC!iBcKX_Wx8-W(=Y1N&l7=BE#Scpu_Gv4$dmio~g9Zzt zLZcu$WhCjR0s)ZAgKfoZcl};K9(wPA<(|fp!1J>d(^IfJFP_QvF2jfIdCm<bjRWM~ zGvsc>%?9I(28j{bxjBLNGT8Tr*j%~^ffK%r7GvHRciKS4Jm1vvhxZillklDU2EQd> zIxTbIIiR0++g?MS<M+7_Q(8Wo(R%sQm&S;EVYa||qyqMcvZHvO$0%^^@epG;CisI~ z27pzkPXLCDJ)(C+=V^>5K?UH$n;FZ@wx-PQ=4Creq#mkNFUayYTsUs8kILbLYHLrE zCFRMSX7j-%pXuR~ou7?yk8~2xpm&=nu;g32F}Hm|6zVZX`sbQ?*N+F=wnIKDizo3W zoMXl>Wq=Az72MER2Uz_MZ=z+N1emU;?WV%pkV3%Y_+`q%CqW<x;dn}>*omzOk$sus z0k+f$0i)xS7skpg5d1{@wvKvSMkXY9W=Z(6^IQR{Ojj?|EIT9F9jtIRn~4hAkG`vD z_Bh|sRf|X|<vc8Ta|kQc<%v=k686RZoLyKeC_p_k#{p{a)RUhMdzRii4UghRciF}S zsGVFv?FS$6jS|+)?Q*f~g~v-e{91guU=rIQj<O^j$VZl(%~PlL(qa);!M3{BTF=2i zz0#bnCv7^dC4(<2VMYbinmU&il?OKLq{uL)mMMivODztnbfZVlP-U0Eq)f6XP(|!3 z89?M?>~5CRc&SQa>T=_eZM0Km+L|S>c9?u7UOtcIJZRre!(G1&g(!#QU>Qc>hpY1J z6P=}iu3H7kr2Z;e*{haTeS$%l^iP&|m%+#~geG}gawO<x5mtI3+x8<k$V35QX8u&Q za3=3sq;=sDj_ZrR_nLtQN*7)1*i*Jih=8OE4-|;qoj@(w0oVE(CK=G25Op1jF+qph zjkS-lan&pX)P$2Wv>wu0hjmOGRM#huOj6wXw<3>%jonB|7fyI{Fs!p<%=rNtcR2$& zh__o?riQ<m8$`l%4l))t6SZoARg)6+t;Q(e1BcrdESQ{H{15_a9(U>4+jg={P5@Zc zbbvZBy)AD-o~pE@|I*^474`Pg42S(pGB1b-S}eX_!Y9WX%-__%;?$|9wRm49L<P3G z+-I{t(kf?G38W8S12TKJ{#ZO`X8N-pCFNnn*648?n^PwgRZ+rit3X;_5c=(*_SL~H zwe)OHIbk~Lc`+5UIJ2W2rJ$c!>Hub$sSxC{N8vFvhO)P=*v`Ut={>HU^xxRy&?c>~ zW%|ure;-^f`zLULg6mPa(OEgNcz-9Opg-gghHp$UuBSI&AGPNk6JFF&Hi>igBOO8I z>Qjj3IYH4(WnKZQv1EFFu*0hIq?9PMJ&AtDE6xRBXTZ!z5D@2;Zs<u+u?84vpfQ9@ zsgj1F%+IA9z!=@4GJEdw4Uu3DS#{<V4)<Q+Uj5no?D%bWJkzQF`OWbc6@Ac4F^cr7 z*Hwore^Hz`_OFv_e+ySc>XwmUzDsMR_>CL?#+a&K=2KU%T?zA?XX3<I%NAILY{h+? zpo-31Gz8BsDebXQ=^%b&u{2<l@rOHUtecFx)FisdgyxNq$?JI_7jE{(qQLskK7vq5 zv%$n$-#;+FbRQq?c`DPvD5BhZAvCsi&D2>n!EuM@y2=VTNf;iA&=FrQ6}YX~J8D>0 z)ouK&g9SAst0VUM(7W~*NbXeEVWy9MJhDIhOj_1j4`-_kL94trD1ZR)ZsnQQ72pG} zBHP&<e{2?1pklHbk366va)r+)Dc-6mCb*a62MATE=~!;4{=v_Vb__<3z4Qa-qNE4A z2QSJmmAlG5l%cnGZiPFt{>r{F;(n(6U973L?VMgxU`jbwSx1a-1L22Tn^GN%2`WDS znX!43Nu8~ti(zdbBbQ5)Dx+YS(d!AIgHuwc?eY@%U|vM^<82)3614dOm6rkqLjwW= zf&#KG`qXHeg@+mh1^Yjkl7S4x@ek#qL3JSiUzrS90Avmb5Ktuq5D;}r4Ji~!O2-fm zOj9~KR2j&BvP}|LP_$70pgR#1<v(3b1*A}xpa}n`0#ba6s^r@qK5$AEDHM5%5G7Ri zKPwPaP(uG2vT{OY0sn(bTu>G?|DcEo)G6#g$l?IShxrdWhd~i`{0Bo)MWLHgMPWkt z{xhUy42qpF{%xr!1rP>>+aw+YlRy5i&6NUdviLt3_6Jt8{$KNBS-7TSS$KN-e>FJX z@Bst=pr9gx=D+@=<R~FH{VT-!jbKFgA58J}Lty_`NDzde{J);N1|wMiZzPYy5Z?aP zAB7`O!Tp1%DF{Mb|DgH|g4X{;TjmiI{wqw83&OxlVMsv$PhnU<K>H^swt(<f3jKe> z_!PG%zLNsyhe!voU1NmnI8#NM9Ds&%?r~xM+c#}Yh}8&g?Xf&kWX+{aDwS36bD}Go zcEx-VSi6bLj_Ty(hBrRTZ>7l^LGr5~kFVXm!6@&n!9Iu1uyO|9&ZDH%Rfq<5Zv{l} z(e2;?`iP?Yw8uJ{*Z40ozIurE)G{yjnZ!0-!5CcdRt7p?pji{mhhN>{O9sm@@8N~B z)KkBHSJT1#_ThE!b>kq#z}vAw<7?UL^^-2`2=1NI#8zPx=4^vZ|H6<Nd%eUb@{za< zOG4_Vh-C<iXBN%bwCN+lEm*3C?V3|hb<pfC@%o?@%d#YhSk@V05xbW@V8Fmtpspc* zg-oLNO8^wm+M2&ASSls3(A@(iMuf)!Z``h1>)T1;-=1t{ts@FdUcc%a?S0UtAg0|( zh@6&%7C1LNCS3JX$bFtCJ6pX30`V(nBX$iJ-H}}$AAjX5mamovoevuCVZgr_P-<rt zi^(nl|B-Pzn%_!;mQ0)Ivkl)V7DA3^Bax{mZ+QTae^w2F`1Nz}i;)e!k7W@@?$VPv zd5iy#lh37lMxIl5ru%KOsjp%V{CvZuhf^MkV)Gs&<zh@>0eAU=NXX!3Ui+~~M?!zv z{PJ^T97n~u`AKoZife~%NFQ<IUY<c0(Eq<gxw$jvDuIE3IKhB`h`*=zHy;q<GVp)P zlYCq3$^Cn2>R<o?;iODQAYwML1tEq({I?#+@Oq5daDjk=eNvG95OGpgXK`SgZX*x{ zaN+-NLIK`Kw3xotUZ&U!W1}V;Ai*?ow;+oCZ!!n9A`*iCn}a1?h)(eTAkhTkC)j_= z$Ni7CnV^AyE+|tj^^x$J#Agvjp#KTQts!Rp>t1L%M<**J5K!m$enXmaqmP8$q`rYD z4f#*5bRUri@gF?7LyRZ?52gUHk^Z+xI<S$f!2Z?K5g_G}{Aaf#j?4!N2MB0IEk&Rh z5wGb*00|rCpAd#7(kR3~xUY{?i2M&mIU)%{{Wn<Ke#tQl1VBIt9Y8>o|MeyXX$1$l z3EB&3i{PKIM<!B||35fSg1r4dZOzokFaH~s1{!2%$p5s-wWC8CfB!aKG9VzrlpPXe zye3r^<OeW-OixYKn~gR)4dmavh%-H>(zfQbFNA?sBgH9e%N;rVYWU=9tnUM!BxD13 z%<gEM6>-v9yK%G0Q&Q}ZGIrgj<8Z_Hb9`+X{g2$u$qVX6Lpzp-UudYG(@gE)gzm2) zOlD-)c#jEw6iSOppawB?y}&myvEY(z77N7fJp{IZR|>w=g6J7w_x<Hk@gMigWoHEU z&-r($=xI6Tt^^1GBLcm>n;=D190DIB41TxKU#bWgUPch^g>JKPzC8rogsC3$dFOxN zF}#fsuGw=A-IqqUr^V=_LL81&2-b9RTtsNJrf1!)v?j{ZUI}ugm>|lGxl~q@8abCU zC&MKG)y8wo#Fi_8Hn}u}t{PX=@`iA1Qcggnte5(AzwzC`v+`Kz9Lug~l9~`T6IWAa zCQs@!<+r(8QEDd*<-#l?>e7(8;;Ou+BhE(#v9l($a7<@gfaW)izG~4{bFJUo4NoSg zJd-<Cz$wgG+E$00$W^)1cw2%7%~@DYZCX(Pj*ljC8c_+W(xb1LRT&j~D+!9$m5uu; zjslr=W9~WLj^#{R>F(}gvq>pWWS+M$aMOpkK1kKy)Mw0eWd2(w5R?TLZqHYqU4E`@ zmZ*OmlM6|O>c%@2hn|YC`Z5=ie(IjldK;gGFm8<e*!v4pYZuHQ|HOAJuv>}Ay7v4E zNVQy9^aW4*AoeU7t;B@UTzF7eiqIro^$LjOjJ?Sl+ZyByZBKR%RZp8d1mB0*Y-V_r zGGj4_XntXUIixe!FQvOE7JMr#&lI^ca8u?Y3~>7>7QyVCpjeHv-jhRIY<aB8ytihX zwF&GC_oO=bi{nbXu`07>_J#Q0w(-3a{O{BL-iDgo_`PSqV14f!|IN^pS1x22fTryl z8=B8jHBmiC9k8l~PSm7YO1;eABGy<QD=3h#<Y=oZl+w6*{I2t+n2<yx8q5%lnu>H? zD}|#+?;{gxxV~_c?CLy5xPYA4JQ4}TiU$p$cqEktAqyc+%s&<s%`pCl(j_HD9t%2U znGUNSsw|yWtkr<@E-Bfmt;|mcz|D!UA%7mL<<*o_87y%*3&p}(wp`RhUNsAR=!+V! zlWMI<arkBc5lyM$J_=Lhr8+LR@vq>m3+;Fo=roLYKaxY=V{{}b5M<W>$JIXuSJr(0 z|8Q*E*2K1L+qUf!+cqb*ZQBz~?1`;Oa_9TG=6>-1Jvdce)!n=I>D9IS_1<gs+LFOb z!wxr=<wgD0y_jY{S|?%XS82<5L2;9NvU_XJQ6%Xb-N1j}if&go*RHU}xvU9By2Ar( zNiocdqTpeqj_6C7bsFG@`Bq-|X!eqvJHdFP6+&;-&)MiMb7^qbbfZdss+kFY7=i?? zq1>FW6RZy<Tb5Kgr%@!vz9soH-K@eyP|?;e506=aAIfbZnRzN_RG$*mDUou?P{swo zOPUD@RIN+8XZ542<NRhSNe%Z`@J|D<zc#c)3;0)(E7y2DQHO&7Pm+~oQH&l-3Teem zoMS6#ckWv*m!Zf`-2V_-s1<Y<Wf_meNF!opBSxYlO)3S95@wSbKWQB|`nS|Jupel^ zi`-U0IA*n~MG&6E7_urEiw2&&q!vgE&xjSXS!dA(b#36802SYqpE&YT9P$7LM+jY| zk5^A9DJd10iZdV(%{zA#lzytK;v`}4$Rzy1!jV>7_98hfBGP%$CplS*NtyQWHe1Gw z!Hq7}YT&7!d(c&_PpIGWz|$AAHly}4zW){Ky=yo%nM51omluo+l$)z9JaF~IC|Yx% z=EBly$XBFL<yL`xJX>7Ov#te@&LHVOD+=*79<|?|@At&qIqXLsr4w2)rw6rGHkRvY z^_L^LBnr;T$~<0!DJukZHylRYceUgvq$4R!`i1<89BzAf8kbV4Tt&B3rL-1JiRUp1 zMe{@M1a~F-B%9I~OoOwR+<XKt47h~&`uM3ZeH@$c)suTW$>$~1=8Yb3uoNun>QZ>- zo!oKiG4N6zZRraLa`mwHZ};n+Q}W(9z0Z4xWSL)4>}w;v%S`Y3#iedwB&~^hP{{1o z0vrdm<ab-vT7YhuQ)uN)x9Vt$qHO|=BU9eEKv6BpZ9<RyEf)B>pI8Q9TOaWL%D<eR zYP925kXSiX@J5zVy0HRy3@}gmrR>W85d9)BP#E1v=9Z_PQQyU-o<!IzEh{h5w+!UA z5b-F51Vv0g_M^_*%2GhR&DTu9r8bFv{pz8O@6Yl6d8f0Ph@S)SR&dXKzOEBro$c*f zc)aE4!RvZ;_2tR3UhED2J=yzW-z;2mqZcFYyjJ^4mB!FwHNOG~;5#_A+J>RpkyusV zjt^~8|Etvh{cTRy!9vB>ZZy{C@Wt_WVXI+CAuCr1;R>xVhB4p7=6!#`<JbovyqZZt zEZnr<8}Pl54<g+tcwDglA8>Ge%wHbNbEh@c7B^JfWbP@18?XhvmP+sLLCH@Spp?8n zVRJ?Wp`2&fs(jOBBuGEn@<3~W4W6=sVVhpQ(<<)fT^A}q8%!+(Lxy~Y@i78?CkUV* zgc8V_X_LXe@H#olp6Hx7q3whJUo=YJ6F}zs-*zlMtt{sC%_AFBKtM$QZO1Z#$WhS$ zgNA(}2{g!W)cFEV@1sE`1h|_yyI48c(>r>(B`nAcF(QrbT-R~7hEoss+9DR$mnpeX zxmZ$4{wO@JWy_)<j~x8;X=*j*VN>Y1_IUQ2kZV9oswWYn@Nn(_K&I=6AGM;pOr^8V z*cYnmH@eNLiqbUTO2)jVLi0~*5T>DRb{3ZbnsxcXHAY^FI_oq23JBRNcz{mgZb*k% zjgw~%I5fk(U$N`%)N!!yK5(O_3#4)aBM}5h7u&dwiI>BOZCM^La=reA^q7<olF2_9 zD|;LYqwKo$yD-`4Xtt9Kb%v{9!5(6SV<GwqI-UE`11KH@e&~ykS7BW1@0h<P!oeMY z${#As|4wuBi4yF^NeqiQ;P9SbD}VbqlCu}Hw~x~M{~%|ZrVKI+%zq$cq=`%i{=Yf4 z&mBhk^S!+oeNQD(`hqDkR(kgq4s80M9`eI~Z9DamXaC8<7$aoX{{)~JoAc4ZH>6S^ zr|+~O<EG~`qkQ{rO_6W@;nlGXa{qtu*H)Q`O#QF_S~7C;zhXifa`-<G#LY(T_*Y!Z zM~3>(yq6Xp!EfIKpkb%q*nAJ**o6Y#hERbF`42R)>yg|46<6Aj-(~-|CN?9R+Z^D5 zfEXy#9o3L=+g`y?0{&619}3DV;Xfc`MnV18HIWO&>>m-o@uNiiYa1quVj}Qg+skN_ z1{%cwL+c=WiXu}`AfPu$p!7sp6rBINBdE5yHWbf)>R6@=<)8j*vl>7N`UjWSgD8&w zX~lGtITXSFu39#SV*0NhaRJ5d->_L1D24xVD0c=uKiIcHn!bOe>4}dh*y$1{IPj=u z@j^D=Ya;ae6`h37HoZJg<upC?0p*`84S7Tv2l<~-HRHUeYrcVc)#m?<`rRKZ{o>@? zr~xl1SODHQi!ruLGEAN>w{+==1Sl;$m7i%pFD#E6`##<RDTOB8lh#*b`N>eASp&rT zx_}WOf`g?$%&PvB%9~Pi#8&<Z?rRl*ggmLL#yDr*I+muLEK1Z;iZ(A1RhcN-PEjM3 z_3)&{y;T=M-_#x`R{m+7iT+0rqe4@}AtWFHrUhWx*jSz%3_N{Aq=Q=BD9_%gm6`9> z(XJeegea=?DJDPs{PlTQ;kA0fuH|IqzcpSNkpDO^&`7;%oNSl0-a@_DDBY@-XL0X< z^5|5^&RRO38+YHRewN)htx#E6J-VN9;+9vV)?s(L=ePUe=>HkU4XqWO`Qb*0R5sEw z-2`}mBv%<R45EAnX5&S=@zSCv<Nr*aZi`Bnngb*e?o^g;c*W~iWSev`ZQpaKt*5{S z{|sL6qX703xWbeFJ@*de1#9#CYzn6k@D!!Jg}c1IF2jL)Urg8!VQE4K>jj-{qz)GE zzb|3ZFz`5C{|?vrFtpCDWe;?kx(oNP$^z)nEAMjK9<Nkyi%(T;Z$kjw9Hb$*<<7~z zi<Ov)6z{%c-}St$S^knfY}Lw4YwD<O8i6C6`6Eustd(N3YrF)J6(Cq1z%vFSxCmoA zbVse??qZlAO`nzN&ucTeKDP*TNqQrn1dpAW4<3WYOH%3k{A6}>W80u?gU#D0hX=sE zcZe692~KE%E77`=5-hF!UF=wD@qsemqQ|;9S$K?<;0XgC#ck7Q^GKq@+DUh%4@=G< z-z(t#eCO-#%ldw`RfWl*VD1n%3)s!X!uv|HW2eq7Y_55oDY5n>h3D!TtJF;SVN2Af zjSS&dCs)EQDBpy$P-4FBtWK3$nF84PMICOJft-DV8UTw^n3T?mR&)Tw$X2M)0~Q6Q zX?72`5_oX9Iwp{}AZ}^TYY2l}_K-Kv$3@i7X!zUrMN&QeQIpr()h&2OoEQ49Dsz>Z zB;?S5f|edA3=mc=CSy=Y7nqcAf>_ViZv6z1x@!Vmi?{Ub*Za)G#Y=ed0U<!a;A26@ z+~#Ws2*a#IxrProWPOOE&Y<t_6*(Mn>Ap`UXyl8-to+vrm_Dzcaegl!7r@8c83HjM zjLzbuOdz-0-vfa`KeZs9uN&7)=Y&p9#=+=bRevyUh#)v-eh{eNRVH%JQlSovaNtUP z-I-&m#k?{c<5q8&>!Q@0z#OpLarSZ?_+sCd=oXvJ(8eY8X~O4`#T7$quU?7d;677H z>q^O}s0!}lpt!p_B7$VF3Fp0IO7t*)B}Ci6mvSKyKgztAH<HNSL0bzI<j;v5jDZf9 zVS=T1<BgCd@wX0W59o`;bIUN%q7tZ}QkW?0rZro)C%o0<;-;`FzYOr((XniG_d}A` zpB_R49&QAFXb~|kY76qc`2Nze|Gl2tsHdK~qZar_pMN9iT*hU0csZ7GG-*F}Hw%nO zIbpHvZjD7CsNB9kbRVd77cf8J8F5u<7gI0S{N@)3hd=yNB@jlhk}-K*s^C(Ke(EN# zSh0G_W@T_WD-ffn%sfE(`~s7ly%OXV2uokE4CDqez%&)c#<R_1pb#vi);7!fv<ELb zkR{s!CpehNFdnl#>2W$7UD<?J8pH$+Qoky2l%~cNf+qJ&tyPfA{j$*S1}mzOSeo?r zUOZpZQq1&EIM$f#U`{N&{BAL?CxdJGoxE<W`f_b+_kJNsJZ?Y*{<GiX+Y0ovir4&o z_r}}iMw=$&Ch6KU@GO77;g+T2fq4Ok<2|PJxC&9)!vlP?5T4Zu<-{M>?}IXRYOL|0 zWO;Q~D8GM8EdsIm=~*x;c6qGL?xtw38<;cjP8?i&*E2gj>boSMQx%9e!*)>r^&n-+ zjk0(*kH6P6@7M=ub>v;=`<`CAmAG%^=J~G$w?gh)Hc-(t`$u#nzD|L!!4u(|BA`H+ z35(^~_b9=V)#taliF5`*S_P7tCMNrQJ-SJ$!kYIhJ1{O3IbCcUVtQ^9|GkkT4j!dK zY+(93rXXO&1`c7h7tjA>0bgmKSls)8K)XVU0L@4-&?f+ZG~IPE<V9thmm7Z_Q~yb6 zp_Rziw2m}AS|agks8fiiaPgja4oczlhYZCX4;NU*w)D_jItYX+j_%<{(y3<hUo^3S z40bUA2_1A<p0|;r2{|xxABck$J0H1}`+OlrO3P=^Bx{?wY)nXu;Sk4@6#gP};f!qE z=0g8X@0-5>;c0Xf6539w4I-}wR3_R9xujRgzg{H)2s9+}-ZG{YW2K(*m_)H<K#Pci zr(P4hj-*Xtv<8jgs%|wBK+|BXxs&39A%!I<>FeJilwH`xJ2WFGU=+4U-22AL6-S50 z<t^FR{&@XC6J^IUlD<`aD*?z!q}w?C5K8vYtOy7Ik(mt|w$jwL`Q#Wz2vo=o^+v2< zDJXa2R?SFoWyev^Drv@T2Uc$(wB}(3e>``BpiwU`anUq7mU{10{SK$-V&Y#^!pB5L z#!|o;Y5Olw7mFk_=$~M~%lcTGLEDY}WTZlmSmW>9plYF|(E_AktYh^AG1ZF^6s#Ix zjESy*N0&r%)AVs`3Dn=V&TspPI{Pk4Q!CzT?1ud(M9l#tDsdB<2nL2JwR9*zqfGV+ z@MLBr3EDswu>A$vs}uNR3(l}ZZrW7veAw(TAy{0X@kU+sEr~|Mu%jA$jKCnn%*t;i z)T-q7W8E#M`n2R&A!}o!(l+3|<l^i=W@u&rJx5057Se$sLJLlDx?{9odu$2H+HMhQ z9Ev1VAyYQp6L&d1H{P(5HsL(8_PSAyxC$7#w<*JIZ?KLyLqLrx!*90rse(H#`Yna% z%P{&8>EEA9(wi$Z>0m9tE9zek)nE|vAV+R)`KuW$HIuL_(YjsB7qM+f7CCPnE;slH zFnk6gc*rwyjVbTI&GDy85lw5Z&rLJ30HSqm*$!BSY!rSB@)VyOt2G;dt4}EyRk__? z!H$@_`+nBcOeon|w0S2>*o=!rBfFR=QPG*j-Ukdq9#T(eGS<>Hv#k|U39;Tc07>^_ zv-3}_CF*HYvu|)}kGouEX^_aL8*A8ufseWmr~08103Zoj<pO?ow-@ZLhZb^(LUZ3? zV3c6bmx84tsZ8=hUdZPhA85V0nzzPlqYy%zK?S|kLNJ&yGg-gC-k+EkL40lFvJO=% zl&y>T<o*_XgO}|jy9JMvNLh0DTkF<H-zLW5+ismTb9i$@d^(qyD9J6zBBIfdH)ELc zEHCIy1c1zQgS62zPybBT$~HJi&-&QM3=#Qw5eX;8#4#`AIf~`98*A2<dwlW9dWEtL z9n`M3oLr*nX1Cw;-tF`hTp+g^fd2r~HAHdUCAztp$cVvY7nW#MxUhN<lB7C?0of`` zQVLiP@mLvAzJQfi%s_+a>07C8%_W_j4+Q&C0%#!i=#{p7uCF9{IsUnnwSi$}VQ^%$ zjTb-|#bVJ=!0%2}NKLN<)@W90h%W5OHb_jRVE;RK1?G>RY{En06xgTKbHfey>~{xQ zITSdL8fdWXi8$Oae7Y~4gbq2WQF>^?59fYCjQi`8v!Drck>i!%H1|naEyX95B>mhA z2GIJ{N~9axf_2<}OrfJ22+sB#d%~Y|R>Jl^x(oi7T9y^X+i?Oq(|AZJd)ef#avwXm z_$Hx?tmw}J1Z7s&1qwdcS<>WBCa=x`MY@*;P2_ljSAFJK^o)iwMqLUQpdcM{TT%v` zbFTQDSjZXmjR>+zhvxnM9MBcn4)ErCBS6}ZX_!=t(O(hCyo4MXPv%wVw#qO;tD9UE zI(ACj_XgR)d5tJLS1V?748{)6Q?WvbYpeC(e2yE2FL7kZdqqG^HOkbi)BV4aRt<?l z;SO{Nx`$Amf~4S_O58cjz{QQ-I>c0RVMvs7l*~DDC_;JYF4TQO_E<RSL2!obk)g94 z_aiy6Y>1xH$v{y#0TjqUY!S+V^@w;&PB~{sNOow4%v}(J#8_V-7t-e$nqU57{*XpO zPy2-|ruAJc8ujYy>RCT5*CJ>rcwvDs$^5r$;67!m8MfEQPpV~6!&Z)gGi9|?tWJ6C zU-SH5jZ8t;4+~J{!_-tvaGW>`c%KSNP!r*(P-M0djI3}@0EgjVZmv6-16`cJ1V97v z>800M>&x;?R~2QvYzF%jaj!nh?#n~tK?rUHEtl%oPF)ELL(lIqE>|Tqgu=4t;rV?m zM#u}fZRA*fIDg+xhW$6ORwM360y8l8B+uv9jaAi5_h0cQ5y4;bIj<=<iRTUpKsKVI zn!z4-rt*wm0l$s-ohF6)8IMSc=kw_F`$r_4aReD@`QT4)t}ds_Gb2;KXu!vvR`8u1 z^K9Cf(h?^&e*9t*1vSq7&M-rhfpw+S$S{Q8>`$am>Skn|$p}t!SE_X&N^nPon1}{n z3#9g=m9cP-oh0drs9lHy`*<t}t)s24-p4AEo^(bG1JGqICRJXoS&q@H-VT2-(=Ma4 z5BftkG=N-EMFP26_85zCucRuC!b@=NNb%e)vxHOWP!0Eb`iIhrni*Quv0=mbLk3ii zD9U+Y`>r`A!GsYxJY8S~A|!oLUh@KD=TxnPoVFL>kqMNTkriL!*7jy7GiKEyr6(da z^pzHJ44|Gz7f9i_u~{wj9O_9&Mkz;X!n6j;co4*?eJ}dg`uMe?Ywq;dqjT;M-IQ7B zLMlQj1aOu_tV!+`I0y|vXTflfPy5PB%EQb&Mci}D+=vl@z9c~lG+N?Ox`N_X2u;~# ze?~TI8sceaYZZyDhzhdN1|R&Un@O`vgwCFXBw!-VPO?<Bd<H?NDvoU@keI~0;HOr( z6Ijr-NkGO+u$EiBAeo(T%!N+1)kEqkjNv0r*Dqs;OWmlJe#Wmza5CL3#^xi87lj_^ zncC}mx=9O@`?Vq{i<=CIIfj^ZVT|-gX1?SET<h%1-tA_l#URfk2jgltHSgbDlS7;% zIRGB}l-Xh%v|-Za?0KB(jU&p|@TY`g_|s-L+KdW1%Uc&P#Ci~h{HYQ2+0@C8`Wx)~ zBt)e$WiU^jIZqCx0}dKZI0{&yfmO4_;mvcm+exBZwt9*_yi?6?fyDg7RFqL35!_p+ z<ESH^FsC?O38RmCwrcz7mb8I-i%R5Dc0jKQ+PyT-U9Q^0&{K{$Q;zhuB1FTsYd5Lv zpViV9e;i9Jg~a|V%0reJ4?~BR;h4}N@b*r-B<kPEdxo2|gkMJWb81%*yTzAO;J?&3 z2Jya#Mjsnj^e?K;1tUZ<{jZ{l067BYc<gRRnr<EHqcE3ij4Ie-O>qW3X>>jhiGV5V ztX6V!)ehK23@O@d2P4QFyrZ1<>q%vN?Qp}Eh66&OY92*e?qePrn7{M6*<w7BkuA_f zmALr6u!jp71j@Gw^z}-keS4P8P4gw{966xW>A%j<$STS!^Wwm=sBO@o+|ltiTKm@w z7N8mLEd5r6&!G{zvqn0b1i@G}$-hNT>f{7@sW8xT1U*V5vA-%6qa^$!R$kzCxmt1r zsUnFC2ZQLETq>JyQH42*PMRiMWtFH7Y>1O}Lz=3+NCngx9EAoan^Tit$Rh^k=zY5& zox4*Y!sN&ZRxddE_{~PU7Ngb%$o=N3+2EUY>tE2@J&n5OIN6GGJ`o$X7{CxHvbTt^ zkA@JQyz5TH>@}fzr1a?N&C&q|vT{R~yPkNBk(`|?EMIj&b=`Dreu!Rqfc>O3Bv>OA zL-Yma(JC{DfE;@lG=(xHX5c%B<d0a$%Xpd86?<Dm3}scH+mI4qhHZKW+Xab|eu#`| z2eJgzI!IU-V_!r#oQl(hc0gsz;2mCNEa`kJ!G=(+3}6B4LqpU0PlJ-w2)|ldmM@wt z2sW@O;~xN-vxyv2@%(DT)52aLc*&5xWL;SJAznZ*ZRa#b3X0~$zB1Q>lF6B6rS)1B zEAmcyY8fS2+OQH7Xc=%<!~W736wV>UJah!?ZxOo?U~I~(o6*plEdZ+fgON_8v2oVe zk7CR;7;a%OxHxGfje1gSg!_9@<Ly&svLe(pa7HFZPT58g256+-q*bksnzyM?P%$5Z zNo%n^XcYoHG&8LVhXLhOTT0d7!ai->#P~{bx)#R;R3f+lKk<6TQwPv+0~A^~kvpN? zdLB(It+fUZ|GGu(N&r*z9EhPYWHcw2AbA*bhcuaF7aOgEY0y$No2{z?tSGNB(und_ z(D;ZcUbVFAU_{o(4`O)GA8}AF#AHd>1zj#`y25o9MlWm6X<nl6QF$R;s3}Q#=Y=jF zLfNDo`YtNmc2Mei!eG7Am03Rp<O2|xJwb>c)EfBXL3U-*H31S`Bs3VWX%~)ujKXUw zh`M?aC+tr3Kv~G?Y!#3?TX6k7$sxaT>Q(;ya4|=%ekL+3JL8B;I?tnwLkP7{+b5XG z>TX5yk(K^(6{!ofl4lyPeGtyLW5;0GNA6fG2MrHnxLb|Qw=Z;iJvaDKu}EWbz1MzD z1hKlfY8SS{kOb(fM1v={h_3HrJVIf5#V;x65}Buz6nN$xSVdm!w$KrAeNJ1w#FCj` zQ6=Mq1TRr3E8h(Yv2t|X7WlCCTGQ%&B5Qknz+YsJJmujkak^20O+F39pwIo0u#Lu( zzv;f3RN%B4NA_}Zv(w|prK(;e_xOcSP|<!mzNr$|^A5-gDx?Q0I@~fudtNz7xa`5z zv+~EUY`Z*D)ibzhbvqIDK=QbOc)`ONvc=T!M$Sk<FZ&6*$~=}lB3QY#_Xjwiw1C=! zagv3S+R1#vZsCtRxTS8lAi64khG0@7I(%mhCi_AnLxxEA_Zx(_a`z4Tf*6Y}H~Zwp zvbU7>AFlw3>wO6HxatGESiK6B_P(Rvo1?%8D`h(^+|TQMA`X3=UiVLjFQ<PGM?|}e zRVuu<Y#yov`}KYsvvFqU{qY(wW2wZ5Z>IfVQRXhQ34)$0n@e)YBcJ1K@kXQxlH_4W zqb+~Og#$!W!|8AWFRhZ>`UW&~FWq*KMt6_7HGu+Ju(dPAQSDobrM6wG-?hmdJQNv^ zE33i!LYnbZo(ja6F+JCyS|Jj$sgWvSnE?apnv>^Ae;iEq7Q>K}^+@8@MV>i^NqZ17 z@H~}sD77roQ=OVgqR_O{eo4Cl7gfei?Cp`uTJwx`MI&%L-pwlx?U`qvgn!-UE94_R zun_>v<<v-7ZX!f1ixpRT=1&8`zcQJwFiW!>ayR&w@A_=oYp=fMaS$3T8K?*5FwZIZ zJFg8QQ2NK>+hTv{Uj-(g=DgQP&&mV(qW<t?ypvQZVI+?xjKs|q1&uFJ3gayw*r_RH z_}LnVF3)gYm$qZI;p~lro1X?P8jLA#Xcz&Y)^IS(|1PEQ)q2Ii6HQMJ;iYaoYd}-x zv|186?fo$To!X!7@+rJ5!VN=oeD#*cq+rka&4w(eY;WUbX6c_v>|Ntr4<yfp(x}ds zXCc~q2NE(<N!^x?g<V=asx6DeiRATjS@9PKJw^3L@h}5USHrN3^;(GY1f4CoX?Z2! z#sLfSW%AD_M|<Ny7Pti6(VTIic>Hf)G1>y#;?suIbT?a=T|QsEkWcKmk{*+admIYC znWVCUKiI*N7Uj0GDN_+Ly-wjmpPQ;S`WynxR2HhZ0xT{^xO23B{lPh-%O(fA&gskj zNZ;l=qn@^`?Yy&*g1{7>nZ1)ss>*o*Na5kZ3n8_cMb6T^cL9GFYu%js(}RZ$DmKu9 znX~tXhQf;#g4NagaF0rRskDvJ(hXBOcwPsyTQ_j}&F_>lCRkg{;OLBgIaLwNlTdsr zv1i}Gwkl4qfa<HpsMPMiY2AgWffA^-B^Vm1268_mw9K`cavWMucqE^)rrMVRu1^Pa zW06tlz3+~Yhrc));Y^(B!_B|AdGTGgA%Vxr{A^3YE7nWSeZ;r2GUY#XjA0RVJj>eN zj8_Xq$(QEkv(*E?(0OnN5e8f2xf)4M=)zWXS8@=U_%F5p*p|qhI-!HBV|XIiY8eG( z!oa}za0RP(>~`L$fj0`Gf-bcJ`k_Sm&FwR%?g;w)AO9S_{QlvhJn=Q$e45KH;`H-5 z>Zn1dFb?bDKG1212YN!y$IBw~+GCp<>Acf|=6Eu=n=rxe?FFhJMePZeE5KJy*Hhz) zF;9@zo^5`ouIufivudvWO1j-A@}g#`Tf`2}H#fRIi2Sdg^7CcrhloK4z^%<%w=8+X zieM+dkB$&;%=%4v=nX(eUi9aaDin2x@vEaEXP)v|;r*7YuU`v#_Pr&>`OkINMq)BP z!VzvG9sh--mlf00*LP}&=}S{<x}dH`GmMqJgQql4U{MS0>ednT&@jbKopV3^QrG6l z$wng?h%wqvg1(=PalnmP04p1sc#*lmU$EILr#w7Exv}L*6eB6)8aw^VtRdyO{s?q` zt8QX#b07>$UldOJ#iWLN6Mot(cvuQ2m*y_iy-k&gb{E=|4>M-WjJJ~gd?3U#dWBeo zH${uKP$uXcoR)O=@rQ>4p%2-@9kSh_vGEv}_?EFpJbX@qcWE{C0MdDNtd(X7PNN^q zz1|?$t-s4jM)@#-ai9}!=HgIK)%aU57vBkg-c=~vIib1;c8Xm%i7@h(!>Dd5EcnQZ z1&9vf7qJ=|@c7~0$Zq2L&9oPMwyGb?S3MmmI!*NyK?-Oq%07Pel-04Rzz+$$8zJDw zO15jLrF(izP@1z@0$LR&&jW+{L>CLWg{3(t3An#BWyT^aAou@1Mz5kWDse$a5r9SX zIwVm}BtsvC5b(YaEtM(ty}ab&a(#;Y@R_p?L*1%-eWK%hDW>cRAHt&-0FYf@H$TK3 zWcdzo9$y=~I^bQcbO-T!TBHJ|O56Rjczs4aB!VZ@$@yGZ0lN<QMu@;WfxbEb(el-& zSG=`t{)fTG=fdD<x~Q>j12UYgS#yI%E+YN2ePZ$|UY11ojmEik%I{n$*A-3=V}VbM zVRkQE2O)rn3Xhb675vdNZey8*BFzAe4l_hU?XI*r{tv?cZy21*Cp(DJx0jCz8z}uQ z2^}R}kPH>74F(0(=${|a0SmPr_J6*;@GL@n+;4wa2XJ~~J}Ped%qb33n<5!1`9DXR z7dfiyKTq5(73v4-{~Tfce1_bC;6On2NI*a&|8s=3Ve+A7{O9@%3U6l%`;MhKLI487 z`>%3M7&ZOBSh$)%2`r26;kH2jH(XnW1gaPC|84|)s5}6f5(wxyDt&$&6)hc!4;8YF zTMCto@_$2S5UPWBe0L=NzVd&*x3+Lc)Y<=n=PDO$3fzdlgUdYr|CQ;C=id>0k<O@! zSO7JB=c7YA7?{C3ehCv=a~>b2@kN+mmh^S_y0~blWf*lW)Z}3K$_Hy^!l>t;-b?HC zVdK5n71u}Ez-}U(H@^bd9lF49Aj1pI@vFloc{Fda$7&VxO%5wiwGINgk?1FxuPq6- z!QC5XSA&KY+-a3+@dx&6S@+HFTM8Qz8v%w)#i~1smIBjrtyxj}K~ekSs_>K=(nKe2 z=KVT4jNm#k2*qaSY=#Uqx6T1(UOGhYkX#3eBf^D?WHHv#ST!__-GLAy`KG?UGAWBz zfm)p?2ZgffF@Ik=sa+0Ay{f>K8Pn4~1`6UyG6pxqK^$|5X&05FW<m{bQyyL<^#H8C zUt*EBubu;FW4V-32xnZ=I6zKR9e{bZh~_KJ(>75(yK=UlI{@)DbOD3dt|B0n{%!FV zKK@7|yCWpimBXhE5?cQtfq<cV=O%NlQ*p~n0<dTp&x_f48;)%p*^}Oe)i@6T&XkO2 z^fVKc(M%>znNt*NnMD4mJC{*7AE2$y%NAc@24{D`&HZTz<C*?B_6a_U{eja#uS`xC zgv<8qOrq7-hcNF$(_$B)E}SoipbSFJEc+dItb5mBN6)>?^hpOz!b!+L=Ttf`4U?|E zR~N0Q--v6hS&r(!)M@X9iD{!YK(yfj^x8k8QB&HOoj?~{1i@4XU-pgT3?S5ui$HPv zxz_r%$WheOKV7@_Oj2y?t^29T=X?5iMIjby_3KhhE~GUau_PN43OtdEv@ZNkbpz+8 zUfi6$!wI3Y+UMI%9dk#dH>I}NKtB9ku<kZX{?x^>OE<sb4*JWv8vGUF03VTFOpZ}_ zf=OdAw|vj?J>!nboovh|2H?FKe}+SGckBIL<9a*qLp;=>D9G><LApTt6N1^$dd-2= z(uzm}^S2b3blIj{pqv$m+A>6ax8m&Tsc@vnmx}9aAHl*4_wbkg?D-LiwAzDK2*^er z{_>R04E?V+909+V*xXMJ<S!Az%Xg?a{na|M*M-;H_&($h0P;d25#X8hUHK$S_UQfU zfI&e&KPUgM!1{zw!xUD#i2=nN$+*_(NF~vV-T^*-_)`ky)~eK4fk}3;sSFsW4oaQF zVD!<gGak&n@g+B=dC99?C!1>Z3GjikOD|2+jMCvU<qdZFjIhjaMf;UA&S1W$1UaE6 z7gPxkxBARkf9}m(d;lkM=L{WeZJW#4ucr-UqY6RHo2K~M%d)&qS_uPz^vAd?+U_aW z9Z&Qs$(%44j&@~cy<N6!F?fiBG&e<SQfmA}&w|fo#hFF3s54|Z*{f9Dc{NB$?HF_j zX!Nc3u`&0EmsY;c(%rd?5@~R)p#g)*FwSz?iwBZgwuyq|CcwPy29B7|++tL%O<e7- z;`)LQF{qBD)N8X=AW|M2Dw3aNn_|7kXYF{JXQCXUur#eTw(3rff|qq?3R&Qjedz|u zjbyVMvl;|G;M;eWSY}iqnnR6@QE}zOv3FcLDz-k&Rg6(E6YaGVU*DO$a&NQL=Y$k= zUd><BJ2V|$9RagbICY@2OYnM)F-ve|=yMy7Eq>k<l?a~5mp_wQTiypS=c_c?OiS<R zGtzk^^~=Ost(+@B;5o~SVDEYe@o%r72x+9m%iU@J_WUK1&nXRN6Mbsz$h)&ucz1qT z-kuHj;_;;IcXLenGDD62!yKa;-EXU?bBPs=<eQI+@B-im?Xzd-+}h1kqzx$NKn}7O zw+Ba$?k3Y&9n<!`LEoFDIF70mOx>j~fIHZ_Y41^^=xu2Fvl%fYSkpy+OG>;x#vbtb zea7BbCVZ8RDnOviTAilXE61O^q9~{%roC{n9$In?h_K5Os=dj$WT-EXu^BTpCTDzt zo^UdOV*o%#N;D)95c49<TD9QZoxTO4TQ)V%S$oJX1s3+k-s#TV>B8K;kv!bLbdRo= zIc!!~m4ppSI`>3B{bc?)C}2a>*={3sD<8W@)lXPbxizBl@vKk5(wT1}z3Td4(BO>t zD~jIWC>!wh3NNs*;-LHPtDc^%@ia#NRtw3xtqw4?iF`F>EegwVaxY%5DfAvA4m?5n z?Zt?+(RB5Go2svSe?9A&d8R88`6De;x7T9w+a%ccbl|W3O};ztL4WLZ8|gHg$SlV- zx95Ss#j+Ny%@(bUkYKrEl=EoE<CSFC4D`FN;EBgm^Jt%Wg^w2aLSOsspD!Q(EUWJ< zQUKVOiVqZL<d2^dSb)=?H^du+LT6o2Nw}V+EFTHdzA}q8yc-5BeGNt+gAkA4N(yA$ zdFO2vccQQ0o&mWsxf@x8(iv1V*HX`K2!GoL)BG#w<VfyK|K#NTJr7TAx^G&)UAPzt zZ(}gXfB4-cfnO!RW0G;}_x6}`dr(HXfCV@wNw6BhbGz2zhgxD3`6_bZ$YOCA(pR_j z(W49RtB*&&)pwAQb{iycy-&WjAMrlHJdu>sPBpH%j#Q!lwUo`tHdKp;iRA}9yO%?E z*}Q__W*Usvu(JLA{zC}*X(Cd}<9)A|{{Wm*>j@L=oua>l(>v9VpewPFr26<2QyI|V zfax&~!_IR(2o+|r^PrzA8-e-4|K3A4L9N8TV(jd=mK2@Sy9@alRyDBMSVtdv?Yvh7 z<QUo4uL{uc!}c6NqQ8oe7Gumu%U6NcfQ`L6Wh)rM)c910ABpHbD~g+9F!{aZDkIZA zJ)&o{hf3B9`GFFBLQ}yLzaFw^69NFCGmrl*eG`@4SoyjUSi%n5!TWRA8-E(?px0CF zu_T}Oq>fE<U*hVf;&FN7G<-WLQoe8S$p2db1e5!)Jv~bLDHvcKj<e|W<PYB!hFQX& zgbGBN$vH?MvLfz@DYAosL_gD<+8-hDjq(E1mgg6k9$*%t_@}-LziM3XIsn>OHG;p$ z)j7bH2)9Fq;yXe)U}1e}SGmnWej3or&5>2?Ncuxeui--do`HXG@JaR8-4H=dpg}-* zi2`NUD>q2$=MFH;xNpvpT|PKex-0*3Caae2obSBhrb;#<PWVd(<&j2Me4vT&u^VB4 z)^Y$Tj`FC!vp9nFB+S%}xgth?bS=-akzdq;$<#cd?70Q&Ghn5&Gq!YsM*Y6Y^m0#T zy)v($X2c&(G)Qd(j7OwlxuY+5`~M(i8)6FT`agQS$oj^)|3a?9^HF(zK>c4JR}XOX zefr)cDlZ_algHA{F?pW?#rPd2khQKC^W#HY=RKG1S$A+oNm{2W<@ZE(!k*|mq|ahz zA5yOz0$NR&7fj64))C}Pf^g_z*Tn_1tL<9weWZKGL`%4U?A|Lgn3-Wt0ZsJ;?;$?? zTwk}42Z?aj=t-ln`F%a3nZ_`wARACmg>)!vc%GUvaVP8oY*2tDs>6XBSqK%B47Nrr za9Zi#PldP@Rz7S0ou@bH4SBwjOIrWrQRVZ(rZ;O=hU+<qKchyJ_Odv3Jue()KluN@ zNZocjg-ZNim|r@^FI1lY{=V8TRHT1&Y_*P>@-O@icn_5W81?@qC6Cmjp!)kv7{)h@ z(*gmd7hd3CrGNZEg-N&DM<oQn_bAt<B&8RFNi?iRM$o;gD|-jNzvyVC9W*Z+*8kjE zUtdqeUBpi@JE$h5l}XU6n+Q^r)-ftkr;r-$k#3_?H>$g}dRmrgiT+Ww!!UJsb#ixg zcKb>ktm8Jh3yhvr&FtAMX}oC+(I<M{#O#TI|BGg|Ed#8bmm^KrOimQgtz&jGd`;8$ zoYc<#3z6)NPsN)493A?GW$@u=e4}_)fPGsGE%iGy!E`XR-#G0{IO7Yq{VWJgQun}& z!o|Kgv#44PBZU4!`!D+09yy%uS9)QI^D4nr0lkFch|X?4c%$<lh9|i6^zSD`FOYNE zdtCO1l>9_uNhYt4+*0)bHY_NxR!0^>yINk4^A3hZqy+Puv^ddM9(=0Tcuf{P19B-8 zv<PjNO8a=p=(=}>CD#^-dY@~OE=&yXpW-SOo}v9j^38$-<6ibRwPy(~>WaQo!PK`7 zp&yQ(pi?jSvo^MC>&j9(Y0*-Y!FI`VRhFsy$r8uDbGHk0BcXBt{xSW5w(FsQsOZ6D zwoE$#jsr9#SVzIXydEM#OC$53b)TH>V&Rin#+^pfX1N28k9&IaanG*=2&RKnC$e+P ze~E?z(etr^U599n#1eIhK$m1-H`a~Vn+oTFawt(ZtQp|1?-0eX!w%UwL!_dh&Vg|% z=q+qsavY|*ntcobTeF`YL7dnP5Jq$pHBC_2GnQjB6j<Sz+Q(K!qHL<dD~;x|$A%Qm z#g_P4c-_|sp8G6!5Ta82P0uxi$ey&5lj3&<M!z%`U{SUp*B$!OXumSJVt>|_#9IJo zfM#du6zAe1iSUftg>McY@;L2h@5b_#4Whr)8xljT>gt2jV2GhgLPT@E`Yv0r_#nAg z_+7lTzTaswM6!~90slYipZ*9H=HF81Jw;6e`JZS@c2RI|!~_EB)Jxy}gDL?q@J!=6 zAD0(DpBWyzosAhP76}wo5rqQL)D?B=`FyUayb?D6R+bNf%m#T=)>G4azLL|sTDNXF z+o(ORLZ5n4(`uuok7|tFtftM1hJB^~ai2rEpX;02(rrJ*d~J||UU$%1&A`rS?^E5l zFBJQFyY${xmEG6d<K^+*MraPWfYiU?mZ2A2qO#uTl-Uq($<t-@wCA5a{qnf`LcIIp z(1I_xc?jpOZQWm?>qn_``FxHcjNt&9j&}-m97lfna-H{@i+*ZdOL@#w|IzyaCv&`i z(m8eK8hWn7w-}%TZP5)Oc<@uru{R9>*p<r^xbuc!Vb$B7JeG<z>4yVUe#Ckc<KC2o zXoRTx9JPsoTsM}23p(tR%q$YHn?TRVY1!VvDHZktW8uenz!tN~L0~!c`O;2t<Y<iA z1iaS-fu?&W+SxkKRL}|8)&mofo~^F*<vsq)V2XT$-QtU??DU;h*5{yy*_lFQdFnmJ z)xRg%Gat(`aVb9Va2f&#jHhE_R^@L4x<788PU71*s;>2Sy5Hx%K5zK~df#r|4lWg3 zS|_I3&0wFHz1^Ig67S%3ph;Fm>!gB*^I-`zLGp>Ofp@m#)*suFN{?*<6MHlX;bgW* zUpf4u?|oYi#>{7xiRPJUv@ga#<3DO>U+`UDb4rrO&T=lXZioS!Gi#(%Fl?q^wv7%H zOANm>Wln>(%GL-0P}BUo>=~O~##S?Cep#|^byV_42{C#N`GIAPK_|5KntEw;GF~Im zXjF-}$KX}QMAEpCQh@@nI4-j|+Z#)Mql6%?+PDI<84c(#k@T<B>e1+uBam1~WJo%8 z2j*(&dQ%#99m0SJ3ir88ES)`z(n4m4<oUtL@5dDopCv|sgyH<3!UfRF(ho{qg;2e> zM^Q2gFnfyGW;?%TgI$%qVc6*Mm4{htieV6+v3Q5`&gu`Q?#FR9wS(wJOm!`qr5!Sq zGae&PYDs+;MbyyW@X5W!rt2doTX>$HA(_x;Av|Iw4Bx0{^JgSD?X+1|>LgUe-5N+y zkId^gYnWrUWRJ0(QDE@Yl+4CfHhp+%IYPDW7fgX<>1X?lU^i?3v)q&74H}2=bU<d^ z)QmX0*(C<qMC-JQWu+UGjZEuQ68@e0)}rNQS~g4Ip^PV}EX<wmQ#u_`F5?`;FZYP^ z=18<|5GnwQdR6uK#)`B_&)%yzskGEe2c^86?CFzq#w^{BKW*f<;ay(#a(Fe=yM}BP z_Z49Z*1jjGiO8bH2w8S#6WVC_Q(e)$bLj8Tu8Ra4=FmE06IZMD3U^OT5EMZ0OPoXL ze)p*hzG|d_h8Ifcd%`#Zcgedjk!7a0M6|=mRAT@bq_hI49RKB80iaAnzLv4PL)KR? zNKz8dC7UQuWpR5|)RU=m4eepcTW8p!k4P}MJ1&pWh^DZnRXZIE8>IeaTW6_YJ^4`i zV+eq{UXvdB-+Jf%Rxe1;H$K)7K5)4a7-VAdU_Z#x4W`B6vYK)o{6=02o7qkXcXIcD z4-<ereM|Htyj^9GK#O)%iq9bqiJ<A=;+Osqg&-Y&2qW;2Fp`n+o|KdTo-j5Lkx<ov zDR&djQ)7(wQzyeRS|%pPt$pfn{jRvH(+>T~XjmSEhc?(v00^eGi**9*ut23M_(+1L z(oPBTJwk`V8Q(;-N1E!H+>)0=^#N(~0UjVrW?O(*&w5#*+3hg`_27B%g)h07VHlt? zBTSSnC}Yp8|J+<d@_J{9&_-?3U(wt+(@;iF5wL{8r(OiPE&o8)$#mM$+tX9UTz~$I zK3?pWW<jT^#)v1K9%&@sF89#R>+o`RD~k-%`D1-ir{z;l{U^=q#<6KnQs^g_3m72! zi4P+#*bLF!d2af>2yeu4ZqqK4!VuOef!<UK+eKIYqc)$-)}@sdXUDGrm&cD!_JjUr zjEFE0Ry$}rcaQ8vKYouq!<KJQZvzkdM(xXqds${8x;~p1`l9oU(<AGKHlL|-VDNbM zJTzXe+W$J}XEWb(<e_Uk_S+d|CN2PSK2|sGc01&-?m>@rpUssR9@qVZZHpHfje!kz z%gRa>Bg`}WR=VCGrldyE1aab><*1ts0ZVjaE4<Z1q!Ewx0up4{7>pVJP_n$%$Es2a z!Bet7Y>%s*(D(;@r;OqA5&mDaQHdPe#|;D4i_h(hqfdPwTWm-bA*@To<w5{k{L=Q0 z4A#~4!kbcSH8T-hDJxL6NWP~-!8s|b8lX^XQBCr6{BoZ3s+CetByZ6udw{vXgfD<f zw{<ijP%C|B84Xxu-dW$z!gme6tsQ}T8{R>vxMJk47fjdN!4lnjZ{QlLGZJqTRA2#H z@lXUeg9}Ac6mh=sbPLk(b{YT;$f$50CbtS2lIv;n;GYza0hKVqVg_1Xto%c2h1#$X z?KyX{%QtVo;>0fJJmpqSVv#`X103!~2X2<d0gE#omB5wOfU!gA*2wgWWu+-^WMztU zat|?NEaMnVFptMj_K*KPEyR9kwHCQvtZsXdS%Ax?`OL8;-S{!0h$vv%Ppts_vy=87 zSz<XuHM0PNkk*NRjsZTrhs?sCFgSCskw#J~oAu+#_RyFmulAS3W_X6xtPWPfh7Jn2 z0zTABX5IvAgzIk}VZxu<x6*!9$71ObNL)o8Y&hO>O;ESFuH<x94uVon8YVi~4O@|c zP>)j!phZ>Z9Df9D$4LQ8CLLfZAF}Cats}kGPM(Jg1Y5)tV!)X!jA|H0s#3j6GgjBr z;iKsCsgbMxX6zPlncf4O^)fSzLtu`UwyE`xSWqXHD8H0unBpR@+yq`i&UI4<@_OA- zGL-#;7HH;GN6Kp$G%n1il5=Ag@$L@T6mdmra7vsO-Rq0vFBAd4x+qWz3(R%gz5c+V zpM<|ajp9NCc7e>aftXQe$ZF-f`Vetj)|ij}!fz0m%`_~BjOkv(wnp{v^0~O|$L1L? zNM>9*2y%)_39Nyz#%JY^O}nhhsdotB9`T&FPv&Eq+R8INeKwd+avEM_ZG6spqgVt3 z)jrYMx63G+Lv;kaa9$PH3;1yQ_M&2auRS&&TpSB;<OE)~5Z@r`^TSHvMx~1?eSJB- zQeQ+o=HWQHM4T7CKd4~cR`V~BOcYW&gV?niQ&^tUQ_iaqMg>*UI3U6OVP%E)?ZBY? zG)0AG<?Y65>Zpw^<NWx#EhTyjVyZR`8gyn2_|AY>1PK7csGu{jl!fFT9f;kP<WOWk zt<>bfG8W`Kn@NTkFdlJqCRQY`o1pRt9OXeAwyOpojizVYdFSt2Vd>k};9MyjH7GZv zgJ5^q@t;|!6|oUb<wlCj>*z;??e<eP!$5BdB(b0ov+<*xYXlcZW7QWpNQQ_^-Q|JV zwh{4ZOGbb$i@=q=V>|<AOA&9r*=drvvq~Yq&Ue1M^f_qtMd23E#{1Z-lM(7$uBo)K z6OIrD(7Ij{=2y3Qejd$8L~}QVksqM2O=ihPm|Cga?R0^^MTEMLbPzK&U%>Q2h@wb8 zmy3X1)YtrgHv1na_lY;^@L?k|K941*pYTSxun7Q;jE@ah(`0$_-f?6%{-Z=$k_?g7 z5sj&p_4N06BfLqKa2EC)PH7y8sJ(a0wbVeUcY@RA(SnRW5{gvfrB~RBy*l%}{VoSQ z8|C?W)A{on?jwr`hOLXY*cvpeR&ue=Q#I2s#i^c!79@%g(KWN!+OZ)|&6NIB<GoLv z9#H|Tg2~jDAGGs-_Q@ASC(%PIU7O?D>v6u%zk32V0X403kCtM{H@7^*(=p5|5WVLY zmgXmd1MTk7TN>`;Q&u_fx5?F&urm7_c?>+~Lu`L7c@Yi_dFq~I9IBeu3@}vK<lT?0 zMd|lN0U20Yu$ztrY6ayy!Dd*IPid#6&0_<^XhSDVHi<r5hChK3km0C`R^T&gVN_#$ zFb8ch)RDt{j9IZe_I{i<e4<3^!BioeF+#%4RxKdxcGq$Vj)t<Oq7MR8b~Dc@B_|MZ zTkB*=W#x>Cj_8h&nU&jB5y%F=3>!5LKWFG3DqcPkKTwn|cCs}L72isR0Y7vIOYoVY zm#ys5C_)E&A(?N=u>2;leZ~p6IxE@Q8j;|A_)K<?y&d|wW;SZrlCWot>I{UIG}48h zQMm!6?|+S%#J}E9k?~i14M1_tG;w_)5`&ja2%uNvR0ZLefk6=dg8zk;my!2}-%Va= zR%Lu(6a9ig@<OM{*H;{ngs}&DFWZ9*;dG*Er2P%M!IQ6l`ua&1w{|Jc871t!XFE@o zIDj7|gQh!QxXM85A7RwiNO*uBWU+ZqG7kbMf&lhI>Z#}O+(%awp6f|->53LiLxGib zCcuo4+id}kpIy9!NG>$8)da{;gTD$M8yH9skn3oM)pS!vPEIQRjs-ynVakByU}kT> z9;eCc_?RIoXBlVB7+@O+iw%uBZZZ&HUMWO%C^UE*K-h#^wwV>fQ~A;L<K2DW)0hS@ zL_^V3q8!?+Yf3i&4)7!b#u(+~|1tORhup$dPgY`pZHBiWwt=e<O{c6~>m`py4Y!2O zv?^6o04J-rCyr(mC|H+83LoDCS(^;K)}ue<ISkgjRwSVk<_BEG3k4QG$$ZhCmgYeQ z6Siy)1Vp0#jp*w7jCZ;xPMGQn*ozt9FX}|Su%l(ym@mYA4q5?^RWEoK2{;leb>^8J z!cGA^!)4U6E~-QO5pqiCp_p$VO#xBO_^4%#J06zumCptfl<99y+w6Ol;A$I!YhT?# ze`MeTIdxDSkY7~1su-)kP|CHjWMJOpaIv6=Rt+nDD%*g_6oRi5MS{9xR<39RYJO8$ zkr|x%;(S58A#fW4XeHX0<V%T}e<Re3Qt)LMd0YI3Jc_C&OlWkg-LGGapwvNuci$a@ zPm2QSu$q&~W)xjTK8uK#rq*T*rpF!#D>PItG+ew#@#uYdMcK!PDFo|}YJ`YsH<+rn zq$Ci6JmqM2>5UI9=90)4BD1#y7|R?bIK|&Xzq2yRO8!>9@iS<VU=}h$RCBM!XT&Jt z0-Y+7<Ro5RH$R*u8%Gv-ff;aO7Z(R@8-N-$r8NGPJSM1UZH-@FPvmm$DU1vno3zTc z>p-|FPC;L86~FL0rDP?PS*-aq?Zp9q?3z>LM7jTq+*4OrBKR_;pNdEy;C<4HUJ)(O z@U?#lyZ2S}x~;8tJgdwB6E+}O9G8Dq5n{8+v&4#;dx^x(%6z7UF@qlCpJEMupHR!- zR)`y<f%sW^fQ&ZAM}@_(LK0NWZ915L+8z<37zztIWz5JXf9WRwDVi`6Rnv{mLgSKm zfrB?p1w$JOo^jV))&D*KXo3v$A=VJ}0asWuTjH96gEbhWw;nr<#a8@4iF6*ESf<)2 zAMujJ7GU0T7;*!yGlS4Mk-|H(Je3`*V8P+Wz<>&ts;Z-zpG=^n+s8AN@>WGENrzCV zilM@%9RM3xJ`CF=8FI9J>Kmjg4E9Cv;c?>7fq)lPRC`$F1%6Wj<QAFSLEtSZYBnPl z`Xi9Rb$g!O*;8rTt?DO?nI(=sd5Gu>wtJJO2phQ>^zNT%_LcrI9{$NONm;Gf=hh98 zPXohlC2bv@giy}*=eM~E4h2oS8XK3Ar9+XM3EXN*MrV?hr3@WtagZGK{zIVFsuSs7 z2@}T5jIs*CCY)#vz#6%G7nm$X*Gpj4KCDykypf0kESAWZUhV_QWO(qhpUcn_I*&`L zt6j%>2!bB?TgKBJmqe?#|FC)$TrThabi3y+Djh+Po2vJ9lsf94UdFIH)inbllpr^r z9G!LLU9^~}PSgh?lQLQ@eH_WbGEDK}fxkj?f@0df`fUNY0AWkcE*E~G8_&Ygr3xWh z`F=x{1%y=)TvL<PV_KD`nN@C0;IdJPRQRJlhP5e1_nSn#!=HYGOmMzI9qzuK{1C%b zg-0-9Ojh<$=PVK2obzq}Kd#<6x~}gF_l|A5vDMgi<HlASt1)+M+iq+(wrw?5qsGR~ z@7uiZ82A3S#u-a{?sd)>XFkvR%y40p-osaL6w`)7INcwC&Nf_#zjiu!x$Lk66e}=l zH!qYL)aqll^7K67;-df`0Yjk)`wViQJ+=2@lHbTN`m<PAeo77bbVkU1zPzAPuO|*5 ziVG@3?KTujfd!ZNR@doT$EGW$m63SDRh6P>Vjyxv$|FpHkT>WyvuBcJ+O^d~o4hiL zHA57LzaBIe2%~WB4++Z`#nASRH~mw!f2=_#?o(3xrIJdY0Xz|qgX(C;A2W1Dh)5Cg z`dxYiP5^etdRHhwAiwsU81bM3(ru#Zo-_ME<;^3Uujo~rf07T=K!7Ye%SmhVE*RLw z=uU;dZ<&Q$*!1ptSJ~VBeeWY!n+9F8Bo%~zX$|G(vGEQeM(6VWwU*L|6k;)((C%1S z5{Nf~-v?-jo38<XbPJlCA2bB7BmU{^t>q#Q;>$9+jC#xJdpPi-+^n_LprQWr%ioq^ zChhf&G-=0umZ=hVIOni~E{^iELrG$=t+FF~gj~YuMZ&qG0kf#si)1FkZ&VJQbX95g z_vfHUyYTk5IYNQ?Ar{538O-6FKApjpOD+;>Vh<WY#tT3Z@oqn9`H|MHJBoBJidinK zRJzA^6EYPp`s2~v3oyvL0f?JJ2MC<B90!OQSR0ch3zB%%jz7F9`3}8aUNq5oBd1eO z+a1RIW>wx5i>y{(N%?&?dPIASs6}v&aK2mI#3aDtoQQhK`}zjT^&W*-$@so?bKo&e z-uj%JiS7ZqN3hJ1YxceHo>0>hPGCXy{CS^}9h7XU%9ej0gtc$khuxqfLYhS-JcleH zFO!*-jci!`c!3I)x0B<VV*|V9?zt^xx(IzQd7D;ShCb3~$UkA65&s!exzQ*kw5q6N zf79o2vN+=}{zF2pPCxB#P$0S`i8?~|##xl~%4Q~@y)^q-L!^Sn<q@Bs%kz0HYxnq_ zAM4@uo9hrHO<~FXUZ>_xS=*2ydv`@HS<;&Zb0I3xt0Kn*LW0SPOUjI1?=eo4qyhIT zQ#Mo9?l!88avEWG`7W}1KeKsJFsO=9^c#bJ#n4e=F%4!S+;(|-YP6Ux!A&w?n7drs zNr48quC>-oE9gxLCm8o1(0fpKS?U}8sDo;nXr3Bs**7JhoNlteBm@~QOJ<6+^b9(a zQsL?vsb6Xqh^yt>qyu|3;Hrj9hbxb+wfy~D-n=~Af*V$*0rJhR?;<}<T_aJe{>9pg z6ishwDKWpB^y#9Qmk=9FPZu8UD}PRLcX<oIp28tJN5Ub&f5>^F$J2D@aGNH7zMZGi zGhk;$ru0qb2blQJEhBBKh(WXpvR3rA5T%EA4z5b3VjmroN<|kxI+YRur$VCSwb_x9 zWjA)}mpl}HhQsw1_DiB-#)+>MDrGsHmDQk6QM~3V<k;t*imf!$BS_F&d_@fv5)@~E zKq5Xb<^kI!&fwDvd=I&lOyBMnzDg%|-!RcD#yxY07*0%ub0IMK(d;Gu0cKF|!H0N4 zF;Jh3>9!OIM`N$wj;e|Il`MB5fy@-287P}oY?*GHe0>U&%5lE<@~Ep>je?Wp)+kew zE<x?^Z-YUn4ht??WRBb}k$k`Nl2D%o7MCsXmumMwrLA?;TBD;?&?7`x#R+lJ)9G{J z18P7qXO=uDO|nqVf}jsaIf^+}v<#Hb7OMvn#cGPhHaFnO4zl`>Xj>Keqm!xwuo8oi z*5$+~R^o1gV;CyZ#F9JgtUrkvI!Z-4Fxe#9-(sAuqt#Q?gIpDFB7P&t?D|y$bXwk` zz{l4L)~~Q{Ycw!^iS8FRUy@m1cDGS-2k3c!pszksm%Z6TDe=`vtWuS@tpzClsG5gT z%<(?#3*<Sf%N+kZ+{tWvO>SKg{cSe;$X$A8fmd_2L^3pihi%ws{}j8>Vo@>12v!G) zPxTJ!DS1ZO){7<v;Vn`x8G6+PsQe3#^>?5O0m~Yy>UX9t7nMf6Vd+!z=t=w6C!RHL z8XFI<TBHPW7<Gz3mAnvvU+TMIW&S~x!#7}HRF#8-{91S|`%QQQeu0W_l^ZB|yE&kg zQ%Md^uRr@HQ&;l3|6h`!p?^vLJNXD?<UMsy@IOpiBP0gp2bgdZ5u+3SKg(5M<9(t9 z90<tPyDN)0*}eh;v#B2!!|#K2@r4}YyZt{EtRWEt?SnrtnvCJ_4`m@e*R-1cJ~u3A z@_7U%b~4c=9%8axI>yg`V{kJt+CD61%b6JQAM8TA5{&Z?k-?+_L*t`ES0%>kM>D$` zqwPa_)NjDR`1sVN5u^7*n-plrNJ9Fz#L_4R%14KjF$~;~ChP>p|3?hO-^RH7*f;7P z2EoU|pARvdKj0N*rx+a{!>s>e)WQ6xc@Q%G!LEC!nZe>GBZFgLH;Lb4^!*e58W1rf zJ~o#Lff@M`C`M#V-49_f8VmCg?ce4iX)wn>rq-jw9RBD)$BLPy{;$J$7$)Y&=Jq2n zUp{tp5Qq8r(RVKm)A*w)n}><`uWylV0p`aR42v;IK2``S!R-H-OQ9Uo7UI9xJKGvA zCkhV&@`K`kRJQlmi;>LyjEB^8QH?40&l|WY*A_(kZu2Wb|L;kYy`J%)n=INe*&zQ5 z1Xs7lQ24tYuO1=!A`TO~ska+5`=86t9c}T`**h?#o+H_}5d%9J;RO%2DP{ok{sYMo zF^9SOfwQyR#U%STsL+R-(c;}RSkL@#y1GM5Z>0YgT34=<RCqs#;3WU1V}5;~PX7*5 z^CQA-pjf#d@iBzPg8w)Pc8FMoAJ316jCKAI6(kHS_m5!_L|C~W?2^v{STaoi;tp72 ziTq=wB=$Y}ZoCI5oB6+GoA#ZtE<eucnje<!zkvP^Mall($mEbftdf6oum@pDeQd}# z9LwdS`5c2)^s$SgM695X7xFm;Yxsj;`C5*p{n1yq7K{F0lv4#V6pG(TWp(DsHWlx& ztZc%92OL%<;x=yRDA!6M=BD{V8wt{pnzo(lTPtUIXrw#J=p@DccNN7Dz;r<B$7la| zKRMmCLHh@1w6v>OXqMD6&mNxr`IG7`iyUX=;-*w{sYp~2?m4>fWTm<1rd2C;UC{5{ z5s{3~_}FBxWV5YuQ+d0p%WY&%R7LJbI-zzR4V(-Xk(Y_7J1tQxR~)&g5=xu|n`lNZ z9fx6GknWK=$0nRV?^McM(#uPpJXZTJG~x0^EwM0`k@;GbSD)t%!ArZhjm@?x@>iW3 zlD|H{Ht!$6(BfAjed})`gA#jBlQio}AwX`JLnODY6?MtQ(sMeT#4vZAaDble&j;E5 z0{mXLo+p_$^mHbxCYj!DAIC?-A8zy~-<*-iVx|^hJRPs0omB~yUFqn`xU?_hd;>J3 zt?4@CYaxP>Dj-cCTDEb8z`FM+Cv3P!4nx)@e^sZVjJ4MK86p;Y**Q2m*30hHnp_D| z%wLCe+cM*{LG13;N;zY~6pt)3RJ}Ir0+`_xf0|3fOlqpQw8`gj^tkdk53X3o%YFzz zYyE|q3cRlY*9er~bry)0$*PpR-{u=F8u}Z<|48N$Z>lpj_^BrX6apo)_HRf06bFle z^v(!&r|f*lA*@QUf;9emi7y)JPny42Idz;tI<*5$0}ePlI?)v-`j!F+)UR-Qr$D%D z8ybYE{`k^xEOZnZggs!3AtsfDQ!iG9Au;4m4|U2}M<L5=2EfiD&628cyDPkPe2U63 zM`aRR!+IdP7QbP%krQMaq+hkgZX?EU$01WYOU5U~OSXzFDntEJ$|aW+p+5Q<e7rtq zneugL+1P+KsE7ufLa=LO6h~om67XXXVqz7+Y6k$_lLwE2tg6NP=L0T=Z=&6`1h<$| zpUotth3oL#?X~e9Mr&YHlftps9--`ph6&~S^ZE_Vb5)-m*uM*&J0XPEi)BiX8b_*y z?Qexdrf5(5<7ftG9pvy%+C_$~Ga{)EH{yajJCnr3FNKKSy5#!CUXseO0P7Lb{*KWW zpa!4tK39kdrupiHP>%CebmLtQz8_BlD<ODexIB3D@>vRKesdF9u<IC1Gvmu#@sqsH z6(5duo5xAx@F}$i?^q%tmh5BF^~P$c-@i3n8)d`3t@ud^JMN4KI+v9B9S%?=B)*3n zmYT78?{rW6kfGt{mmMj5U~F!Qc;$<YSoWGXp8L4-ms@4Fmd25zrM|rZ5r|VXxkiOq z8c~{%1BNOicsqL#K2FdtmBE52#yAn>Si`?&zc2kr(~ZhxO!!`5m{qP*Z*ZX#U<P|I zfc%3;2|*qlX~VqhVibz|<lS@ILN~n%(wep<tM{@OcWN}JwRG`w69}aR@v}N(Ye7F3 zl4rOsI_Im&qB?(Ga`#4<R46~7I`{m}pWS`+_4t8?53uA1;3p@`4@}RW*WA6Mt_g!; zdRdDg-T3NK`NSC~G1BiusMQzW-0TGaH!rV;M+Mue$*K%P<gQDE!tGCA8q0+6wYaSc zfzw;Xd}fIyvbWkNpyxe#)MImHAj)<dV@NFEDTm=K?y@|CCx!2md8E+Vl&foVzf|^G z?}5M4zaN!($<)fUX65W^x;BAw`?N2xQeLC|Y<N@r8=LoLL#=Z~sYd_J`VN}s3_RBl z9XaJLdOZ8JQPW|=Mo8D5m!oR#Zbg8}*IT~*J{JlCZUdGKxNbz3os^;?FZm`|EKQZ7 zvMA40l9OpCLh}^em@YcgFHik5A=5_&mKT(tCO>J;fX_c{84lIDa||g<9<f~`IYJw! z6Z#OpJt#RsdE0Mg;8Qd6daTN8+Qe5M*~xALb;>Ub^fan0A~|t1aU?q^W1*dcys|kV zXIrs@okHZzfS4zmJq;d=bmZ^cU{P^PsBjDOJ~B{GS-n0%|4kQs?j4tGGX}$Er~qao zCrqJHf}YRtK6$6ogt8Jk0=w}%1+Xoio2E_%s9zs^iWKmPso19mLG@JIc6tR6?aGX@ z@ZjS?npjFpLXYXYsxa&B&rOR!3&q)#jdx7Y473Ib0Csu#EQqBALvia{`7DnN{LHI{ z$!R|IX$$v|PIvfMsZZtxDU#eJS7CxG(s*+45p$Aw;_xX-63SgzQn{fbPc2=P$~Rh@ z2}p45mY^zd)QOdl$`;vk%>w>e)J~S5SnfrR6_AQ_nacH88Wr|3qj(0vh$F#*OEjWS zS>U4Dz(MHGx~!R*jENy)cYd?Ca{g4JOvKd7H`K-_I^hA2hHuqGYr~?xhbDblv$n24 zaJQ~W2-06@DQ40IA=)`gDUo|!>_Asm6zX6(Mv15?|4}};G(=*WG34hWderBGYO=n2 z{yooJ!}zND&hY#EZ(4D%NwAj20nq;4WqyO0fIQz5lwl_-_a^ENutD`{C&3i6zjO%) zQ4%4=XtizAYB1bM&uT}O{12u?VvM(k{pX^?%0N!Oz8K0mSHPUR)NP&AFgGK^C;1cv zhN{@IjeaNFq~qzUvNeR9JyhJD8kW12<8|DCT8qH@r5pEhgrXCQa3LWba=Tzz9yX9K z08eXSeZ86W7x*m&su+JU*`)jM&?R&!($tZ<T-=F4R{>~E;UO_=QEX+`WcJ@!x*dYc zAcX{skBfiVUQ?dSB>G(H<ShsqZ%6p4`cjs;Jwdtg<2JrknFFT_eZjne(UOd$eur9u zLy}@;h5TZ!=YBb$mD{vuTA&a(>@(sNfUYq9EfzH2CUXk##Dja}AQ}MV2*_l@r}5|; zYgeQTL+@8aOF*%${@nQb?W#Pw@+kB5-WA+fs+%?4NTCUdX$Gg@z|uZ0wnW4zi6t9| zYHCer9np~?BqKF8;?(p`i8Y_7<L1}23HR_>YRAi9^-E6m&cn=G4C<|lTpRKS#Hpic zo;bbl6R<b)v74S4a4hZh^t<PhX+j|Uq(|7x7F!l-xtN16J?7~}*rGIoYT%gEN(x-= z3<VLj36?Irp*=vO&lddu{uMayeK8p6x>$n0f<z>HfFYaim7?un$lvmk1GTqhe#+Y; zR3c~1*9tfkYYVaux8lZRV3COhBGISKjL?=}HR_pmWqvkNsjZ5!XN5{&Y7Z?G<*7oV z)!t}N)A~a#-;eDG8GAGGh%x635C{Jx1m+o`i!wdu1(c$8Du=o=%aGhtN2~+*l(ihY zlIk{JMKJM6ajWraylB`WZ-0O5KywKG{cVWX@%aZYC%lQto_Nd=1>iUeu<C7U-NCC4 z;jMV5%Psa%zMe5T&FVtlE@Ll%mYgiM;$vGi?cmZTimTTDI>RS=7;Et-lVvA4Ts$$c z&+{%icSv;*2G0y?kjn5n+?vHh$%VJsn&Y+-CQ|Yp`%|3>mDO*C)?A1Hd2&R_{D+u$ z(wGfVl_hUj%Ev|~nwL5i04I<eEB6+i^qChiFom+el)s2>#Kp+q|FbDeK$MzV@Y7U| z!pQ}Gvm7}!RMK=CJ?%Q@y52vIr+ubij?vtVCdS5S!x`mkuwGiqiYMY{$Qij4P3X^V zT7~Ar48LRIK*n40=k^P-&-nh9&Byy_DTuiLHudXXtB=Q&^hnFA22_Jm4$p*(EZ?S! z0@4y1eS*@Uj)bc%1!s!F({eZ}f^wiNhT{IPLMCoi%qCpZ-`hj6SPGdDes#<v*wqw5 zLE;asj3mv}W{)#liX|Xb`Lq4FVaSSz%7q^R49C;6EI5SNv%}yA5Zp3*vNe|ZO4>J8 z`A$aF<$4;#9N>k+0)Ko;!LaKc(ZyJFRiiZ0N6BPRR0k%F0w!XoY}4{LR7PsQif}WP zaJX7v!*RMA&(OF(>>sf|=u>bT%tNI#ec``QN7l45qOT1tDHWSu#N;E^en<S4Z5r@a z7@_dK{OVy#>#yq0cM9bj%wbhq$9idF20{3xFRv6?fju>|3EY@>xkK6ngPXcV<$4Wt zFP^pFQf_3fL2xoaFBdS?&Jqn}U5j)4@jcPhH;MKjKE5tH4=#;Pd^b2^Xp|H2<Fc;| zp~Onf2R5~#6M?nOTA~m_32=oRkU6jHcE0LIZ<DCOCzHJCJS15r0wXrzt`w7Jd(Nx` z#x?B?FNi=S;8Z6C9ekqIm_@)2H)(Qbs$xw9QEn+Zv_9;)w;5P(&m7}!At%bQyV$Xa zZIWmG{7d#VeDXZRuGm^G=>4Dfia3(|cU41mM7%^!I4%A0>J$4^fBR6nX{9her3T;P z49~WkG$uNwjw<{wweSl$&X7#pIIv7~>YNIGTuB4U0ORt(&tz3J|8AO9qoj(cUcBbl zR$8QPcg(I3w1l7Uyd!1pSGQr8Bc7=g&yX_kh%46LkLE%llz0^N;XylWZ++p#r;4`8 z^nzoH(n@MZl+iT8YFe<WUhI;)FE{zDy@H^45@tf|wUP<>=;cewU6E2$o8%-w<j8Ad z15L0D5F-J<4M9)_406OFab0D*<nVezdf<NH##$g#XY}yTJstYP%hbsE?&ig1M1oaN z*Laq?>7#`z=w+drWY}`ttBLTlT{S3an<Lkd(A$cF$PZJ^Br^w5PAqcHB9on`WxoMy zftss*oP#^Y4<h@bbb2p{P;#u0dC!kfRwkbbobw`-mCBtS{t|_WPs6}-P1-9|6T=H) zO)Rnz!wqT_$B!xJFmgQFDn|$vN(hIimWk1R9DlQ6%-?FKazNV-q90CE%T^VY+$5{H z5AKdUkke})U`65M-W;%Yrbo(z289>pNQXr#=SZK9cvHi9Da}+`F60a+FDtwm&NHqC z1{lV3BXU|6-8}{lo5$U1&J;BClyd^coi)>1Dh%pDdm^>ca->}F9~Bu|EhmqRP}Rzv zdx%E_t}$E_5u;Ljw0#E_S>~Bbv0yTHm49DY3fQ3ZgCm|uf!>)GJ40S>A(XfWK_%I- zfIS<aoY;vj2An%jOC8$4i<g#J5KBq|L7UTEVCm`I4ZD(h)?CfVLUB5LKZ<3le-HBr zA6xI#F|lx5>|-w*&2(FQqN3QhuU5;khhs-Y9l==;r5c!kEn-h~OCl1H4FjVd9v}N& zae)#kPt1;fCTw@gT;V)~w<V`O((FKarsJ=&h{<&5Zliiql4pdotxC|Y<>W8{fYPt) zM(fz@Hn_DORoJUgS9|J@x8I<})o_vP?DLjBGFuoORru^!zs6Se1UosF(3>I3I;9?6 z$cPn)-&ZVz)EA~%tj!$_t)eN}V1_oFfrD5ll7e$iYL~e>(uu5rG`ET!!Ix!$kua*2 zrJ<;=BTa2pIHU@Tw~*-m{L2CXkp4a!np;4ple($Pv1dT_2<?szuA#9osXB!;f!>A) zpyL}RZj@2e-8%Q^d0C%xrDmch0H-=$p;pZad(9c@IOe_WcxP>4lbM#vG>n*m-GQKv z*)E)WznC7%d})_qgZTF8CS6XcGjAmJ?Cb`Ayvb?IwVE7S-2&{%NhNs$V3ecfoo%3e zBl-2y)=iNcIbbEAH<8$BTt_jJu^QBH0fK%^F$?i%y16gGcNGOS7j?{?*&*~Q3}uTv z4`vg;o&)WIgY`52a+U4nNjgY{R-^dKiHG2j^owfhj|+inzm1g5n~X1l3;tD`wunkp zx~G*tYF4XxO^(X8vpUSTfM0=3ON2_*r6(sFiYgUccqQHge#Eb2F6mOIxIr|Jmoyw3 z+NtwNB!wdQVm7Afy$2kfS`%8<&&a(}?0$v-t6zhEkD9|2@&3M2^55%h&XuT!AuXN# zmp3g1TP7xpGFWc73aUS#^)$#QEUyXB6AqEZo|`Tqfwz*+&tVEb0R-y4zecfdD~YG& zmh*=gSz+j*sbf5)#iB(d_7<k3(~h22nZZrJly!r_U5{!?8cQ(wB5}ObM_8OEs0tF6 zEoo3Zot!A3)lS;$Ywu0wWnbYhVSvVwl;_%CI0~OY!DD#9oV_6T;95Ch#;`1*9UFwi ze(N|>&Sc^{@Ei8^08-~*P5xa4Y>vcA>QZqN%}{t9tF)xwGNfRTf5A+_2(LCUh-=de z(PoO@F$QLK+QiLqj*w=PmCzc~!6ctj?bK0T-ct8ya)=zQBmW`F=9|(%E_ib>HN>^O zKX?)b=c802ssOPUCb`ci`rEqb|AZhy!3NfgGarS4kHfBf1`Ov5WTXV4AT6L2@;&y& zugbCCl!^S#8%?_V2BBBhU#^-FXz$vS4HZuRJFX=w1Z9xy;ZGh2qnBLgr$!;&<TYz( zCY4Y$D^cq~mr>7F5CpXpIPLVu%bX|GUz^{cFIsp!`tdDuWV;}V+?)o>D8ktodZN@F zE5#HN>Gs|AF#zVKARaL39PVU<C10g3i_@l9NPaZE#CaAg%ri>)U5a>P3T>MH4m66< z_R&$X^>IZPx?NqkkX@1-1viGAD){Gn7fSow_SW9sOK)G&`ZLs)Vc1w!Sv6+(VYH&Y zmTX*tCqcGgv&8SNAilCjqUL#bc{?d%yfzXo2e1Osoj|%yzARqQ#F{%5nOxYD@5JS9 z>P2EQO*Fg+SbPU*)+m!Unn=}65rRb#2(93{?Oxk7uV}|Tq+cIU8M!gC;(B<xea4&8 z&U)r_BY}yPe764>V?P(Sc#=O~F!))U;+VaNRnQ?28MR4E`gk^Y;Z#cHJF@g_Fv`W9 zV))E)A#fo$|7>XS+Mf<YTff0Cic}$fQim{OfFO|D2<+wZk&iL^#Q`wMFP^kQLgZz= zO7t&p+k1LF+&<4oV+-nOZGi1GU0gE1C76^wf29#9DUqNS{EOLv8#*?Ns&tAZ^hpvk zSY6-mkpMm^y=$wytGlZ`^JJY229Ef=-xwD89)RNxc`u%qpixyiImJy7Dv2}P)emvg zBs&+UF^8=&H{`c$Q}XrRPx?#u51Pz&;biu?N-r*op`^k*v1ScEK|nyat(C0cZk4yd zQ9+Js`|)Rvgyqmyk5fh8ogA(HWLu3{=?y{5=Fa|dxCwhJYlvasMrU<aXhSy8@wNGE z571^3YfWdXfFwU+sN*r5EKyO8OESvO{c!+Ac_RoHV$!D)ml$VSEDcJe>@I}(Re!B9 z;RsKc4OMFfkyuB%3(R85MqkJ-BzwPGEA~uv&+drc$MQtoa`eF>+^oA*9SfAoE39gw z-p@#yg*)f_6HmfUt=8U?#WO4<O7|IE9gtc-xs~4gl+CItyP8UzHCUFI0cuWh)3x#y z>j-AK<n;5Ci?Qx+a00cO#4-uCMP%k(JU$(CBPVECH}vAh(c65zm&(LOy*h-J)g1jB zxwi54tKp_D#KJe&w0n{#+c$7~c2E7_cJLQ9#k6)lSmO1l6_H{vxnt?ketZ5l7a-ti zi=E4M+XV)U>C0P>dnp~o1MVY`iAsCffWo71_ls9x0khLJj?CbE__0#_g`gs8!l<vD zVQ{Zz-JGU<TG6?Ip58-u@u@nH30=FEXc9+cO>&K*SP;UZletiKR*?Xmi*P{OkvA$T zO7^gHawg~z5EGeHS8YpJR4YNM2Kr3BXbP+z^`pjMEBD@bNJr7<CyWmWbmI%!029#+ zv(N(KIQtl``e%HkC}`@vCL&KNC}Gi-Iv6k2wKw}Ijqp|`W)s$C@ugi!_e1Hh7d9+p z)ehD`FQI~8=Lszv{5%a3^{b<kC*~Dst3Rd!-XOXruuJ=}=MeXZ?t&rQ4S1!EepJ<x zD-8+%8pV2<T8qp~A9D{l(N9saVV(0_q+HDwilA9{MAg|+tCbn~ro%^mx^q533WNOv z3y6qMh^pC99+CD#<b@9A<4kZ$AAZ*Ve{P%jPGG@)B*>$tuvkd`<<<Kxuns>m_SLA^ zJRiyQGfeDh>i;B-{0(xVk#|L+^<755O`aCT#%=l|fV~LuUlAWAr&~qIcR}iVa%><L zPV$~8w$^{@0-edgZt{Dr8_bCRAqmi%48*W&K5E=x8e=cKr;Xk(a@kp-T_3*>=6OdD z5GGICV`C+E*<s@X1s6Dn!g(a{Wl(EE4ru69!)U(~?HNuxt0=i|SXi0=rr%Kt{Zi;i zQqCSmsK-FWEn-S@jg+Nka;<D?`5Ac%XzJU_Pe*H$J%*?$Hsq=qbFTkgiAmk95=G4D z?^rWpL;P%ERc>FdkY5*ZKO*+q1jAiVb>3QA^}a8CW2FTbc!nhET~m!64wz`nO<1md z6BzCcg|;3CzsP5XQx`xXrisCZGbhEElTU~tRfz3>AW6E!5h;W#syWWbj`l}WF5W?q z2i&n6342Pb+)^%&Dw|HuCjnpXr8=bb|K|pvZzFga(mVb9&iJ2bXlHK6l)Pk*jSqa? z8A#fAz$Corb~a+`8arvXXDA`<BCiAyMKp=m4VoGHfloISM>Y(OGzx4u$P3OfG+6b@ zDj%1oe^$HubLH(+_LrlzjqBCxfGx^Z>pXKSrRW=9sGER+X>VDD^~Jqz#(BO~$JoxQ zxm*9I5zy6*-y6p2h9mJQFKx}O;a`EQ9y?gGv5^Q}oetx0$1w-2+I)J3E7yAJx(veg z{ftQi62Q_j&Xg4kB3}I0kQ++1E63zzT%dVcq9LxSImw`yX54v*_eHN#0Tj}gtl<0r z+u=@l4T|=8fC2t<tImX5?tVt#6*of5P+jmICf}6krX=E>S2khcsDKA{ks&Z+daxA{ zU4?aJ@$-pU!O*W~68T3ZrArfzr$7e8V6ZxsdT(8P$K%%Z%k);s&FDjwsbH2T3NX>b zk_Pn+E21|p`66P`TfUZBmY}mJd^zOcr6NnDpY#qJ$wVi>t$Tw)Q={h&A!hx1FB@oX z4cfAL8{8G24i-Iz3OV^FlpP>XsBo80YZvl-9a?CQ?Mv+{j-qrD2<09m5`pP7*Z~GT z95$q>Lkv}`M$aaO6A7~M{e{1uL)<|Q%<m_20y#~c>buVCM-cdH|M8@&Z+#M7x1Pgi zMHy6(t<muT;%*wU!nq)+C#=$5!!0?KT+61xN(p<$VvS=UaXw&DnB_pm09sE-o%A!v z(16b@#w&3Je%HCV=0tdRnL69}q#IYz0oQ7DojRXaqK{D6<vdrGtn0*V2sUUfPYSy4 zYkdaAsyMEM3vb8@`S2OM&m&p3Nf#v83XPq00lWMYrl_x%rw(y2$S?A&{-M!nW#Y`f ze1hgp@Zx;)q*!VJUwPoEscjC$Ou6YUG!W9PPn1u4%pg>zUZj?svOdB(>3oua4KHZW zzzlYK<a<aF0hncQ2~DTKFk}`eJbbmz6dDJt14QC)e<%1G^jRT;Fe8#)9E@1<1KUi( zSEBcPdhkKc4OOyw?^QNbPumixWv+$g@%g107eZ8}K!%#X?+pOtCeyS|v;7_5hqC(q zS%w{XR(sv6edD<1tpag%^-aAh*d3TKMzNWwg{1{GqlgeH5gRrdTh506<LZ<8KnUJN zhGH2@40&QPl59o_0(lN0PAv@Wdtwi!3?78#Nwl9sYXA<3m!nw6y`qP-k|guiz|P0d z|M2IW?f&BYhbw@gqvr(^<t^}Rr>Uf&H13SR(-!@6akg&K8&X~5#1k|lnnq|QNB;dF ze%jQB4#CnP)UX06z}$mN%)c`=r8-bg&#Q5|c|BpfKb`qWNJQp6jM}~R@R@quw%L<i zOQ3$PpKSlx73S<AIsUEESUD2CdjL6{I?6vF86_O<HWP>te&y<j{PMiI3JXFYf|rF{ z6(p69IIUL_6t?I)ESUZ5&G&4+v?&9gOaq`C``OW{W$F+S>SaKDl4q1hj+T#Uhl+6L z!k9jeeGUKgcV{q`(+9w6ZEbxQ_9Cp|(^G=qOUx&_&)pee+<&@qE6x5Cc5qlgx%oCG zjLv^3g#du9X4O0v>qPTl<&&QOuy-BpuJcn+xLKNzG{_-n?PMCr3)K$k#o^vPjTQd1 z!iVVLH`LPbUL72gSK!yZ-46)+7cMRVHy4;kzMRil2f9XQ5U{b=`{5#7fuFMj!Xb35 zy+;S&6VRU7pcN!-f+#25h)j*IrHZ2lQ8ouMM92XNM5!d_5LghQ0F6&rvoQ1D9pC6= zv9>K0jGa?h<WZA*nEN_mo9!@bK2sXvybw@cq;v`E1)(Dk;-oUmf5)Lfxv0j_Bq)ke zWuQ<?!)yY-jzuXzHN-)%3=K4GqZZ<0L%_o7wX1-OL><H&(uC8TtP_a{CkgcwI_uKF zHM9e8kg@|}(!#ci<;*F+E8hDD5FhmOefg&417c3T8)rf6sXI1vo7-nDlBPhc5INh7 z;8&@Z<wq6Y@fv;zEeZiYfN;`MLdR)fJFZU$)0f8NCnW2S@OKZT1Ck#cC*@_vRypz< z-2Llx)F%Rq7mz#&-7sOU7-ZqRImp86*n)K+&0gqHV#lXTz)Vatk`<*dT4v1a*(m=D zn%5#5wG6REkXT7Ii;BQ)>LP?w?71R2N;$ZrGfvBvUbd4D&I5D;i}3<z!Io{`iUvk) z#?QYu_y*B*f6vo%esTxJ$&KtH-JTY4ya^dqH?mxC9DMUuEXX10+qcEPj0{8-_(T*6 zEW#q=+=00ksL8n|ez|ODh>H!Y;czf1pX!7~CXKntw~7r@$(eM18j2?ByX2q6n!5*Y zG~JO#l3<=r18+dE{q#%pcz^fwB(P~r)&39}I%c5AjJ2JIuh=gKBl7@cZs(6ii<~mI zEd}X7U#KL~wGztxoVv|r?35SF<}P;_fNaXwlT5XM_=Vu70sl>D&1oL0A_DD&sXj>1 z+lpZRV{E`lb}SwAiuG}JgH%8EQ?8M~3p^;M7hl5Ajtz?JJ~)Rbz^^4z+zPt)C%g+o zwmN_aXOC?r>g{%E0@;c{a*thu{|3p%@&4yD>57ssQR$^KSa<lhd_h+gW!b?CfXpZ4 z_$%fLCY<*EsEn4>!O*ApqPWQi(Y^x;UX|(Pkl}mOe$IVoQt@htzlILM<5-Xq21(;? zv~VV&+X!l)un$cFlx!#HNQcgpn)}Ji^nZM+MkO=Xi)6<5g?HSN!pxW=BJQ;u;lrb$ z2kx@E-@qUg6$w0P4n7BesT1r41$ZC5U8;1ESS%Nmywl_Sj3&rbaV0h3F!|^E*9QqA z(CWYqGd8`x?k1dYhdxM05SA32FVirQc;TCt58284S|_^Js+5HPjj<w%J%^8Z;qyId zp-VbpRf@&QJ6oweQ=d7troIbxxdd@E3JZb_p6U_!wKYLjh(sMglya0E4lqmi;v)MM z$+xBizMRh7S^4(3by6RM*Lujm<Qm5)V3Y2&lD9Fm@C%P(uo3V^b_%I_brmq;&q!P$ z|8-RiKqtcKbg^znjlk}KbY$j8n(Op)#59ROWaTalmlLqi5f;HKgnTD9K^u{`Q`d?U z+4~N^U@+{^>f==YNI!iz0WKM{!p%|MXp94LH>u10q?ikQa)sF-*II~wcfgd@?z0iB zbFAJ`Kt7Oe<?xGe1>~5isAx8aB6>QfI9?sfX%Ugs-bgBOh*Hi29Xq4y+0&~2<lAkp z7#89Xq5DBHnSGlaEh*b$wUY!ucN}&@V9K;*IjnreuF>Q7*^osF!0d<774ESQ#<a9h zPJY0Xd(=1Od*R!B>dQmCCEzP0lc9`gL=$^-Xg2DmyD5|&=z&kfG?9$`YZuIQy;acO zrXQz}PbGUgD55Jgb*K+HYK}b4Blzun@_@11U>5k01>{`0l<Z@r<t{r{ST-1#A`j(M zsfn<u;%7Y8Y<$ZV0551U2aNnwcMh8Rj_Oe48P=I;yR(bQj&`LbvTYG;FGuE5${y_s zWM%>+ai#l*hwpc&c{t<-FNU^c>0|sH%T|fx>&J#hA5K|u->P=90L+G<y->X`(CT-{ zltJ4{i`;LK&U!&t-69ivg|B+XF9YAoZC=^Jc?pTWdh3%319$X2($cHijD?LV-&WL9 zmQjyxxadVcIf*wpQ)?X2$G!G2dcZ2#%gezXCFW#P>zN;DrYw(Xrfgs;`qjE}Xiqr{ z?R~2uqG;)Z0&Sd%wD!-kEB-6c_Ld!gRjS!+30}w#?mNpyPwWekJ@#peYVe&X3B_E9 z#|1`DyeeON4I~|)$t2o^c~K%btg}}DJ2ghBNn8G1JG*z`vs=^WVa`ajj<*EkxU#1- zRAPS($<<RXpm+J8Bw9i?s>v?zRq$zE!qPz5U!6jqfW`W3sGrY>^1Ta|RU)JW(G8+# z4TvzN#EqLMQ$9S{)<c?K9zn`h+rP4U4A3HOEQ+u=1MF-dBPxUmniewPF_ipre^tHq zq5l$BvHR7*|AAr2TiYE1Yn=rx&|&xHn(FtFrl0q#r4fcRc)T$%{W~CmfAS)K9jfd% z<2csPSY1Q#>fq(#a2gsZ`*)xR`e=lkWe0EJ1ib^wdd|f*o+Xhn%>|req_%*xg;o*C z#YNw?2LKy`>e}QM8TjjSB1;b2hw_M8<9I1M;mNp2Zac-t*E4_*6B!&P7LPTTEhT}} zRhX4EXx}FlyAYO=!Wis@xn>oz82Kk8`8ASMF#gMWOk$junguF$H{On<Zgoqk6g`r2 zeG!f(;Zb-Lk6RKf0RK9t7T38E0Ks!R$F}EY0KhjPX}jfESjCTXR#+M#?$3~;Yl)^> zNEoFBm6*!A6Oz;1*o6*>XBNQj97*)q&Ktl9l&R#D=0W32@=cG$NP|^|wUC8NTOR*h zFz6MxK*U5Pz8PuSoMFuo+ie<pXk<XO27OGnEvqtw_#-3y;7me5ZZqUpzBk}+ak50> z2Q*S9#C`23$V#Jqg{-tmoGle)m(F1uTq^0J@uH3OIJ?1E%4~;PTPX|%3GCw$3Jt(Y zp=W)eTOr9$Y8jf8@WcbRa4dw-*tt1s3EGS#53=w?qs!z01Ixm0BRtB@kZ)iscEVj# zIv6y}dMx>Bppr{-R+4+{DiyD_r)0Wq2+(R9(dW;SI<^F^5XM`9j#(X)NK9czoCpY{ z{?x9!u$ci7(-jo>ZPuzT$L94cn-|sJ({&DaoiD3eq8M|k(wdXNui7BFR74BgQTEl* z?3|Na$CVbV7LIKi^=kv}a1cm6r>I(jwL{Kixo@=zON?H@w+xu3yw4kP=a9EQK!Ng^ zh{2i8sYZvGpZ-S?5K5+kh^`!{Sw)=<em2D*;F_=du}mCheLc0$>{1oBVXIg6i%<sF zvbs$%%Q-6SJWbJH;kK7ls;(nH$8JxO43RAu1Q`eHFC?+TX^Hl@W4_wDSAZoGsa!Sc zo|RPtl290<-VhdwIEJ_{#S5Q@rh&CY8B+WD`c?~xxoeL=vDH;I&g1LG^>h|q)9*|Y zMmp->Z<KZo)61)zG02UPGlFv;lL`rzLN#myX{3=bK_QBLN?$hD_mHi;f30jVv*1jZ z41ka<I<GC<RgbZ1@E^2Em<}^z;JGMD_N20@6WFnD;dwJ_`@o}iB&873dI9bbB*K)1 zGTlzvE3LPU9Qmz914;<ujY^AH9g7}jLf_|i`(*rR?SK5~n1}dd_<fm2<=Q&ULm1JR zFE4)q<&I=sXyF`&IZMOslVqEr-@H+a=w?^hPeTLEY#WzftdOJGr7>+$7~-%*UG0-Q zadk0DcWVXt{d9Ti!Sp0d;ZHzwhI2c<0)l>1;ZL_j^|LicyJOfZt7T>c@vm~VsZq|& zJb_MRSRJ5TM#r`DIj^L;>zs}cpVambEU0gpzMNT^#LaNbIi+t&e~J@fXB$5gZ)IUL zG>I6r8$m#GiQT<eZqo^)N5(MQfAW^QD6>gYo3)1OJ@=`XfyB*P2UP~*DE2qEm#+CH z3ok;DT*8wPk5<lIa8u}#-vC2HeJdk5{4d|Ao*pC`ZSgd%SH~xWemgI4Hq~k))6cQU zeiQQQ7MqRQeQXA`Ovwgqe5W(Sc*-L=L-J2rAP41>7E^XH|N3^52`X`_K;AVp8S;hu z=K`d>81gjxfe!L^`XdeCd9IUkn%6k|;!ThvG{wbfSLbifwl|$wy<{(OAZ=f{gzKZ< z%*C1HfI?vXZSz)?Uf($9&JUwFh)#L*m;dp1w`Mk&_O`7!jQc|UxPa~}l>OaDcF=yr zptY{kxF|PL=<%Q+*hLcVm{k00O#fY|4g!etGW50|E$?XeOB{D#L)ezS{JU~^d{e`5 z>g)RUlf=Y^-r2DAWo25W(71kL*iy_JYBmk7g)<duVpVMff_k%)sr8N0hD--VfeUIG zvA@0Sh3=MVy{cKNz_F1ZLn~douV@8xK`LYK0z7N6;V?;z_)LYGAj5v8W5z_?yjxTA z;YgKoo%d+0P^u~Lw6H6LnE8ZXD!;ZZaK3OPiOGe`<G!fXvQ_Q8UzE$L_U*0wc`C~4 zda=bv4SDi=M0t2iMUeFuw<p?;M78?<PZmaBGvyH+Eu=c{=%JdAOb_gXRq<*h=ycMQ z7Lhu=U%S6os0M1kq#qDSyJ0y{_}e$|IB@DV*<4v9vak36gk3-(K7_NXhp((cE}zCN zIHy(iQV!mW2cAEU<rH{i>vGl++v-=pz{@$>`{g!DI%D_2<9b=>ZSV-HZfg4NA<%E* zspM;eVZD8akVd7{1Q;?40;MJ)>+R<QGVdy_xKezK06B?t=pc&I@;4)qi>tChe1sxK z9rJgsS_K(cx*1pNWo>A9wwkt?x-VuYgIOqjCH{4k-0Ufryu#`GtIR#1qF%MpoIrwr z8m-*AFl21Ia9(u5d|`H~=9ZJ!_0AHH0=FGL#2{m@YmEo}7EH;8e!}@u_8Zo+p%Bf6 z`l30H{oa&$jjuwamSgcijlB}z%taHplV4lOMVSbY;LoXGsrp-}c*WFa{*kG2w_@qO zEnnc$r~i>iyEUz&Z89iZLXWJBE<W%~uZnVRO}Z+Z&61%-E#)^n9MkZ~?Y1T6Qxvu% z2BD_uf*MW_5vJJUK+?9_m&_{yhQY}~fxqUFlMb|<LFZ_9rnUXUfd_Mm_x6o4>WT4T z$&(Yn54nwER(~ICB`*D~Po3<_P+o1~Co2s_<X~nX(n?>Wo;V8^*VMD*m_!0SAwlNu zrl0O(r=LXc-8=3%Z?eXa@SWZw?`X}+(E8pKYuUcIL{O<Z7b=M0+f{Q0{$*mhxTU}G zs`z7{8!a!}=S1dMzQVbuPof~%B*?Q-zch#OUTM$MDfcvj`vQ33h5J5px@-$s#pf#T zuo@wzC@=sI&yz=uU6shquooD1vhL*+V=V2r?)uPdMn_oq?7P64<S}dMe|gNB^SnAO z^x0n5t-ZfNZ@F}n!)Dc*62+m;I4~ThK2V^H#~zz}<h1z+ZR<!@MYe;307F35RV(ug z;FRZ^U(IIoNXD(dn7VY9obO=<cKORabfcpW;zy#<=YwB|@L|%H{dsT_DN{2W3ExSQ z7a;mB+luwdCU0Lw%SyOtu;^!S7U6ulrBg;%#3#JZBogPcModT4*F<m2Gy+3a@l4FP z#wBp{j0>h}3Uuf;vUT5%xP)L?bgx09ff2rR1#zrx3r(EYLg%)h%5qgZJo7Wn)Wy!V z;-BuE{swd66XF3i)`mv5wbJ-9@cGmxU-a;xZ`EtgoV4A958O^3dnzvvUqNaTh(7O2 zN2GR`wwZ;mHl3EMom2)xJwaE^{+VM;GlTio&o6(Ls-^OhHUgK5>3R@(iwiD207Ujj zg3JAOiHpB|km`?)TC53YK=h8ciM$Tua>Hk8FuaCJY^JGbn<VB)aC^V9c@bs<L#rre z0sPto7|+)^VId<P5L+6Wwn50-=lhtQ_DD{}-+ZfPJ8$LQl)s{oR<whul5>piGJ!#R z7RH|bX0j{?8SVJ?#K3Krjyiw24mh*2%r3&&|1`3SPw%ZaLa`v_!IGOd>oW@O;9hvC zoo}Sqj#*9|O|@y$JoiKNTfl&Bo1~W)b%PZ0u*nB$xbZ`5aD=HViR_8AWUw^X!H=;V zS-;?`+BvdfPjYL2V2Sih<66H(YLYU=XRp^dGVpIOK{unv6t|3-qm~3F0ln@t>tO$+ z)4;^*`m7^sOBaOrfDJ8%C&}lPg{@+v>@5i~zrE(N9Xe<HvT?0BMec-`E`nqwy#A9w zdk^!^<5;i-8C#~6X9{J%jqxa5)kkMIL3FkZnX|A!ii-7vL+^qT;9CNXGL!;ndQz33 zThC-D7{{;9ouNEsSS8eRfR)c#dVfCa|6$MzZPoolN#`a<r+Y2^mu0iXaS-;?D*H*F zakShSU#N#D{0<b#U~X7deJo}E$@$6lOK@55RT*fX79W=n)AgXEU7&9f<PSwYhY6Z} z30IYnTEUP-X;0`}%d~ICkSCIk<1>?(G9(#iC%8z1w9|NhevXNmqyg%-7B(IJlr&tJ zb}MGp&UXa3G<!b!GXB41R}GMr>_63lwAugkb^L|*hoxxSY@Bi+-TNji2iCI|?;ZlO zmgsS{!yQ*)>W9?8yv9jOMH5LQ)S~0tXG#kjO?6hzcPpg!&`P7*g!`at`{!juy(BU< z6&FQl$8k1|Li_9ng-(FI3H_9tjs=&W8UIDN>3NQ^<7_1cj~8_Q3x{hj<p%YJQad<A z<gZq(ywvM+9uI3P&$Wz`8wOIdppNX--PDfD)=2XPO~mDetg`v^CO;QTGDIiZ2Zk&* zF<1{8z427n?Mr$$FvT|7wBBN9dsPm-X@RJVEb=^Z*<{B|lX-wR=PHCGt059J4Z@Fh zW$>x8T)U!9N(W51DC)4$1R@63>GPa?4yjE%2RC)4l&x_#cSkqxm7%}=w&5bd*H(GS zI45ps+U^UusOji<u0izquY88<RuSzB44)kcZgzGY{k-nk>Q~Nh3g+8RMnI-n7qU#? z)fLX<tj+Nx=>7sjKRe^!&WMrj?`U<_o9%>C;<zoXDwgxnAM~~B5P@9dhf0?{3Zvh; zx7qhcMFgQhn^)`Gi?%yS;nS^t)kd2+!qV!h2l(69(W-XUu`%ZU66=?uE=Z<%@x94Q zls6_sp;=VQCzG!GK08Ra<jNNxn^knG;6NahtPPp1$ol<DyhO&xIE{T5KWqJ(CsQDc zVPzq)xD<~#a`#Rc`1+8=GYOkB^2d*{CVdK#j?{S8CGKhF1QoWeSAWXi`pXc4`BU7k zcC|uc?+f{L*^yJopX;|wVoNZQC3R00f`}HlhxlE8J<7l}mgJZ)Jc3(qq%)FNc!c6P zLd>fqt~D)<QgUN6xEBkZMI~&m7wjuc8BOU4MMf2aJ_iqlPL4Y3yP1@hAIM*q6#9Qx znkqMHDWv_i$1l@bgrBF`coh7<r9YcNaP5nC4bK<>1O(@w(iRwjjRnXr7WS;s%Mq3f z%|eB$BU~Yf$I_&i$>L$C_ElbOxR8stbNvZ23^I%#Y~A_ofE>-|w|acn3H`}jLPOdQ z8NP>noCfu?j#mng<oAa-tX)6bMn6XNA2LKH2>E7g0p=r8!dr_+$Lb?6qBkqkVC2Ea zt6z$j{9vTE{8(uYy(oZRK36}9O(O9FqvGK^g2;#IG-2b&g%<G{$qORvQ)NapHIM0) zrO9>C%X0U<$!>CAbYnyczc=s5!n!2vtu+tRB}q?&&pgtIC7PHwlFBon^qGyE2G#yl z<u%p*#7OfcoF|J}7;>ihm%O+%BYc(&v{FjID9RgHNZ!2(hNKd3F9Eo-P^Ee|r;&5U z?>f7q;~y|XErLFW`0|DaU1?&prT&$?cVT%?l0ypOY69C?{XkG95%B4;iin?|?{)$z z>VT`cw`iksU5+Om2JO3Ndw1>1DHJ%>kE!S^0qUo#O%}vU?vc2<i8Wq{Kr)S3=!%xb z&O6#AEeA7|$83PCxv1Q@YkKE+;O@AHQnHIRboy`Ra=3waP};ONM_+lG>(sz;F_Y9j z__sLvSi5u;sj>aX1D!;%@%1BXcEy$EgZ=W>Q;BILr4jM8dC}9##_ZT(o#5;8^eBF} zCE}M9wuLNzl&2MGy@Jsn5ft|<zufK~oVRyK7Y=t0!mohnFto{}k}p^ludkjw54nvr z7L}!1v)fOjgOW^kWRY9mAwAP=nUg{JX_iziK6r{=Juu3+LALS={v>H_Hq<QXGAYN? zcQ$6xSd=cegEnWUx{!%?OgaJ}@}C^C4k(0|CSGwl2Q^~|7D~p{LU%(?p(~U6;Ns}m zFNs}O8)JZWqBOoK-rGW3F?Pb{<M%{bTd9PzDT63)Gx&+I*9D<7>)tu#Pm0?>CUd3O zP?TaxhZs8LR&6_-zI`ZMY`+94)ZcVp?~||`bHQ1m#n>)_I28RWjDNLIlt{_YJWJFL zFt#3rWBqAl(zrO93agcg>nJB*&Q+##T~0nN&65EfcdW$<t)!)s%d51R_<pzRaUt}G zidgpb{M?)`bmSl0KS_VTTDp;kmF2c5vh@dRlY?vS3J+ojY}7CxM62h$u)IBbB(W>% zXT<k{X33w`8@(e_tL#ces|BoK<OCXWB<8q2n=K?U)Ei!?tIFIZBKw<dy3x)#qe>~E zg;*g0&{9n_L%KTpZc>hkgf{uM&Eljk3?5l@Bdq;gYehAjp_@0^O?-#%xTO_3u5uGK z%Vcsj)AmhVBFDfVISG&7X}=%j;5;gR!s-LG5IUj8)*p?3eU;mW$pj<Y*HxZkPlQ6F z{u33ztV@D=KB`#*={F9d9uC&P-3w<;8;Z0G&?W}OQvbb%T}vvvVhFj6VYwltHVfii zbMSm2_BP{lhfO2?2|ajD>z794&I7h+fB;V$((wc1L9(bw>F{}`O`-ZHf3evh9Fam( zdt+&%-{-@$hMO`-#4mFyoTA$JUs)QWJdiMJ${8aDnO#3K*TV<>4%664k{j<WQ((@# z0yI1X1-K~q+wr0*zu$on#+*s7E;>FMZrG+SqqUFPt-jX_IbGhr0u-#F`R_XG5%8De z+cX<GsJ5BS_1n2*;trb>*4)x8*v={NSBYkZrwQ2<e)`@Od~zme(IuNarYwv<tb9aq zo7nije-7*ZqM7!;8xNAJVzDp(^D)rTfZYG@(k|Zrd<^KxoA%gH$%Ap&jQ=hnmLy^i z{xeHBCShB`{%2nJ|3|!!B1ra4!p2U9&cSB<&;77_*IcRjPN=1ZOqL(TCQAMdiw~Df zo{x=!sM{C}*8nY35-^8Fu9z&GkF5;^I&%B>^YP002vQ|VHb*RDPiQ8Gg%IX;Z+)$5 zhR{Kt(*Pg!{0x08#QK@4mYmz~la$6Z&ZfI)>0ZErgev0P0s%TRXCwojUwXQ}`X1Tf z0kV|1?Lw|@ppz22aw?mD-SL-!HtPgr$!dxJ!`4?u$I)Zk8e?W=W@ctP=9rl|F*Dn3 zX2zJAnVFfH8DnOOW5=)OOWvKi^WKkIOI>wJI+ChODs7#;kra(7bb_jxn#zUK1XyLM zy#g$*0GicB^Yx3^VACxq^NaZ20w8pnMy5G{(s9;Eo?D-h^xzgsF8{zf^cxi=m%4!O z{wE=}$T}Qj2pM88pJh9@HTPY-`%_{0)>FM@ekyR4ig25EntZj{vNEM2t$*S+3Jze1 z9dB_T2bfrG-Mrt?PU#1)-Z{XR;n&wlMANNV0cdHhpbe;}?kuP}@>Myv%&suL`*`!H zc~N|MasT}AdEv9C^x-@MiTY#LPi!^8O0K5f67NobvfR7@6-A_wr4BeqK?l2WL!X|3 zwRVT$*a!<4BzTXVBrf`GmohQuUm5Uq;mj)+xkq)`A?lr?lZHpuaDd7_(`Utk7=~#> zDz^C(W;e@=ME>9$gD;rUsEPNYu9o;B`W^StX>7LNGb>A>mGJn@`aj<h#vx##Z4UkT zDV-`;gvCGv7ykzSzn`n<iRSrJU`*{k9S8_vs?r2-8;6noH%5DpC2bwM4Q@=|=NjVq z(5AX-RyNRfKP<*#`GqvY1xl%TL`;#~c~l!GvP9f?OG`t*Teg@oYFXr^a{yRI=x9FT z%n=_JVf=oEE@rwE$NrN>wo+HpP;t8ktA}BGw~E%glg`ZCMrPzvX?t|MWh4Dzi1-WN zmqNvKSU^=+0d_3(+fe;39phKbolV_LnY=YF0UH4&v+0N{C%q&YItI97fyf2rLg6e^ z)v~Ohl!}53zx|2zE1+7iHCuMv&rJOHJ_%Z)rMXFaCrW|!zHw}JyD<%lFsaN0^55gI z-J8QaCKCOyRmQ~ApXu?E9%3+ixzHQ)!rug0A^?z4rqdAI`+S-t?8{wqz&$+E`wKfW zUp`!Z-*Uxv`QU?NA@}z!C4X-@avY18JAw4LKKZWQ@Su6heh_J*NIl--jlOfTU+><r zB&Vm;mA!Q5_gj9rjMORLS>(4YrWCkF8EIkXF46^J_GU{6{MtKpH`ue_IH|;ED0>%( z5_Z7E38{&j+p|i}zArm&k+pQ|3zDq!WN-@h=5BXt&6p$&{=TH7<c|<hlrD2q^8o9o zqVR#z@Bt=>HtcpL1HYA2rjVh}#b(38v^3ZdvS8Xv-pK5+BC2ET8bj<$?bzURN%$P5 zk}C6?a`2~c%9uFrdqI_EDYT@Jpz_>b8HfOoqmUyk@yw*D<mjj7bkc-4C{ab+GiPJH zsO4Q_+A<ZnjLnz?ZxyfzGi1=bQbnYW?cznpT0t0ret^x~D$1q9aq;UtL6x<ZpriCm zygCaBlE}}g_`(D{iLg%=W4+S5&e~rg-s8_Xep2xMq<9UVWrx(E8G}oaj_46VbT|c= z)NULGzm3~d(*T36<xC+dc_Hb4p9Y<9KutivpDQa))iaD?LqdT-$yDRW4dJs|ei=JR zr?N^=6v2#|tw|`*4U5b?GmXYIK6$VTYF}aP!*F?98xx+ASNih3ARp&NmF4rY_76)} z?@lm8ppM4jF}KFV0c`d`=VbkXMvXq;z2ID11<8A4`8#jPLeg<M2JE|zKx9SV@I;fe znooeK9?y(hvH!<`d4P_cF1?}tm-JDcC;LwT=&~+&+fZ%hFn}HcFL@xY3w6fVp~mRy zY9>1-1tzBbjmV6bhyAyUgG=&E=dX$x#*o%<uuHH6Y>HLYX)uwsx=rKoECVipVEafm z$_R5P^qlTs-@D1+KD-C^(AHBl+xq@WSZ|8n7rt6VUmN0Y*GR!DqWf)jfEt!G7YMsn znJYRCci8SW-WDJ)+Ai(}4YF?P&jc!TEc(*bTIHp!AW<Rg&po09VDX+EV%Wo<t&<~w z5Y^f|L`YsWf{)0ygK0zY82BrIkTq^rR5TG%T15>hjJlL^Eby+_U$9E8fRR%XM%a$A zZWJG{mvb`XXRA4w-0(bQp}U7?(~=c4x(-Unb{;NXRB5&nvmWMtoG)_zveWXYm_J-K z0kldflJbPOIh$@eiKiTBnUlHQ=BoH;ic%&}GO>@V+Egz6q=|NWSseuMcm&oFRyQ!W zE#D9O7_hnTNIJz9rWKRy+J_1&B1Q<8J_5(sCsJlA^CsVNd^Z(Rv{@}ub}727T4*Ux zq-o`&U}jZ~6+Xxw^Rn>Z>kNYtzlbYY7G+g!GP4i+vFkw)wq<$I8Lfv0i~*cK-lV)@ zZ=4>8z_=mTmo0+as%--}Xjs)wOhMx6e%6yTF{}S#U&ndvWQOK6%<(j2iEnR3D<GKF zmRd8a-x;c|19{mSMPE26yz8aeqb0T<h$ia~7Y9OM={P%6@JY(3D?nRub_q6xo-)-k zj9uLFSliV3yDV0Yx)B>&MMPh8*ELYPv1T-?&6nU6;#Tf_`4cXHwmpDYM=V1tlrN|T zIE*#<c0=h^)5CQ*d!S(!H4`8oM~p#ga>{BRSfXd-o2>U=@h!H-gsP|Sw+Ay0u2zW^ zbA9r@cGH2JxpcaICeFj!@AGl%7jB${8ulW8f|%{Jt5wiKLw9n!v~s;+Izhjwq3R)+ z_TdqX+qTUTV#5N!FX@j+NK>kH45KGiPzLDwl$tj>5=`6(=R1KSa8|l;N(<IT(>L4A zFyVn|oh5Hd*b8&KfJ$3gZC)@@z%0(%#z3q#DV>L?K(4Tv4x!y$<|02CL(toZLhPWG zf1NJWkqX_X$dk=pBJ`e*8I2o3OV990wJ=LlmOqzIRR9CXi89`p8@dH=cC7huW%n2L z^jF4&3m9@BPhFawO=fINES7P$q<U@_9wAmzJJc8J!t;VrSJG+YX(Eh)Z#*f(=cRNY z(0$*%)<Y=XCQ*FE*{=|)@s*=<`EkWt^_Zq1^l2}EsBzNS8qOgMdu)O<^z|Fvcyd=C zzui@SM#DK^#$x3*L!qTdXsT1OHu3NqlwA?alp~6UyxY>JkU*EhY_Vrc*AM1Hq`7=a zVfPK?&krZ0*$T;VU8{Lg{Z3zVX)%o5a{Bm!${<2vD_39O`K2pT3ePyu5WLBnfzpL+ z;7jt+(RKd!n#KHc&Aq)|8}DDT-hUFBBvL<N+$nAYRzX2Nzjk^Gn3b*3VYlQ~2Q;2( z=0cR_xn&`5ZKuWjszLk=gT#U*I{6L2$hd3PxH{Zwq6oDPIU+T2CAZ**!4r{B2{)JL z7CmxhaeRx$zRHBX!<@PKxlqqATP*!Noc2opfnzeKccga_4guZL;Bf%FXuJMfKFYJ| z*>)ie0CUku(m8Yi|K1+?0M>Aa;+stOe5Lc&;WRb^6*#x~ew-49p0!(4F4d+rF-DH* z9wOnF3&e=#%U|fc_+bQ@PkCSyHk2AHjBO;SijJ-zbLq#eEuS|&J&%<~U(~z?qCT%R z<?Xx_ZyH|qN1U%g!hKv0O<C}D4-8^LQI&G6132pxtWAuG+eg;-5`Q#vD&d<1$Bb;r zi&RCX=E7pp+cZOh(`SiZ`j$h9GybLxBMF5av~L7Aj$Qo$T1W95>PPMLD+Srl?HiOE z@sDDj>Z|JLq!~Wp(OUp6e{viV7+mA{`o>OPkv@@bh}W^Yal69$cLW&0&dn$LRgIW( zK>&9Y7{mwof>qiT>#{t7an;Rgij_NwnL!#A$of!LR;iFxSj+ia3XWbA*h_=Q9U;+0 z-2RLk>swy474Lq)_a|{dhHCJ5A@bI&!Du-xB5NW;Zy`%=J_rQKPs|k(kHR7Q-Eb^q z*J+@5UBp+qm*lTM53g&D1)0s-ca&EL@B#dUKvF!kxBWa_Q~qyX$csjs5m_X*v-H6E zC{e?SfC36hC_e*1`Eg^530LBbD{o?xP@UYrX^s>deA03UF6q8M+;O~pKrYd*$}Y}@ z2d0^mK_H>w$lo5I(?V|?dRpPy{dyPaA$cK5TrVGG;Ag>-mDZmbdoh*q6-=A#L<95* ze)=NmHOmIaCBS!0&#`L5=(+;VEe`$_9OUMMnX4nW>)iT$*9~zSlmSuxvUxjbx5Oxf z8n%{e<k*5pfoUfT@W$||B*bg$1bafu*}5F&TJmb3;X{WUsn3iGbBW7%s{w`#PT>>r z(Lu`PQzx80Avis63HI;m_tIYkp=|))=Cf5a`XoYMHp4q`<zf`tt_JM^|7z;bF-FLf zkWLO4cf-E2;uj;<{tx;&`6XZ-&xH9<k^-?-?pw?TCzWO8g3O?dDvpakeimHS1@NiW z&asw?Q891M&EmQDVilPO8pNbUlXX}L3K1XLm_Gt+PcN4t;v^m|(xYp;N_5d&;Vd5h zYcZ+?whn6n?Vk@NuFs%b58xmm=rDgilwgLi-~ej!b_bluUFRC`nP6503A(ug>>?_Q zO$39&oRRA=o>D6Q37;`h!d0bd!{?rQ++IjF#yp?33CJV%Gd%8(#X2twSrw&>ZkE{j zV_SFPJvC|Y)wQ_><}Y{|o0XhDL84JGrHLT$ENBLxE8;DjMxHuP$Z!4lMzT@17ZLu= ze;jZVLFHM3&>YUGOmfKgeI>$bAkEL}kl~CvW2U}hX(}PRC34BwwPmxHf?r&C1!ofX zE{0`NRcXXNxJVVg`(tkdThN8t*I;L;wp)L@m*?`WU5k71YeeyPW#}03btArrz&69( z64`o`xJJQPqnLj0_107}ye+hx^v?iW3x5DlF!F%UHr&F1=AMCo2Dw~c0<wouaC#=O z`0+NO44m0h3fFVSkr%S;sQj8??hUrY+M~5om#*pk(d%@X8{e<i$z@sPFL_g<BY5}~ zb2&&m6hG+8%*U1vc7%TalC<0*>cNrEr+z(Bg#D)3ELY1=UOEWNx?b_ptnod#=Aa+Y zslPfe#g&oKwv!=z=R_UvY^KOEem_u!{^HIeHJ!}Kg3f`daHCvMrUbg>ms#7*GApI* zFPoRAT<_vR@e8yHMd#}an)WE7zWYLNiV)cYF7}q@HZegyZMC5wc!?daVfZPgJSM)` zOsRX*1h2Uz7#FNo8~8xB?s_S&8HYXKi=TanF^~zE<}{6D$B8Nc>JoH_jh_vp7i6BO zKE|-d_*)>BnEOrtXvK^Q&UO5v(e5|xAI0XwJDD`zMbmRs)a^9}g{_)ZA6OxDpq2D( z@(i83P%@hvxX_(iD<mqoQO*tF4i1?^aeVT@?@i7zJ_WayNK#_Q)bqD<*>2JR7E|9W z$Q)j$FFQn8a^BLbk>x^52G6n4!{ECY@{jQKDX2{^hMnq?77S$vs@*~WQGBFMk$Vvp zBxc-rVqol>jr60Kg_2*$_&Z2NA?|AY6dKzL@@HiAv&972w&v10D1~i8JX1?@Iw+qf za<D^IVj=M3#W;odjco^O8C)fRFJhKl(oT_sX;OwrTT(O?8*cP*3G5VB$qLWvov}8g zHP^0HXmB1Uv4gg`GAp}ka@?_$UqZ$12eof-u?AQ*_L4BW6uss-1f{juwgTALTk5cf z410v9bycSduX~3|m)p~@7qTt2qWIDN<Voyy)Rx0pkx_Y1&Pn$Y12G-I67>{}{yx-O z5>PJrp;SP+p~cWtx2TJ{OH^x;^SUUwKT0vg7@Fp(;iSC&#fAiJO(X{LoMU}X_y9jR z>yQH9T~YprfSp{wMD6bWUU&L#S_5-vy?ToKX`v1ipu?alHFDLgEveZKB0&q65%$|O z_U<s3$Vg+%?>}GDFu(;BXpn!!hqUmFVL|;3SP`GV;z$0o8WXgE#rMzKLm`lu+7d{m z1&NTVl!=4e(z=ZW_)GP~cZhZShZifA`3}qD&*Dwh9aaR^pClhF_6psfBqk>|<zMnD zVPR~IzsU?4?3TaWW$ItBH!%O$a+qPC{~;_>ByQz3AqD}dbNf@alr6S9@jphXgE81* z|D;ObVzK|aj}(tx4F4yOwk+(#zie8Hh1h5qf6iL8VM9{>N!ssVr~cuMONBeZR{dwS ziE)NO{t74zx+@L>!ks!7gH7BLcY=+E^5>3{pV*aulBpVSIO2a!=D^{Y{0Z?%{ltLt z<<Ch?2Arh7F5NKU?0ots^W%uLbl^M*BoK%Ykf-Kt1DCR{g>fKT-Z*hC|B`)Oa^tN3 z<wcti#!35|#aA&Lu0LX-TroDc6~L~@LIMFH`*#+p`j~jIEz9CKO@I3(Rt6{gZ<0k3 zN8)d9cv|34WBt>mLJlqMr$7=tU(8g8Hf-F~k5C-wmU&+sNSHtVZ-nBA{N>amj>94Q z+oY12IErY03^HnPenR2@>*)!*YrHPO1_AK^YD59a_CSE4H4LmAjI61BOt|Q&aX5Hz z0Go7!*u!C$p`D*JFVx^^LoyM2Ckz4r*WV8ha#rn41zW_><ry4P>E;dE67^ndqk)%$ zqq0%N!unu`B#|dW&=B0})(~|?squdNF@mCZ(sz6kPnDUgwXSEn^<&0MT|ctD#)Pu0 z^#o>edUD`@?+-I2_-my*Y8_D$&<Cug14wZu9W~PBd0F7l68iSmDimU%YUXdJ40*9j z67MS(8U`oJiMl`fB!%QYIeA%5tYdKEn3^XO9t|gV*z#9-9yG!fA#A;*G`NM2saFr~ zMgj@CXPj)CJYM<@VWv|~N1-%4zG$+m89Od~YG`(`sy(d!$}^L^v>eJyL>X9A2Do!Z z-5geJTC=ZYHhUd+yf!u$o7!rVvE>pF<hE&z2|tjqyFVRV*%Cu{)hY^=FFVC(mQm>M zT3c-s&eBQjrhZ!vk$I8#v|OJL^~ERjRXOg~bh(){hug?;J@rvrZ3gGh(ED!7UpB@6 ztYeXalEK<Q(p48Z+`ijmk$&ic2Y55LgW(joGh^0bU*#nD<-(j{)_K-q*TeOrpCT|> z)}YlgyQa~*h(;$dCaNM!_%f0+Yu!Wt*E|c%R+UU>alIftL>orN?jbZ-u?$m&wiBA; zX&J};dmY2X9Qv<Ya=W&?ONR`?`Wo4us+6m-d33mjMHPP{U!|gv=2j=OPJnAhH}rCS zyjEz4L_-`@GNZt+ZmuLocCC<m!gQHPO<r#!OLaEa`a|hy+~3P>OdD=|y35<ioCF+d zM`W}M=TrDO7LfbarZ^eA5Bg}gRywPWfW@EAiq`OWyK6rbEfZb0@;JL1ZwZ@Ba0q_) z7AH;7Q{}dr>&8+#xp*!~XaLqWOG*UUV3T8Os)cxX2Sgkjw6(JNzq64UAT+aY^{7S4 zLTb3?9cR$nf%#&D6+aB8x+kjBBu%~~v-me5HVu)9snm-%P<9!sZEY@cz^NQ@Q*`Hg zz}dK<G_Wo$SLzyDPL)+_LZ<3lnD05aAuRTyBFT>;B;r~_TW7Mg=mJ2ErFZZSp<1ec zi+HAY)Uq=2u%EB1ahzY4H!-v><oP3LX9iDwAG#G4n{3i6UWuHYcDtb9)8-@nNfFei zLK%9~pEqOO<|tvr=UthDC4rywvpFUZg-&f~F1n`tYO-x9papjuk70Rr*}dJop{0}k zXlyvdJ?4k>@N`ITqcuSDF3jX>AAOJvTK)Tu^LhlF=i$PDMf)#{=u7LiFE|3Sc3D&O zbGaRmnaD)dX>@{poZZe^69qr$)r?%9vV(yLWd7|e6D#Jfr_H#>HuOM5jX}>B#9RJ= z&8gH|7~%C%5w29iEzQ!h(R}|LCp^r&O0r4&*a?fw{qie!bu>WqoLkVLz=)tK^RiK` zOSH<az4kHU&+)VB^-MBz)dS(?5qz|T8Hh8(<Mx1^l!e*JG$DQH4J=#mt~A2!)p>!p ze5l03+Hx`Vs+7r~9Aw;Jz1ye;N_zszid+iCwRjY(?|dp04OP!LD&q-1p@_&OU*_qf z-7ZHiq~v?Afdq3q&h?Hih|m(e^_5!qo?%mI$-~`-1BTO%BtaR4Dy)k9u}7qRA{v9| z30v||x@)7Duof;Qv<j<TwPtqA9dEmpmd3osoIH_3le%q-L!`V`s|mL5Mz^{1j9%Q< z*7^@7)g8_$R##6UKkJx$8b-bNIdzUw34@+aUt7pEH=+S1%mf(4;Yf8{nOZINJ6yV4 zY51LNFD{HsWzQ>#{*Ufl=iAIQI;c<zgxaWjtUWm&Cj&%2-|bs!$-nA^>4NOvp9WI} z4olL&U$6<$;`U#RTG+fnW!sWn6L~ZfcLj&60{j$Hb4Z6}#XR+<ypiv$%t7zff8EM= zg!;>wOz;6LA}hsO`WPIKiK280#i}(D)482`z|i{T!YkOmdi?^WIoR8IV@z9#d=}b0 z#eIvYJUZu0YcW+mC7xel$&_b2&pOxKbcG+Nh1kk@I0(6jfjHSC=h7Lj)iJukjixj| z$d%D>DW!=}C(Fp*`@T--?hlm^E%dF0cZo1NtuqYZb`{5D|4gX+QobD<`iW#YHcd<= zZkzY(LZanV+7*mJ8_z~c7h6<dPRFD;d>(?s2!eu4L~1y@lzx(li*Scr`{)5~Z#f0m zj-lpP;|%uK@Xuk<TDy&EOL=f$q=<T$X3#$#zxJp*T!?+zXcJI}e;zPtTv>kF4br)) zasA8<SaLyP#7KB|zrccfX5a4WIBWGsk#1Z1J(*FgdoiLiI)3%Tbwc#v`4(@Ke~eWc zC(HVBoxW-Pa5zl+uIf%x53b=rks4(@rIU+0UDqJ%EthTMK`qhL6=5nZt>z=@<2OEj zGfQu*joCS6gC^(UYEyB)RCv2q+78qyg!zOEfD#6w%k|=--)d?SKIU2AGgK>x#pj3j zz&H#07uVWnrRgSx<~l91uw_EY^53~EU5IYQQBZloFenAHvmT}X{iezW0^T@X2zIx0 zJdqzyRsz9P3LPHh$yJE@ySbiUR2AYKnvSokF}1Dp*yG)f!NaXXm5qejevKZ;x0^Ol z17_<M@Q-})^)&;lsT>HoVDBC}3*ogd=b}r9wWxE5?B-rEQH};1snVRixkN|4wGnpD zdcF%Dl2b$u4Wybm8ZR7BPP&&Sc60za2*SzZlFHEV5QfkM9S#?r<7gr-9Tmi0kUQBm zkh9PkvnKBuPQOv*-bHgW88`XmotPri08X)+QZk9tq;8CUbnFFLf`tlc?Rg<ohvbvT zX6M3@XO}@mD<F%wUr8Hc9U~j)v4^8W=`N_s4Lsa5P9z0cht805N_7j|S^XqEC~`*L z&%g9zhajVwh6_***oO$70qYrTDA&axp$pz%1=Y=UjQb6zimvep58Kk56%x6N0I+P4 zyTWhU6lvmg%ILJu3^<_oIqH75QX`l%UXZ=Nfuz3yYg2I54VR$i9e3}*g3JzXHpt_| zZ=vx<=TKTVRk-;Ss<oPC%ka&NZtAPhe9TI7|5V+Y+>9pzpHdOO5tuFq6*ki6%f0fF zPebIScFsEj;}2of10nqOur}q>bbv!A^odE=ISSCC0hOvv2G+3no(>fk4jP-3Au&}) zrS#qJsy<8GtheoT!-$tU)Gws_jMRO`WqgM+SS450jK<&W?J~y_gEy+)^1tmUU`|ZV zpp9dBVReRal`yL)=Dczc>ijsR=IP4TuP`rIl*4?9aX`0?G_7-$Ef%Gj^aQ+AUWK%b z+mzgRS6}4On~t)u5)(4ZQAcys6j!gOD~7?_ujjhHBbp~+BByg6=9|`uanmUsBt9<+ ziM1F*Q%P6vF<K~F@q3P<E3c-z(3z;9fss>}=YC3)F{iiUTvVbWw`d*&cOncMJ=pHA z5{)-*jp8269(b9~SH=nts0UyyDgCS>hng<b-yye1W;1RG#!p?qrG7@;4oQ)UDmTav z4+<9T`#{)?fh6t6LrHRR{DjBg@*^O8e=ux?wi1ao0E)+fu|*<qyp8?yi=xSXDOkDU zqnmODhx5V5j8Qp;TI1MHuwV{rS7yiL0|FF<H+u0$-)>!0n#B-)$W(yJOz_yC<psVB zb9{eUIYF(OPYk;%kI!@ib_9!pRJPct#4CB{=bbkU-w%V2)eSO#HHFZ``*ms`m#M<Q zZ_E&Iu|JCY$i}Ce)X6U<9TOuhl<8&ndz5XwjE;RVpL_7)b0Z__5@pjIN9<wPgbU8% zH!~=Jv0@=@du~(SE9w9N!ztP(E8d6tDt??=aCC(F8ulNe9-5J>CelOoP)ztNTd@ix zjqR5rL|a(5A}QSW0~%nckg3agB*OmkM8DRR5Vy&k#^r`(bg-AcBH3ow75Q?U-;Q2? zbkF#i;vs9`H;l8g?bJfkHLJetW>s3`TNg4R*>=`1Lu$!vQ11gG4@?en*;f2@Qomg? z+B+?Bk?VVYOr?$qtRGE5ZDOb!G@V8QSLBttf*<J*#N2M0+Td=9X-K+6@@PsKN56Q8 z`PnnID6`SUF00Y1M@9F;X<U83wTcweZC$;ng+D|C&kV7+e~o?H!i;Vn+R(H}<ddmB zGXhC6=cTqxK5Ydg+wR`4Ld_p>Wah3n_2VkgocR<lZ~pXX#FsnHUUyf-qQpFUUbRg4 zz7)%mHNP1S1{s5gozBY>z@o1|M3R0uF8g^I<<=_MB3^-c)Oi2&EnFjdO-wD6CB2gS zeS8f*uh=<szD3neVFqumh&#uj8muh}j>;b0>bEVtmU=UQXvELefo2tC;e&+A41rBu z&r8~Ne#S>t2FajTvBUjv`+l+`v_&^OfHjr@B1B@cB(8(AO{}DO+Qd#=Q-A>0Hwg!t z)NMjH8M3q-KOXvYF!XwHjj|e<;{M(Bc4Q1P|5M*um`~<3F0O-*5H<&Ab96`?v~ie0 zY%6p|07WTak$q81`A8P5Q|neTl<QkeyQxp=U`X&;y{VLdoH3hS^L{%C%y(<A;soVr zBY1aID*S!zM?}X}JXOhSVM~$Rp;?2i)^nBz5cyi|wfw8S=(id>RfNk7O3XlbJ>U!i zB_z1d4mh!2H!djY>c-d2BFdtuOirQEN{IR~#w-P(biB@lqUReGGq~3D*TjlCCOgtX zjK7I`dG6MdRzS?dj6syf8)y#8FYNC^Yin<wmSQ$Yx72W#YA#0%NyrC9OZL%{6)GEM z%!W@5ynUQ{AU9?|dt4B<RH|0`&M8K#uU(_9$v`xWH+cM+EPEKM=x|Ov0Bj(=RXocq z&5{uyIo<X^%c`XvBz%=PVLnYuz|)k#n4y{6N3g^n{+q8}du$>kYXmRLxCb0dzS^3J z98$vi?1x~Ny?T@;`)^;Nbpf3WJ;$FnDQfO7tc+siX728I33K;9j*fnv@kGh>Eo%6{ zML=-HLSB#C9pRxU&_JxS#$=prg)^#$Spw&OYnt05w_knBIFYY5_EuY!f6@<L&q&e7 zi!DJ6D8%|4`^>uxwzq)2v+S-7{HP1N+*`GVZEEYqz)=FEY)W#KUv|MVHij-3ot4;l z8j;zlqwsBfT`3ZJwhvR^N^t8K6HX2_kKsQVE=j0%U}v3phbbh8h|9m4t+I4L*^u!8 z2z5lOZ@mmAw*~DG_S9CB&2^EaP@|@Jfs4|%ETQq2j3OUh-l}TH?7n2@X-%PDcm=Ar z-MuqOeL3xDHqFe|ae|Jm?ilT?yS25O*O68j&1M1W8lG))i+@$aYCPkvk+aI+)e<>G zFxv^+Z*Jp~PbqWzmg*s@IIRigUSBT(=rBEOd9|~3|H=`2d;Hsu_XYm8^31`G+oSkc zfK|`w%w|ATzgtq3+;N!jQXpbP&xLYHBaiR*rNY8OD3jP4EaTKb4`x1zLKNL?v?@<U zJ3GQD$Z~DF_0<zYSbQMFBNF+Sv`SabxJTSGg*UF3hbXznsTqNoW18PZ<OlVY096YT zOw;Ny^x|NU+D~1_(&|QEUHLTZTjwgKetBsg!5+0q+g;)X&m@l>9ICU8Gl%=pZEB0I zzRX6C<ej7FJ(g+uQ?ktNt;%~XL!Bg3$QNQk%j%7mucz#XdQ?*<{r=S=L^eedKGJr_ zG_G0`3~h_|9e`dgnN7Tm4oT^P1&}33Z(pjnA{(O2>_kYO1+5T;aD`-W+DhiFjj;Ia z{*BrO7$=_uQx<B^RjYCCGOC@Y(sy30niy<hfQNZKzI2B9s8<e@={dH<r`x0Xct8TJ zkng!0_n*YlXc@zh9#9@`l3ttqX`pOdFYoB%F4)zLBPS?beLY(}>%M=z2r%4wIc(28 zCc{ekWaZ6-{IIPMP$Y?^o>d`$E1gjXE|7`p*w$mhW;V;`uR@y`_wzMNWWOAwTmZ08 zy5O`T47oy<TvT}Mm8x4qy{Es2XSOeEJhaWV*6;f;aH!35DCcu<Zuuo(vxiou+U*qM zSAHWG=e;-HMz*I(={v9O4WRT1raw~l(fs*k-Wo&p9C6wATl`EjAHA&sQ`*QXJ0whb z<)VD7;a3_KF)qS7m9C%Ts{W<12j{T9Q|)zMCC`#{)7Gv$$0@l9i=5c47at{TBt0Mp zqXYByw`nuHy0n3!Ek{T5aK`!~q`vi$I6#42<s!%18;@QJ6QL0Ja)4GSp^~aV=N3`A zRFsQYvf-ArDqM_9FGT{!NiJnc6bv)nm$;4gS!OL!5BeOz@JYPT)`;uRCSadMQ2K1D z=jmd$c)Q$R@EOZ3GnTy2^4n$+B^p1w_JVS%K*mL7ZHv};ycK$VxZS{h1g2l#h|;IO z*6Ara?B7_q`&Le$C<ESusyoI9Dl&+je0N^km7`;8J0<HSmc<%O3%lG!3vgE|SGJJq zo8xh(57qikSRVyH^)+dKrPsDf;8;j6K-2SKcGIK6uC8y|R;eQrk#ecg4mj(J9hj~T z>)NQj&n@;t3fo;qAG4Tg85=3R34RH=C&`L6d1ilaloo^w`2c8)jmUd9p#1vutM1t9 zJV><R+#@zU_jns2K$}7!!0d{@wl#63YOsJy={40pJ_N`3aF44lpa(^bENIrav@LE5 zmGxNna$Z^O1)*JsKuwkYK>4ZCE6eHN`2J<v#S~Mu`~-&X4#4VU!3w?MSzwA$|J$dZ zP=3E`g0o8-{{*mst~j(D<bx{Wr|2E+^y{-&lgks6wkLObPWu|X+eIC$d&4G~<zNX~ zfXvCFYt}k+`Ab7(<0X*@1+9`@r(nbh#7#7<GO0rB#be0sEYo+j&$CTkN2{s)HQ%8& zOQEXWN-tm(D!FVF$s`}1%)CSGDlS|Hd!zOJqmOBSqILm@G*mFZ;n*!E;G6JiOy3I$ z{YpJ7R*KFk+e{-H*1CPM6?KL%z^}b%9hAodN)&a$O!5}0ljjkxE5B8>46nk(IxFI& z%4HXol)n?d!#qgqQ89i0ZY&mzuljs%DxM=vi^1dC6=XRP_$$I`{|7Tu6S3z;lAQ6t zQ-4j;cRCKhz(adM#ZXzonJotux}Efp&@;TnyBj3dnMd*tn_Qu-q99sF?y@_|FAp@@ zxS>HWA8Sy}f&NzGqclM*baG+-BQhwOUY%m~w)~~Uvg3`&Qdu<F)~^feg9@pr0Xo-l z3d^-<=R!(@8THb%&5sCd+7%i8lBgRL^+9Nhv!!={z#lUJ@|$8WuhIrekG+m-Z)Yv0 z*A*^78&(mm`ZOlJH&o+%SZV>$Th({>NqD@+ZW3C2>*^jER~zNzZ6={t;<ClW(hQs) zWa*N`m#GkGN)R8XWc{Y%%b)>q4ztVAw8%+Hv~i0obeB(2;9MlCvz%@|b+}h4%_G?Y zJ6{I?{#G8prN6tF0N;CDo}ybTP?f9cinONNs}Ef&V&m0-xM4fl^6gizU2gRsTka<< zx8RXsnq`<n9Z+bJC<MR8^A&?QbrjEsIJ#AY-b5P?#*;YZZ$IF#x7WJ(Sn2n-Im;7^ zmUXMi^XMa4CU1UcA5h_0@*7~h-c?Vj_Mol<0Hfc#pI@GCHFn;&+=P7IUORI5e?Q%( z#`egtypJ6806BdQYKVF~-)2T?(gv3oXL0~Za@wSk(kDw_Ht5?9)_$J6JyH$yU4odv zFesrhq}F{?p81~Dt^ZwljlT2urbd$Rbai>Uw1U%WN&Yyk+Yn*qr-i1;r&f~vNH^{$ zz=|=(4qk}Fg6%fJwYW2qezpNYmaaUSvz=!r*6PCQg;>fk%0y|mIKDQJ+4_?jf9xW9 z`f;^%u{}K&UnE4Y&pWNp-hdp|EOI=I$kB2Ysc#v;#gqES>vEOYO5Xf)5Qxt~l9-x# z5nZCk0@|{rwCRT#FEx^xm;0|vo%i1vfE+h0>Y<}|8Ccy@vP1#*EIg(=eZ`ifxkjND z^#Z2K^)@X@<!n#r-U{_WB=wjBJA8B&1W%~+FE7!0rS93B$*raV`#a^(X;<liO?NL! z+r3Ck^3N_{vKEgv1F~E-OH(7{=^Ns$TIXaSF$^f~r~c)4kgpAi7Ndf?+R%F`fG;UF zdP3fav}NR!$AmPC0HdU*_J}RtvXv&A0wm32&V}Tag!<d$2U%y~xw8Ux*7b>g(^165 z9)s6QzA)nzyyFcE+Ho#Vf;>49Y220Q9yjO~AAuc>@>A*0&Suv)C#XltN+A4N#iuT+ z@m};Yh*Mix@Rm8T-t|L8?k~zz02+;w)G5}v6?VA6gw|xW4x3deoo4yFQ$uIgpUgiO z2lEex7O4Dlp6It}cDVHCGCRuxB`7*^qdwKaDrw5Nbt;d*3WgcL`_HPJ*0FWGDBQ{H zXcQhCswL{5*o@L~fHO4?{Bi+cR0TcH&KMrLj}<GQpH+tPu^F88AbpT~0BoYspU*cX zn0VAt*LWQFXg#{>_<Y<#IAen??k*J;5jNt7#)G8^KYrexkL-x>8WhET^xjll|29|P zfuVl@p7VKr=6zC#?RmQz*?DK+e?%)itvCdL{_kC>p90nC{DHfXC4lhGe*z(lsq;8^ zge|snIFWzwoYeOP94yd(021<4-2_O4mQ8g?PtbqR6to~ZRzYAUTR^m)@4wI#^5$h6 z4X}UA!fDT!1Au0-z_O(O(+m_3Po$-B6({PCT@}(|RwWS68sG*25&ADXid4o)peBwz zHFV!!#83X>AHRTkP_%T5A_D%%+@vDB{g1mX(r-9ofAG50xQ~CIp8UYc`vYO6I)LK- zPvwXS@laB`L2<+Xak{ppfbbNktkVr_9U#8;zmOvGRG=+tYA*rar<NQr+<Sz7?ub={ zlk5OiQy2(&iTwZWz-W<cM*1rclv<1S?~-E)@i1BlGLW_YHn~3Lzpwo!#6xeX#>9>O zdrb)E-`9LJQD9mk)llaDw4Y?;ptujv0UlsGi~T<i@E_qKx2$xd#{4l(IL?{a1=cSc zSU>*%GNwpHq(OsDRV2oPYN^~tJ^NF?zOt%`4Im<T4J<<Nzwc<NVZeR*^HhAK+{su0 zZHR$x0DJO(TCN3%u@(I(Tu;*_zzt}^3^d{S50ihvaO5p;wpf3ijKoVpasXcU03O2s zZI=qPLjW*HNr;F`C^Jm;!3GE;hu;1Y9P0ElBJ2t#g@Nq9%ByGs*vy)nqz8Ndg5?F= z{Cb<!k3icYW<vJ~!d8H<I^Bf&0jN8`koUA5zuyAeR##C|_kt}jG~+!sGxBd0LLEaz z=q{i~ks;%dmiw8`Kto^-Nw_mM?E~`n??m($mj2Hg{Xc(&)H4!1q!w&jEdM{%A3}ny z{Z~f`0;~VuElHmGK8OX^VwekTgMZM=)IHvR_jnH}9$E`CA8s}n;9qd&(V~xXAMkMt zLj?iB`;YcvH2vn}%<#?D+>S|JLRCauL{+3p1Gw9q6Ww>EhPXBqREM0Q3jrg=w)&F& z)BIpqB7%?O*YK28#e5lM2Z)D}YkcLHBJQ#xBhZA^#WwFB<*_+wc`zJ{9~m@LW+?F* zeR$V^fMa@63Z|~D+l}4imeRY-a$eFE9I|^)LDGmJ))}q%+8}HiNG(|j_dEoQ04y5l zG&#(|!dn9QG{jCNNT_iOo}>OXP%d(#XSq?-%y=QxYI(EP+3w?0gCY)Osbt@qX^%<R zskc+HvhS{~f-}`!Z3zZi)^8aV_hFf1naR6=^DxulBwkoCd*mb+X>i7Y>I?B<3_q?# zzbXri_B%MOvQ{&mBgzVO&_ZHy57i6WC^qdvWUW_zT=k&1vSdS}K2F|;;Q&!ht7&vY zCmIoVQ&4C+`TVd>3PDgLX$-PMd!r^k&xZb1$Efn422%4<PLD}D$tUd;{^1{RZJc<3 za?KICOh9F;l(1YxHL0)^#*`ENEl-f9NWao{vX>vVucv@2;fF3}K4CdlCsC!t+lGDe z!5(c*NCo_~w*8xV?l?UYq*o>qKh8%9*!710mXepoh0c&soDUUFJQ9^=KFmVhK1IZ< zN%)ra+j)CXu<2?b43T%y)B!E`5f~vL%C>rju?ExM!~ONpn=@OVzIcXzj&@iJKJ}TI zQ8~Eni-T<N!NkwhwV@L5d6sB4O`l*T<)HVhK{*_$4fO**=YARdBhu>BvIMm<DEEE_ z+84{*kOYCe+rr$~=KDjxMxBpYVu7mbF{px{U%feXFAt0zrG7B#i6X5JRsZS-pe^=# zF)Fm4RVkNngS-#PKI3YcD{nt8DKjg|J-Y@N!D&8sF!B`k9#oc;#<!hp9201LSo5aK z)x4I{bNLXI!qyuX8Vg|1LSC3XwzKgkw&(K1l9A#rag=%z)?@YTiMj4f5iI#X*HVbc zc~C(C_?e(b-Kcq=uL<^Ezqlg;)D6{aJ-}xcge@||#mQWR_inc9VSI8tgW^UNlUW-U zgi*-97%j+hiqlC4*YUYGyF`t)RJ#c6m~|z$G?p;=RRc`f7;O;doc&O7tN;>;NtY%{ zQIwP>)bq_YN<r#WZw(@@U{0|2H~h_v2v*YC_5|i99txHXIdz<-E9klaG(Txuyt599 zA-OF^25DCZsKZDgc@v!SH_71W%?QDI^Llkwx8<jFdLtymI82y$V!us1AYHMSQy=vz z`$G(h48#SBD1yI)WP+<brW_@g&B)j4J~WSQro-lM<`woGgNb+uzxN8BkpgA?oYazG zicz{tl5=GiB~8KMaXP^PT@>>5(9Cjb;NcT{muaVdnP8?bV25n}6VNxlesxF-d##rI z7^rXD#2aKM;H$gtCsWIXA(hp0H{~*qC@)Z=nJh|#C}FN*{#&B0$n#n6Id#hjhha(b z+&7WjH{}6`pL^ky3w$$0NRyM=Z0ieLAj8!a(c_$IDRoD3;VFdy?bP2_ixDi@J8)YJ zZRdo&M1cWL2`F^I5u(L6V^p@&<xA?H=Gvbz!p*>1;=g>;dF#?KuRKj>Y0aY<o?Tv{ zWrE>95&t09RQ1HQ2`qMg98om5Yx`&o_l9o`*G0>9b5Fc|yNdlGUngnmuVR9IxI|2T zRl6U@F80yTOrt3RXv($r*7&6J8RAN7SCTW%_A7JFMIHp%(=n_HF$AJNLH!Vo2iUDE z&gxR*Cf<)_nYH;?L$_}T3lUVp^f$H_Li#B-uDji#pK>xz-Z3mCHdJ2rDXkcjIw{Wa z*TMw~%_;a)T&2M6szuBl=ag)UZ+p8DbS`l@bq4K#3{dI~012T^*MghKXl{@YS!at} zzzKJwUP;zR30uzsgd>55b_*ZbHiw)FQ46lCjGOmBTSQe4*p*=2?)TE1-O29cJ(OAW z@39~{0<Vu-9nU>FhF)tgfXUZ5s+ERc;CGG*U~b6OLAt@rfsnd&lnX~kr;gqpENwUj z2k(0Z{~CJ$=)m{=pDj_&$Kw~ez+XDR1Gu;4e+JzD7zH$>BqZb@`&eKHnBgI3^-5+} z5m^!Sa1C;u=|~zI;Bwdv3yCMhY<Hdog>uL6w%r!`RmLCu`mMfWU4=OWX`Oqw{a`Qg zE*hdgTsz{xxB0+C<RHpzvJH;Cr?iAE^F)3Buj%E<BEKCc&=n2fLG~XmrPj9KFas28 z)j$$FZ__*|$|O9(c|6B>(mdP3(>&CP!3z7o`=S2d!S!$bMu<}TKLIlVj+P+A|B=~$ z)X&+@-oV<-)y$gF8K^5TO1}&C-+2g-FGI_NfJEnkfB<m*|CuODh$$$GH+GHGSBy7v zuGUzf0QTeyaEYO$ppml?{ki(R9v*^I)+BAOybAVJEvCAbV_sNscqX~zjw=^lKU^~# z<-A6I3O&}JqGAo-e2k18Afn~tUckJrI*FH@EqJr0?_EXN8e;?KUHHP|W{?QFM3@gh z$^m`ow%Xmj*ltWl@=GdD?vt9xYPyW7{;rxw#N4i!l1s|_luuxkYj#rb@8Au;VH(A> zLN4L3nbf^>vetdc>np5STQuVp9tg!#Cr}9@$oRN8G6>gh;cbc0!2x+o6CbCkyzsvR zE^vJCiz>8vNi+aEb;ya=UG)gsedLEU#LnTO)iOgzoRc<&s2^PgjTZyW&i8f+%3(?C z$w?Kfu+;dVt6xWuVVq@-U{3|dDN@X&<sYY!{qf)fwn4f`rogG_N2tUnLB%hi$KN^m zGc|cTxgH1(ttU~!&8^lz*r57^i$rW(@Syu|p5<SwbAkX0?o~!HTlTeKCiIsz(w=4U zL*_^$D$G~I89oeqs0@<{lc(<B{?IYq2Wn<X!(!)8-_!*6Sk*|#9#h0U-nu#59DUY! zjO(gZ;J35yAcvPL4)eEy=R7l^q&xRQ8i<yWCa+)?JIa5d{<w$1w&0ZgAxO}RCSpb@ z<VWc`+p-J52~Fz-SBSmO3wxdtRK0;gyj!C@&mU;UKt$;`e0Q#S(>iMM!<bGkTY^iu zU&@<Pb_^sw#MA6rv`G&3{Rk>WwReK*>TBvnq~2fVx58ZvPlfK<rY6;FSz9z4w~oba zBQb>V2)yX$cP!6C+7yJ(d;~JCpuM0Madq6p@~!|<jC?+mK!5-P8|oDc`|{8glnDd& zxz(hbNKjth4@=15oqnW#3BE+DY~<@j)1NUqG1-zA-Dg|TC+IrT(H6IQ(zZIf+`m1< zNYsc9FuKqKzkA3ybCSP!ph_nuc0v}H67?$Cs4H?zxZ;{BaNVuOk!k;)sng^i=e7j! zYZn6;{8Ur=v6o@HCb^0wTsZkIy8${pr-WGUzyFO(g}}SAR(FGkJT3-8(*`=sGxZL9 z!>MCds}HUPKAU5oRRwv-0Li0LZ|0k18m7?WcKwcp#<mM1C?Aab_~wP0l{DBZb2Y{q zQ^1*+1I;*FTpOo{@>doaeoskK;$gTt0T~OxH<>(Th9@cN7t`(?V&<^k9Y4Ue>(=9I z&3(i2Rs`r8UNaVp(S_*JG{ZPq&3w#ph*tkts>tYWl|J+_h^^Gg);!_rH=Lk*ERWd> z1f=BQicKw!E;3eS$b~adY-@$MS-g`VsaNDAoUa#L*tZz3Q^#MD7FSk?heI(=oXIc% znk^*k`Aoa=U~dT_w7awS8<Aw=6)<8uNtYVLWn0Z&N+vd8MFes9#05R0A{;y+DDWK( z!=fDyIV*y7NE~Dfw!e$kbD`yYK!5jMd}VpS#&YU?lKBDI5I00B1*P6B(0kuZ&%T1+ z<0Svw$*U6=u%kD~G}|1pQG1SWCjsaOFxHeil#%Yn)y;ixKJc}2gvmoC=}IaumObmg zVp6)qDQgySc*AqX<20DA<W!?7pz1-Up*bKC9w!mNPiq}Ib2RUesOLETA{mXJSh44S z$hB~O_JCrP^W5>{n$yAnv+Bov<!2DG`;K6%j5QF=s>moWR%~)@UQ0F`Xo!zuK%d_A zlUR!_4|Gm^$GiWMZuE0{yxi$iRIT&%j@JNV%zht4o;U)Z*{RzvQ-rM?&SxEvEiR%% z{r)N{q{}&}MCy3FfNz@(W1PcZKB)wOD=CrK-ow;5c?1R!5#AG>Jm=ChUuyL|zX>`J z!0V8wMyP5yS2%(_q)TM!D%5)*1D0$^?WC|l0fRpGH{rg9hc}F5&+uDJB4+MTJWiWZ z^Jm&8;VJtRye)>B162s{1vzyuOjlerG#AC<7-{raiYC|L(iap}O2-`me!b`d&{}F_ z8pG^URXrkgfVBrC6rxzB{ko-SWqvDU*A*GwF8KU7aR}co$EQ|=2LkLMK%H|})i9Vp zQSrN*Kk-AzOPXzhE?HbCj^nbO#$w;M_V%H8-!Q~MAD)|yq5LV|?m#T(z{HOL9eFeT zwwY!}e%A5KFbx8FZ7yOA4$PXl?|QK?i`4d6u}vPW?oJmgTb}~wkV<Z`cr6ERepiyr z>Ee*f|MHXMib6~4EAoGw2Yh}RZ6yf8zNvm$Lvn4Gszk>r^o97c$>_0NBek>w$C{?| zLfNt8-VePbpcA!Arn?<U4;k6onPYF&lMegk4Ic0Kkxi?E%tEMR^!PHyHZ<8JXRb<q z9|N{FwINFcYXjO*a4clc$bUDK=@sr5T@?Rbf~5Z(&jl;x3f%;?HUJ!Lx%1$OxU*u! z&+UfN4Dpu5@9m6aJ68CeXtUavLR(EYH(e&P2IJJB-tH^xU=e%NSRdljT7&FOwYWxK z^>r`;7z&a9DNLK&{Tt7r18FV>H3sR(XJort<=PFxp|7(bpmaQGzFWpi3?^u(x(bNo zfW}1r%WSvFJkH3#698eP)d%ggS0ZKqP}c=gI1kyYh~04e{dLSD&%;>?%ZcQ{bOlyU z-sExJ;;sN(sU-1P>DO8<PcvQmpsZ^+XHwB0g9RovjaCsNJ|QPPwR$JbzZ<Hz1oGGj zBGp;n%;#IjSnAsGM6TWYbALixy9DXTWIR9n^$h#o4eWh@X983g?#HrRvLnBIuH(bV zogW+ViCA2EPvERdE34Yzi65a}E;Bztfbl(M0vLtf-Cs0keW#o{+2f3k<aW@9$;DB( z;6yK&&_k9=C^GhQq!Y^Kg#L-6@?rduKd#VP00^Hj&7N0#(n#B3^CyYrUyCAjkB-{^ zl2xK^vT4}(ga^R)-;qG6OrI^@zKO)}ja0D?-0|e+Oylk-6i6v8cPPHzdm_!I{nYCB zIH#P>{Oz6tg{1D;rl()%`z4w)Vx>{oF)#K6Ukf4|?vG0CdJBnO5c8URJ;Q5T`7h?D z>7nsN`X3m-Vh0B-uskE%-nac3pC_|tqUs?(R}RdH@UH^ey01|*EZLfb4%n=<@y-HW zyY}7sDy9b2H@A71uRT+GvWperr@#2;{4(cyo>SzRc#^Br>I1uP?4!BZIr{Beig->S z!Xk%g%4}9@4%TqR-Q|&O{OyFA1?>sNTd4-&;8G*-P0Abav)wLqZHLO=MZFybahsgo zI=8T3V<-*)h*lfw7`)-cc(`Lkk8J)KH)5ySqaA$lyd<06Y!)Xt7j0F}yySdwjsolS zh4-b`VUsI7OW)W_(tUUIrQ$qv^qKhuug&#6126Q;!^=6_8;u`Qa39fE0MWoW*vgp_ zr?eQawRFfl{$St(T6}_4gj!XxSpT)}A(yuXLe&RA^hzeNCtS->9|T3v`BJ)|TDJis zV>O_tninT>Wkbs~8qa1*-u_UJu%fR`C>^14mJf{|D15%eCO${%0F9SGI~1U`6_F~I z8PDQ!dR6uaF{cpE?=Pfkwv_nhajxYvsz?B_K`^8jA78v!EU--})OrF%L^iX<S+~W4 zNV5&l7NEOj0*+$u=JE<Bse?Nuao{FsM-}BW`708WuBF4c+^Ji<_S_6{&LNqTBwT|Q z<v)oYxB^SsNE_TuFn?`LK`BrXwagw6P(ce76YpSreUQt8cd328{WM(b7Rc1Ujj*}> zE>l7h0V-0>W^|FlETDxm9KhG2l4%3^Ku8Jb&8vI0rz50%jYsfRHTdm6-HvtqU3Xa7 zP~u*WDfKP8`7>!&*#x}Zh|PiMX_LJ}ofR?N{1}3qchcDJKA%JgC;Xt=Ngpi=719D_ zA-e*@$f$AjSZUc|Eor0gJ$l5QIu!o>LJ|4hL(}Q~J||BS<n|rW)y(#Sw`cq5Ho*Dk zNZ~GId(lk6c}v^WSy{cxiX7aa;LEEJT%m+slTWtiPGR;pZCq8Su~(Fm`bt?m$rI=j zO?2jE2BIwg=LXnpm@UtjGB<{|=3z1c`<!=cjS$ZO-d2{0Czz=QV#yR;BJx~NAcN+| zK_+b!p{cCH5)SIyj`zqwZnoJ`gmx!@gP^@c`qOCiJxFc5H25j?&9IrdMt#bOFN1}B zR5cCS(-l-V14_!lJuLnFiG|a9Oan{vJU92=R8$=C-D~m_6_$R=t|zC}PHZDWi1*@l z1P9$zPdg4TU9*=tCxL$vDb)homHQguOJY-#s2P#yVSQ984nZ}Q$9??sZ=j5UhJL)@ zxaSPSIU!N?z~Lr54d2p8IJcdM4w~zYddRc>s%7R<{HXw3^Fv6l@2Xu~vsIye$P-Uv z9#)_Idtdx@f~oGaPAO723Rgag-x^-%Q$atG9|KxWB8N$TEmGD`1Ohdiy{Dctd7DRg zCV3+xf;=`kW-x3KDwxo9u+LNgiZuhoiOv55Q$Vc0<U2?;1_X6;e*>e$hrRxWe@_;A zt2~p{d`wYSxfnwW7DsYNOGLZHmtjq`g^3r*c>?{GdsL!7to1MIFN*TZH?BteP?o1P z{3bGZ3Q?2ooUKWHqZsWXZg;TRb0xI3+xTwJ^oSi~8(bCrBO3i#BTvhhtR2lcQh#uF zG_g~;nd6;HyxZgGevc+lXCgEMBNikNqygMEFhqRRBmbb*%Nw)T3)dx}SX6Y-w(i&1 zNVx(%O}e+uT{jW@VLWYbl4IXXdeys6xA+^j^OLZ6Ry)bP6)Z0xAt{Q6VjueG4$*fa zf{IYQ@a63|$nzoZk%@WK^nTEd-+$SHvxc?$85>Gw4d(QYcF(q+)!^2<^Pc5e_w76q zup@gh!k@t=F)MI`V&FT8_y_#KejW!{w_)^b<9Tc?6>5;V2g6fm&G0^F7zXr5<&rac zJopoHKclPB-}f6B{0N$V@O!t7!``WvK6!~l92g!HAWU84HYKe%#$BBp?0^2Uo**}w zHl?&wRIkyQk<bnLnXdjpf9ILAA%Z8sa!cimh>>7x7bs@)y{3Z}FY1;cqf<K-G-apV z2E#szP-WOpReSs9m=3}M;*#SmcqU|;Pu>yRmxI#*s0$mu3lQpzOvp;dMB_FewTr*F z?$AGHeFHsRkF-_leSA^u0)J(NEy4)&_7>B`Z9ofh8tT-<&-H1I#UTDsJ^Ty%UNRf7 zb1!@4_AY4ncu>Jm(|jw$(^ERpXQ63JDAVi=T?76~rV#MmjsAuGQo~{6?l7u1X&1yX zc-O+D>OzMH)_@LBSMX3TX?>kACYfnr1Ht%5qWTv)ckheyes~y5IDZ}B&?nq$p%ZpF z_>0!1m4Bl4_&Bw^h28E`CXC=-*bq4OlQg=p>t3s6;U3U6jz4OY6^5?95WP;1!qrG) zp<4!8sOMXjdv)(+)dr#<@;&MMi~LSn>1}5QP|-D)Z5pt;*$u>*)s&XJ#O_l2;z>qN zN5FO_Uf7@@_0xvuB7bjd|2bO%uH_9G#V8_&1C`LnLdBIceU5SqiUr2^j*|kxk>^<G zw>)xn6YbAL9wl~rxz0$IQCz@LxxxT2ej5&Qc)b<aMW}hhJ|c-S9JI~9AitMWmo01> zeS)W9ay;&xC?m`(f6nw1)2ZAMu`A<xJETY}PW}cXsdrlQgn#@|-&?iK0v5;aZJR&S zC*L(B?sz@XM}ydzSCouESGa0mMlke-`losQ4SIEPwc9>p$AKqnbT*%x=1xlR=@1sM zxaS>QM_hHN?(!{V&e3+O)7qm84ZZuJC-7h0zW*O3{u}>ypSo|?-0~%-G!PH-i<@9b z?jD!9DY|#6yMOQ%0f_m0V2#}zNrO8>Cx5>|i4UIevCdx-`0TPTtQ)4+NKDVx)}-rb zcez&7c7kUP)hBgLfVh5)f=JA6YYGVeua|JbAnjc*e?FJW7y_Xyvjxp7(cu>&MlV!3 zcWZ)aI_WOgukm&v>H%uI4TOR34)^D^@-TM&<+;;4l7Iez1}C*S;4q&Tlsx5?Wp`C1 zE;7*>3{#9-Umr{8!6?Z8aiw>!)>jO_$K3wJA3MdrS=fE`K5~8SIv9&Ul!}0ocT>Jg zcaOy7qr~|g|C`8m*-RvMlZ07pX?`H@uo^8&g4PcgM3)IqR64?=K(!aS$ML+o<{uXN zkk{E|>whEabT|rO={Xxz5AQ+|;W?fFDPoZ|u21};Zd}pYAovF@cv|KUef*mJ-T-Z{ z`m%J&54LMm*2x$!SvS4<z>G22nLd(&B04J88o~ZWWPg|VE3VJaxhptZ0F=V|tv=8W z#kD>q`{b6Z4(FX<r^=XOZF;vu1bwv5|3#_)u7CBe0Hhn%^GqL#^1c#4U)YWS#G$HF zxci$NpowYUd&}6P%XQ_!1ckjztPPuQA{qtIGw2*)7K}b$InyXk!A<a8h&;)WcQke0 zE)XuRry|}U(8qB<P}gju>)YW`{CW;+a(>PjF2-hQUgX(X{W(J%?ift818r{c($j_N z7=M}fODR0U&qpr*fPS<~gRl<Qyrc9NCvL0Zxd{R;dwHw5BF5ZZ9Ji1iR#E&l0vnFQ z<am1>;u4xM_nMCM2F9WHfc$rbD1EVj%z-4I*@>Wg^2j>Nu0m(a?bLUeb!kss%VyG| zW4Kwd)i1{Sewcq@Ka1TL^gQd-nN5p04}Y=y{&qRsC_{--3_6+PiCPGx7_FQd(pA_P z@#BWO@Hs<rr(46BrA4DsTXY#Hd&oU$0?*P4GXS6plz@}H0$=5B4L%<o@4ukms`J6N z5ZQhuWiHP&6bzlwUSJWa;Fw+5AM3pIvg^q#jy<?49s1c6dz4`EU&Y-2c0_wZet)YW zUAeANB!a4PDG=jcIvGGE$_S2fTYa^&gvPJY@pg?ucqQ*2ZKfyO-$wrb<Hr9i@atd3 zmoV&2TC>OTu)o8eFS%YbhXF(a>C!YFaGQ~R3%e?Q3^m?*$Q}QeMgE}wP^tPaeaX`z zZ>QnVl?2*G?kavyZm+u%u5x7SU4KgSzU#}x0q^oLGihaa;#L=o{>yPZt?^H3=Gor_ zUS4%OUl@u&1qsX|chF*{d}|z>X%}<H&><5|=E_i=R<ngaTB6^Virj}^dv^6^u&vJx zMpZh;3PO1EVL#pxY2oWT5OmT?EeFcEA!&m=27~@p!=BfvhR2}Lw+|yd+ke_^?X#%_ zXLfNqsKO$&g)p`_jm<$z?u;gjht)V+bc2ImoW{5H>b4rw*M0(|@>{qf4&bU=f?G8% zNH2F3liEE{%b3|E@lh+LaIf4}Vg^AUwbAbj{VwRdEVM>CHG#bH<O8SZNTN`Aosi%L z8lIT6&Y8>yp7kw|uas_;Ab;_{)4SD$4QU$|Vfj+Tyo0e>7*PcxODUGgfxOijrY!y% zO2T%R$U3MS_kuprQM@5R;xlS~U+cqSBQPqgJ3}4O$(Xw?%Ft%TPo0B9jv4J*l6O3N z#D(bP>Cu|{XzKn-3jf;{`Uf;~SDs%vZzE^|gbkX+CD^5wpgz!!%zu1!2fTr7df#&- zU@qtVUhVutwCXYPL681m=hbtE1jyL4xOY_6weJ{n@V8~9oT<G<_dHmdVS!Q|RYhnv zo~Fk@$$O>hH>O+HLHa@^&h33C*}_Rrz;^KZ?M#gWV&=AY-YP?k>q>^o-Pq&!nz})s z1&-gaA0_nd8i`GwK7ZXNnkCs1DW%(e-RlWy8;J=;hcj>D5x(EMK;FQg-Ti0O)JyZU zU!2~JpZC2>FUq;8hjuVEw>Zj^$is%sEW6`bJ-TK}iGUfAYbLa^LLC3dz<;msa0h+* zQV%@#FeT)uT1kpPk{O1k1Bw`@zSSriC@Kk+3n5LLdl;<gCV%=}94oDAL;rmuh}DyE z5wRw{9{eLoN{OA<4oNh@Nxn>ys&O1^^qqQz7jC8H)VsO-f&Qn!?L4MdUWd~9{xqUT zL2L5sBuQ0FIa{r&7hKuJ)vR;pd&uzjI4a>je#}*Vmq{4M&Ra>>GNP{aipXyxBW`4C z!Nq~gy2Yco=zk0~4m_gwuIL~m<5rn`bSwVgmVLX_Rr=~f1}UQ#B{_*+Q1zLd@!Gv^ zWxPdZdE9aKq*)QmHa4-VZmXrh-xcLSGP|Yg!{Rw+4QMWkxhgUZ4C~k}0vA(Qf(klm z@U{bdp|?83+utC>-=Ne-Md?4tkD2(_0X@M*$i{85aes#FG(f~Htz|9(F(RV@nldPY z#EcrUWYq@$$VPwT#;dFIUoaE$%JXMS39h5s&?2g`x+KwnrR?<c4plfxsQ_;_A#H;Y zA63!cppWUO*RYMi8V7|XSuTxz4{UjyH9d$zrV3GdXUqiKx14|iri`v4puUS>^+Iu4 zK6|2Te}4@Ly#}}97S)(=vV!Q8`ewFO3)ZZ6HXB?+_0EqBvF6M0=YZ62+#>CsB>-jn zy2+x(DrEeG>`+G}XH$_%(%nZaJP2Zb5r~sCDs8-h5#(c-{Ws|EhW_hzJFKs@;xH7G z4zd)lsP}ehISb_{Ss-io*b_=YgqNv@t<}SU-+xWI4f_|SLL$>O+%s_Nm%U(P`Z*xn z&UY;(E1lap$mMM_bLZK(Noxj%e&jK~LDyVksQaqQyWfN8&*|6ogq}d6sa+;6tpWp` z2n#Sh2m?#HK~YZ0my;{HIA{tR6#YmJ{@@SZ%0>LJdsd1T1sK<{d}OdqxlX|oTHA+i ztA9Fj6DVVUKujKhK~y$edP7p=dpYel@@i_ev-+ZEhfMk8jxa>2twfPL9(PU6glqVK zz{~Ma4CN85V6_UejMFw0^3m-0jXRW&6MQ{Cw71|L(0M)6!)Jf$C~{vQf<#LJTbc@E zmbIdR=jy20kBNNhqfPY(xqT@Ny9XzXsed8I0@4$<bEGKBEc;Ohh&3O0N^dnbTP^Sj zy6%5giZN~b!OdT<q!cEgNy`KY(wt1%d`i5=FiZmEafvl)KGXH~x(Xwq3V(y3AKk{^ z$nO;jE4U6BE~NeukSQSsk%Om=kfEO40zdl%9jBRvj+MTo-Kq{Bqg<o^mH%jTGk@M| zF9mhat`N<`yW2jr=?SPR&b|V7(=qC;H!s^t+h897=>h7wch`N^0>pqqHoI9fJo>m- zg&uri5|ne={79PuFt+#TlAH_ZI<Fy!K;EtRff=VScSzK**$sCEaF~N~88oHWjbaa% z$6b9IMlE3&{^o4AvZ;fQ&cFwAet#eqVe1cV>OM<!f^6P6pPGvVRpmarMOi*EdMPxL zeNnjmm2{U(_Di)vrw#Vm_<O*v=Cu;lw@${O#ep==#kglkdFrzkWPQ+#51E%<Qq1>8 z4jt+1S>(-X)mGx;rq2WT59(*DIRHke@XvD<#qh!98m85O5U~ABjO9cDtAE+7Zz(jc z$yv97VeD_;Xbj7sYQJfKaUVKKJsO-4%Ce#8Jea_(yXNq^>QcsuhglVXfqiAS+8QDL z8tosv-#`9OKllGIb$4n=6*3PIlXPgCWlD}l8jVOZCBa31gu}6Eq$Tl^hwdXUXun!y ze?Ix!R+$4_Vqz%YA=!XuC4XGt^Bi5a{p4k(>IP=Ov2b5CeCWp;oFM)M+b^CyZJKsH zysy`IwbQvopl7oo(?h8;U24+-p4Rys6&J6%ENf~;ApZjF25$YDjgbr9s*)+*b+*`@ zwg$GnQe8Tp^<g&xejOZrd$nxi;!4v7$NqZpZ`dC*_vSPbhZ3fz@_%$m-Gpb$tRLOe zxeP}AJUYN#1oA%DsfJt40{a(H|M{7}Y}}tgHYew^9czO*EChK>6N<L7ZQl%SVP!f$ zFEqLsJuyA3?yrCRhX0^YQ@bV}FAKo8-n>YA@YIyc{Q#q`*qydCMzV4P&EXc$;TU)B zQx>n9g2L3l;3wf~+J9Z2`p@n?ZUj>!Coq<Wy__n+fLJjAjsbNuh_W?K-r08vtWCn> zeuI+azrep=iv0hOGk$~Z!iPUfU%F_l9Kc+j%OJ+yrJH^hO!WW{CRSv7IomO}dE02b zChltcw!t9$U(WPX-br`#iI|j+vZ?}TME3C=p#`j&6hFofB7evz!CAQm`?1y@!-4;z z<-h*&HJk)}+qTsSV@i5=g^otHwIRy2!T!(*(Rg6`vn*>+S3B<N`@w%x$=^#nG{<>Y z{D2(lL$R#7;ckoEGxj=@pUp!M^rStJKBgS%ITY<Kh^7q+{VzVG4TH4%ScYngX6miM z$us?C;7)aJynijRcTLA7E!A1hL+Jvc0tWHpzTV%<uYZ>b+dhrH-UAHA%T5buvj&oT zW%&_EN#^}ruv{)h%S!fS=}==~w=<V&^S1^2f%<WG7`h?bmcqvEJRh&eU7ocDG6_L# z2WRAxMBo6GzzuGKZfiZJ9KXV@5(ppDL1FRFcu9fy7JonQ$2Iq$XO!-J<dUk;j)DUU z$3~yq;VeQ$4uw=3m;ynJ_zqmX_HzekztuUYK&}nA^mTXH<4_XIM}H->bWdG7>PVhC zbt|rEFp9!oG5>p3`Q8Hgs@YpaJPa$3opyH_J&Q%5hzF^=D&x&#N}vb=G;=Vk;V0lR zJNZ8^@PBcD+C>!z2`eJKSBYttQa4dGjaWyb0SQS@ZF@+rp=``ftGW>&^*^nUK5jfc z>UHl$h-2fyyIkI%ukI~m^HYS&y{s6Z0C@hsDrR;<`EKNPP~L4w5QYAyh4PiSe6*3C zJ@HZlyIpfKd+)YWFD{ryE^sJBnrvMd%_VzpMt{8lTrqlB0~G=NkFs0;xAW=V%0V}r z$tin%uJ<s$Kkvsf7A)`rqTS)#rI~qMZ2Kx6j%!Lv;@JPRN?#TE=U^zaCLHC#^$TPd zu1On>V8@KZo7;}A$H4`l0}S3qgo$ipzk$jBX_5b;4zKa=hbF|jQDmqn($y9&3Qqi` zsehtS*&mEmm0KN_!{gaJcX0#KD{F$Wudp9-fU~QTT{*M@#@r0m6U=OnoR-{tJf!Sn zqI9l*6|ky%v;)vJ`z5}@ew!gaPo8w^e2|8C!Ao(o9Hc;Cw30+jf{}*9#b~7C{>0(b zuU0(#T@Ib!ru9piNgitg-&4GgaPeXdUVm9P92F@<*o8+iL=RaV>Zj(^Q{42>4+#E> z+avJ*3Fq*i91=Y0*lk-GoiOxco~GMV!Nod7hF2RwCbLoivu#FR*1HWw5Y!K#I10FJ z@eGWUD(xb$j)CZ`GvyFfSprIaSk_lWLnpdXn+Oo>#*-eqg$WY<3Hcc{b}frc7JoGI zZmgxMC5@&q?)M}o+xHuDgzIatfax+Yt^aGx0r|e)M}qp&<d%+Or=S^?9B_-eA?i{U zkn6H%4{Q&GP4Aa`NC#m~trQpkXWr}$bb6KS74^Fr0mP#~-Oi{had-K2tuQqzt_o{c zxNI!vWS3l++bUTIN6GJ?pOT$}#eYgp8EEW$T@$w+h<UkejrYLfPJ&nPC9K%v{pN=Y z`lxoJKO;Y*hS@O!+A6D<$XpfbTrV!}9n^zlT#qnhVy96fc`Vg{6Zk02ljJ9ATYbIK z5wY0;P@-5Gv*lt#DVsP`))>Lnu_{d5v6H<8voj;DESY*^vHzfc1RHOl0)IqJqw3_R z_Q7wi%&@GX=BNy6QtCUWfVmUu-zj|6Tl`bCR?y!+o*Q?K7e=m6Cp#6rw@%-d9t~lk zMUKG<J*sgM7o&D;_k#1NG@{6--L9zrJ%^YE<<~14g4A4gXWKKIqk9R=nLZ?GbC6DZ z*~|twyU58lTyOvjd13R<g@3qb%*SQz=c=W&4=04{r+a3g$x?W3SN02hfkkt?_%X`> zeAY6<%u1HEk{KBOhWO@OulMtU%knnJsnxLEL*l`EliHt-TRw|H36awL7=)(j&*TPy z@NbAOe9Pjkg%-*wmG@GnZ&BdT3{H)1d3-sYhTXBKYvv+f<oeL9On(+es87VlA8(Up z1_B;^?l|_MVNG;qXQ-VG)u0Xi7LTI%GAtRt5Rnb!)rKGu@XJ0|=Fqxj{Nn%JLhElq zV)^osv|C_X34(LeudYAe(s2tJ@qB?i966nWHBBL@FDLZ(O4r+d8;nvA(d>Lo=g15n zl^xg}8DY8McN-mz!+&+ZC&^{YTc?%5L0@41ySh2_9g4qgvF$1FMDZvXYsysDJ89b{ zS)5`tjn{^NW5ND-RU*9Q5@E9*6HLCIRMEwcZ~vzD4Xk3)wc}L14wpVwKqFO}>PB%E zsXOi=-IBVVo9b4L>BoiU@3>!|pb56B5vF8N-&&!QcYY?{2!E7TbBgUah6NX|C-0(k zX$0$6Oo4ttetEv55BU*9N~*W+0ajJWY*n?cR_J(j>A5NyS>_Y7N^HB@VE?p2-XJ44 z&pr2L{1uEL>0X0lUoiWE$0q84bPm7^x^|VwtAZXO-^bK-jc(05R{6f3+CPnjuP;D> z{w|gD2vz{^5`XE19S`$-FIvtr@`YCBx~UVi>3BnXw3^VLfWtg`8<)~<t+(q9!VMdw zX(~F>AiT;J)`sRz9gnxrF&aTV(Wd}h_xzTHJ|P!wxvWfmc)4JLE)Ec=q93&g9-l4X z-lt+c&-~#i!s%@bL2Mi?-EmHZ4fRj;_Y3yW<?q-vWq&spD;2r^7>4A@w+|tEa4bJa z^y$b3C%gs7JWl8FC~si=XNi0~K@wY0nSu3{jdI;budJQ+5OTVx%Y{l)-Pho&wmqgW z%uK?nZ}6KXKOz5~svFuD@6#ZmeknYIKOP+g+^wgqAaq4Ga&vbu$x_V&ePZkoTiTT! zpvaHleSdWe%k=)TbJ4o@M01*#T%>uR0nOzEHT2>y($zXgf)pHAieuhvW8pR!0{x_Y zh1=d<o4yh_g)os7(5&6#qrlHh)@z3?XN1hGc0$OjB9HrtUC`DBf<ct}3HfuJnB?qz z+sJZxDKI3k?ZIl$!%;(>pkC5a2w6xzFr2~N9)DvDD1rP0{oiw*|H1rWCMC9(t3n4} z%Pps!h2ula?KC;N3gdiZVfvg_YGERMdBz_#3zYZ){qev;8l|t2UVLij2#e3}i%F~P z(eEJ9K2vOzUJk`FfmhoN+3SV9t+X5kLE<NIyiGEoP)894WxIXb!25u>`bZNpJC_Z~ z`hRp9tp+ZR+2kgXdE3A+fnq<vzTbTS4+xu3;$gVPZKB3UTg$N%3?D|wlHlIXp}Pae zB7WmjdV_)3PlEnzaa1YW+*NIKY6wp1Ebh)b-FFXIq7wDaRYX12F2Zi5ea7S24g8Jm z{s%cv*OaFFUQ0y08Hn4ZLc8mTox66T>wjuk@HKjO$bhG<B3}7BeZIAK?GXijI>-Mk z(bd7ThpOlTy7$sy%DZBF3W=PiyU`IjF;XDk*bS7oCKurVz-6~VN#u=byj+3(I{NFW zJm|E879VFh%lA#!4u>MWNcxh%J=FmBMct_&hRob*lqib5`I6l2ANLI(vpQcJcYkXR z&l)*NhidPMj99Vsd_n_pCmJ`qj~1YCQo=U^p7d9hK~VJH&_D8njN!?<M+o(dg`VFW zNMvqnkwOJG+}4*Pt4EYS?k+HYjvtf#*w5f@8MIaU+-+&WU6<%g1YAWZGx?)QZc#PO zi34OBJ>cB}Ls(OVed=2Ah5{kvUw^Rc^jRX*{>~wLYxYe;+xFb?n&(aHUnnYt&x!>V z4GbLnPMh}fhJxSPLoc|6>sQZaq~+dnu*vIvK;|;oHu9crqs~%8cCvzlTSHuR2bOsZ zRdj<x*uRMSzvu>^VTUq()@;KrZW;{$XN138kHsB<I|Q?$YKNr0i_a%c3V*h`!zO3{ z@k$qpkv}7Untl*mkiD--QP>h&uPK(prHDb~iVQ%rD-l?AqH{OH7_hzl=LN@a&{Mkp z+w?4F?og*yqA}Af_1k;CoTf`ZF9yor$i#JVgRC&Bh}xa0SC1t6tyWbp@vl(u&05Ie z((v2uv?i(h8FQ76X~DyC(|=@<*`1g)5wSvQ(g|{#M@%4*pMXD~>}<=RRzf_5{F~`0 zGIVn19ko?Y$NLW44_omt``&b@tF!i>mp|WeA0z(N3+toZcxN<I=)wzQL=bq|QgzS7 zLM>$TsU{bip^F5#$fEw}NJ8Xy+?7hcHY2TbU#q2^4j@rlNOhvh=zoguDGNF<Y$9Qj zNyg)iH~h$rfA#c!fc<q1`W5+P%R;eK&U_QO+nTw>IDWSFHzt9+nQ%dNIc^z$OgL)4 z2F4F&{TDe8=NDAx#<U{uTvSZWK~5LImZp3%TsjGNmNq}$_c8~Dy2Sv$-c@}kHh*D% z-9t^wX91tEP`O>nqkjUmn0PZyl@)y*bOi^W>l+msmpw-tfGG0t<CV{M2lp4a&WHHr zF~44#!Is~uoK2<GH9lXgBU-1?)}tr&s@Xv>o+scE(Kq-}wtMSo{sY^FaX44?b4oOs zu0$QP+DT4yP;%R*+TE@*Ey}#&qa$MCg+I^89Z)ab1_259JAZKJyN_4wo~z~;f3IGz zP=6g#)nyA-QPurcw+@njj__h<4^W5>6hu?|$Fp2N;nqo7&aX)vHWPM2S-FOD3Xed6 zFa<;Ex}}E&hFzXwL|FE6*M*l--VpDVtN$SP%Zp)1xo9_eSE_n|k=Nsbf#Z#aeN%RF z3G)uv{wVAjwSTPe9lgO(_y;}v#m=9t*b9tfVik^Fbso6e%?6dT7b)gcGcwdpF4!LT z7wYQy!Ul)m63%~6S1<f{Ugu?U$;|<$Wy0b*9LKcmS^;<aOUJ4OkMA^Zg3$&R;p+hT zjQ@Bj3H-s5-zu)vwO8ttuCPp?hP>==qwd9~7NoR+5r0`ZSVxBFsmL0VN6rntrzx-g z;rm>)hGsntnn)^<$RL*t1*jar?v#n#lM8tlEY=0J%=27sCA!E@it&%D_`1kRotUCD zbGNGbMBa&Ep|cj--O^p03=II1RxjbRbm66VO^C=JjPQAl!maDq1Aw*@LL3G;zqo!m zUr$A!OMfO>F6YXmoW8$Rm_2p=MYC;mgF?g)NBhe$ef@^X<Agne9a+NTAckr$5`Sn6 z6-YDZO4s(zlC<1jeD{2(ACr*K4<GS&fzJ=f(SB<RWJtNv7G`#MmvJo^get%TcdTdQ z?Q{leC{jRazaa=5{NW`2Ebwu~f1I-t@<rCqxqlOXnyV#cI$5>w^^3ffNZQxoB*V16 zJ-O4bGx#Bd{j$@q0-C9nL9T>!+|q|yP;-IDta<V;7IV+y2EX&}>2@gyvPFf>nw$RQ zVgBHM$_LM@@u2Nts=wp=PDf13LX)_<^F#yjXTpy}L=*G4RLE(AAkaJhpC^l*q^fRT z_kZt8jN?*g^N!79clAuDf}4d7va{RUd>}h#9~1nG37CYn!9P6u8~mdw`%JSfpmpc6 zSgs3$J#Ox$eam09`#zipIytI!EG%&|<AnXFHc-gBcD|zj7s=(vz8^17{2C+uT2tpQ zQXO%#F9L@+nQO+IZ%3#-q;iC_qUPzShJO_e$R5hagAT<1eyRWb_j8HHH-)5_9Y^5! z1_I+FYaj)UsyP`fq;+Qn-=E4$b;_qT_k`iM`uL-05O&{LBAYV~dr#W?v5$6+>ss2R zCD7e4&o+TM*7m%lJ%I(FekUL4GVv4eAJY8#G{vsLNytL>Ba;QY8q=CO7i^duRDadV z1&2>Pu%mV0Qk(utkgy-1|IP?H@0orn$V<_pNw*U4Mz1j1+qQ+z?~_r|5@I&<d{~;P z%g?F&(4-&m|BSCa1M`F)>hp;i-O1~76W;BwdIe%U#h9vZSZ9H~1Q6r8YvQ(nSNHRS zP+n6t$Mz`ECHlM0rFZo-9a<^d?SCyQ%=*F+OXa%1mh^5ucJibB^hWFcgZcVU$L&C1 zeN&$O-ll}eNeW7}xeWDXqr70le7%#E(aqRYTz|LVe$b1@e95x_ZtO0<og6P5SThtP zAIo|dp5mO0gHxy(Jr=>Gy3^aSCpYNZw8Vcv-&;I#<Q0K>NbP&3u!5NiZGV7f)%L34 zSJ&%h>dyI6rMoOQbdUZn{1a?-OJC}j-X#UnW}l%K$p%57@_?2d@3&F(?iVEA*YSkJ zc<m~P`5>U!8xj8p_-mzq2Tjf0GRhDt6u8w;q?f2|c!7?DOfN*(oT&<ms{r3;E60b4 zANKtA(rR)&xe+yhM<NQQwto@W{(w|R2)~|g{WZc*>C`I?e5fD?+#uvzHQ+y>58v@T zt&<K*m$EcM>6jMzTgE(aGl_8Rt{$f7LS21Ti$|D@`jvblKY;#zzUG<TyLpz!_{gIk zFbe~$y29mfxVh;hgtMb}dl_#u^@2=yYfVS~nHYY@{nMZ7UJOFsZGU-{Clv{~>Bdkh z4bxrii*(OmA2JSl;{`29)^|jEJe>A}k^Om8<)vVGQ#n#Ij!NE(5epLRsmgPfxT(x> zLX9M4bXPtW{!N|GmEZs97=C!DI^{Jwq+!f+)nEc}DcXVI0d10+yX^QY&jE`oG;@cZ z_uS)&@gFqeYat%syniCm?;x%Km@6svp!JE31Ne?I#q2t$CM&w01)`9-(gq6p0s7rd z8VWM8itM`k$PWjoThhZyq9ZhPw#G?Q!`k6oPt-Yw2OoEh;CFHS+ht!BJo|J@DUqAv zw&|Aj<c<Nm4_$yRCs?+RGlbs*Bo|KB2K#UnA6G}8w_S4v?|*Rt&r`=kfARWBWkJqG z5k12bYrE$&cqg8R5W)_^X3Z+mAF=;y4B^Wa)Rzrk_y{cz^SP$O)0{I}3Lp0w=)znE zmFdj_HP=&5X4t9dO8Ss@ef|ggw`#l~55S&JuQ11OY6~lJc#*Cy*qTX}D}7Xm&T${p zNsE9W`k4TJxqoE(l?V_pDXfvYC?!uf6=J`}&k?=}1MU&izN@66-$ywx=r@O37x`bT z;d<TUHJIlJkl7-mr|}+@w85iN<N$c;$V@J0nxsPQHDr6F6=pm%`_<&Jw;J8oMf^5h zkKwgim-~f%9I@`iisfPFT<`WY=0_!iC?TW)tlbKA5P!i+Jgw|?&3^tDt9%U&dFf=X z+{fqqswoExhlwR+k8r#r=-4&ytB>F32R$L8o&3LC=08`1pQ)dQqxz}80PI{Hma-^0 zypl*)knV1JvU|^g5?WyqR%N+IfBdbh*rtB+bs+aTXWSW3yn}9m4Z>xL&_*$%vTXsK z?Ol8pmw#Airqr_6936ZQnZH3l3WK5J>RX`S-QCuZ4N4hoVc5n@i;FwmNp}|=HWGNg z&0;=n2=cAB`Ww^_^W2%zpiiDfy%L9T3HyD(X7|0L&u|V6C!#MUF%df0gmQk(c*gFu ztLaDo^R1Qt8#yehEdB82$pGnRs`T`ruK8B4(tk|vl56f?uc0#{K{g;l5y0Ka4j#Xu z287>P>J#+p6LQyoF5HyB%@6x-ug$Qn6^YzNp>XkmJ`9gZJt#7EAa#l9mT~u}?c(1~ zE}S2Z#`&G^Kab0BYh+Z~0_z#&E(v4lsg1Xz49?A6vASRZDnM7YI5&{Bfgzau3f+h0 zTYsV3>`_8?{q1S8+Tot<QXm$YhJaE28tyV`TTru>Vo?hqs}IA!$>Pz<FP~?mSi_zr zYzw=Y;IZv8(z4x!QLaaFb-Z^ck-eXe-FZhYziMLNPVV#J_p7La(s>IX1tWEH!@e~< zmlak+aF=VE$?~mIs+*y@bjocg9Q&qdd4KijWxc(B3^5A_N;G8Gvu&-;h!;;7x_a3L zH38=Ccomm%%{CxU<>MKF_el8%w|b#^x`S`eK_wO7=)$m3Pw4&B2;kM~?ucs7W(G=? z0#M<@Nx;arotDFD5^oa{^zL}7p`{0I1b7fmI^LJfwh4U(Xchim1BTq~h_iGCZh!Fx zLcaQh+RelM^;rl%2uFyQPd3Ppc|7P;TC?%JRD*KNxx04QF&@bE@Ji6Q4FSI0#Q1}n zzD%El<E|&=3z{FWZ926YXXYZmE&H@*wqnO2_F39<w0=GF+Df0l0seZ#=(%4a_Blaz z^47Auf?eh$M6p|Y^Kf#YX8lgw4}YDh^E<$UA5RE=J)$qa*F=L`$cY{3u4G-7Y5~1h zd^n{AA4$<Rbre#vB>X00cj$4ih=RVU>B<*hz8%<-;)!VI66rWC(5KmEm^x$=?rwr1 z2j~JKgoPEkX0h_Fw@&D9#K#@|XZ5^?`Ru8y`#;#_rJW3^_Muy^g|rIjK7Sew2mflH z<>R>*xXt@zmfwg|S|YIX*?U>J(C*3!6LaR&(kZ|~y`H5;*kcXQ497qb21}@|(Bh{@ zXO+OdiS@I$<*uQ9=O`GicyU#8qr2!ueFH6+93~6iVvOrSQ;sk-@Q~`)v-$)2$5daW z(G#eBM&@C+m5{PfYpQ0UGk<nYIoNk^{2*WDJY=_g*yfKco&2U;kBR!{dPS~uRe|kI z@T}ix<YX)ey0GOoI`@qU*j!kfrvxCtwtn2>1HV|6`*OWqya0Q^ZWJX6ZIaa6lL-rm zWY!1oxYsmW5?qIlz#Kp$aHX(cVyv5|@k<{v28h7|hnsLd=i0twc7NCN!>u@-9CQ2* zWlmsF*zAEFe*K;{_}dOwm;dZY#tu5Oo)Y+rIPS!t-k)u)(#}4UTH$07cz9uId^j*k zx4|j+i<WvX7h&_j(^>I;;>B|H@t0wQW`&6H;B3NsWD-jA7D!%w=T-mVLBF{WuX=9l z-U@eQd?zRg<h&XvIe*TEGFki%9`1}OY6rFF#i;iDyZ@Mc!ru!V55Nbdc-G2OE8j8< zILFC$Tk~h-B6ACEXJz1{Ub%0?eUW0;U{l(5^&k-P<yibGe|eQ3VHNGH?=qs1Z>AO; zjFBLDF;(n)v|tNV-h3-5U6{fjmvu=P`Xa?&kWSyGk6+pI41b*`@-5)K(U7Y1PF&L} ziz{fO;71(`^4Pj;b(<o@t9wCzy)OP0c_?0pE328{$rlPL=uVduf+E#R1t)>iU7wSS zvUPQT&fR=}Z^#V+Azyd<?m&9Vz1wwh6?6^r>qKYlRqHQZ!OQ^+aJ1p8YMxXru!kP) zcaVIqZ~aAGGk=EH-_ECV(y%4zxIY=KU;1*JF@e?42(KGUH{h9FL2a85KJkd3<QJav zxc9bVzWRIL!EkjY^}*3<c45sjf;!lN#-&->({fx^Gav^cXhTi4!QL+v{{#DaOL?!U zigq~5-fZD^Q8`^80NA-hU}WT2m5DB-Dh+uPvYNFfP=DkX_Z)<gJLJ9dyYT;P1&MrU z8n@bYh*!5lW7O<KmLjIrM|gL^<$h2D{JiElmj8G+{Tp=Zu3z=?j#k9ykjnMf5~AYc zR1J+91LR%O48&?=ra9()8%c5pvDD*bop0Xx7w>(pGz6$851t(;3l2z2dwu-YIkqoh z{g!7ZYJZSyNzVt^FJ^E}Rq!v{4BPhg$v>=?kdwdCS($fy@O0?rHd>M_mGqF{p}W{9 z2k$`NeI0lR^~*W^x=mTP)%^U7g_=_;E9`7Qx?x5^b{rHRV@Jbw$<P(^4mj9gq<Ovu z{u<@_7CL^tz4CgG)k*JE&ehj=Gzj6Mgj>5j$A17j-8~6C?$sDW5u@3W%AtF_Y)5`E z)g+CR)#E3xV+Kd3D*<wM$^@v}XXm3+`DiuXj3U))W6Vg;Ou;1#;B#qhDD0aDeEUrc zqrR_Bfjd?FHKPyiB2DH^V5IXxTottgA{3nzd3S|N9^7E>jnv<OL%qCiQeLW4wRcD^ z*nfI$pDJukshz4^7L~QYgNRMZp2$QsptN64n0+~@++C{S<q^W{rX1*guaDIJp!$an zn9#wFqMj)CW^%co30n{1R>dAq!K2`}aGa;(i<3Ga!;PkS+quBDcl7hd2GZ$Vag|L* zAj@iZg39QHZ!&TNVG#Mn^*<0l^9HVW5P!-F?k!W06>>g<H}_bz(jf*Ox8i8OWwLX2 zsBYeju2k<^6#aZ|20OWuqzDpqn0-@X{F8wC$H2K81Stt|1X!M+mt&+%VD*@#d#^7% zKp#`HFRxo7;cz@jScgKFtvtC(rk4CXbXaN&Y&O@N02+9-om&?nH{|<t-fzgz?SF=s zixzCra=b})##Lp#I3b03?=B9B=(px#I0V>{?z*JyTO9d#)BB4XsofxW{q~c-=a62U zDyoZ5kabZ8)^P;L$^PHou4~ItEy=#mw<LG)<`D!434}L-7cG!*LU_;D4`fy4%~YwX zyH?+xAJ}nhoN#A{<>Zn#>b89mbAP3?m-)2uGrX93<;P!k$WShv$?#$jBgKV|mF1kH zkTD>*JDutIfLwGyR9yJd8=D4w3Ha_VeKvIt#h^n9%bIY6Sl%g;ptz}tn|rC)n<$KE zaI<1%@1Icj4MAQ-J1W2ULU%&7+5-d=^-v4g9*${AD^WSD__0#Kj2ca#y??|?-eT3^ zy3ysm((_fbj`Fjri)X|j;2!1m7@oVbBxJ~A9riFVXkP@&4NI;4EzSWlT5Vb!p<e9y zkLH!1a}CxNeq<JK#;DxB?W6l?$7EiZ?o|otr<)?*6Zy!)CNZvR8AM<1(5IG|pOACU zRI8&kUZd+R)^?`3Ald8!(SMboZZ~P{CVZbHQ&fUD`bI+d3q`U5=U?iG9-y9R764kd zX;UFcP7cHbhdX$z;lp{}C1>=p10TG+bm)6k>dR#<w>Gq2NZ}JfU|CtXX19z?9RXXo zAO)8GU>iJ^1QvWhlqjJS4|D6t@^W4OxybYe*M#xcU``{O)Z{g`XMehfv$q3fw?LuY zQ5#)3SdV*nRh7w%wSgfw2B;V3vk9xuTZ5gvqn9gkyu-8p251g&$)Pekj}q+bc#qFO zWnHb^ae=q2;H!6|>Ax6_U?ls-#piS%u}=CGF)`;<$(OdzJ9divvVmu$jG(AvvN!a( z%HtjIx%Gs3vMjPLA%CJv=33jS^_`X6YJJ)*D8p*|1~_AS9EzjuZ^X6ei-*0vHsO~# zDYp#ukTa}gBGC1I2Via(0riNJWNw|D7u=*>HL-R-E8C;-YM<Niy`bZBEmK#Bp}6O4 zz|kL?UAEED4te&<PP8ag!|{?xi_v4N;eOhBr@S2Jw;;x6(0}5PRN~`t*M+JwJ-?k4 zGCRkeJMRQ5tBhETANmm~F(`gR;pc?OJLY@F>$BaNsa1lwA?-0Ga0bv(t;pD{z1c!> zT$V<Up;6T{&2TK&9e?F+KG(8M)gA3mjCbmKxtGg4vQj@!;|EefEjySyZ*2BZcQx%W zUY&Ljd*LR$Lw|lQt`0r9SoX~U&C&~oR<VmHchCeT<T2gOrkkLbW@Z@}9T{uA-_Kcv zcgT755k9+}AuAsA@c{s#2TEfJ-+WltyQuHZdw*z-3J&can35S7Wo=rMc(LoxEa+Zt z5;k!<?G#rW33KmKh9}J~8{x;*QE==CB>0=%+ljeVmw!EfSosDSHhH>LLUd2vT%gsT z?W1E<3=g`*v^KraJ1;%6`FJ`nb)56etOO<n%*X91B3>wuCnir?A&%;9!GYM9p)Rqd z^NA@sE@xHS<zrsnL*)sFF0Ef!ZzzGivge-1Sjuy9PWFD>v5$bAL!`5NBy1hU$-qI* z`iUQbOMhQHd=Y#@h?gt6A%6MlF~z1-E|DLEf!ke=9edkhs|$`DY>V@Uv}Ud2F+p9M z-<r+78e`*M!>12(SJ26siv*$<EAj%#F9nK(FV>|c!m^#jla<o;x(+wp-Kv{O{MA5L z74kVB?Gzf`(1X4|BDJ@#nPxIz!+}kaG37=Djena<YLmjN{rE;bTU{F^!%IpPHIc_l zI7{)ZAWqjKUNp2!&W9Yiop%H_G*D~rZF^*mihe_$Zu@u8pZ=LqBPs2{J3~Zc?s*Oa z4)!?+!#gWnA1Ey!juQj7SFT_m^hT&cy&UQn>#$AGElL$D6e=FQQ9d;B?K<0+TJzfu z1ApCNQD|#l>8!Yol6VRD%tQ(7%{^>ebyiK|sa|qhL3v^h#M|u<&_z!R=O}lytC=Dj z%j&C{Kfk=BdY?VWw@a<k?V%%}ydU}+t2y9q@?Itadr8QWTNvcmx2H<jcfiZ|YKK6c zoZ0R?Ay!``IZ2>mLtj+_Z=3c+ku%}Uf`2yd`iB>+7CcV9h8^D%oS$QWZ5r_gJh>5+ z5_q2LbF@(Ns@J9+;avo6wc9;0IkxV3i`d~WR&<5^Cl{dU+pg`(CVcsof7A^-z2Egk zmF%;%IQ4MzB)R33gpJBdlx{F5(yla$*7gk>{3Z0)Vk$o(W6s-OhNSIpsZR|H8h^Hk zosbh<CyTbAyWrm9muA{Zi69t3e~J72@1^h_Qnnh|i^YL4-pu;Q*n1b-DT_Myw`-fO zTE_946DF;P3&fuIoIWG3mPygJpG`(dvOL1u`)LWJ*sT)9ySOt#qmS|eH_0lc9_*Db z24k+b_z3*#L9ejyLHy4D`VCOFSbsFK#hbx`aFY_iEdaYh0qt5+DPQQ9+~}?j(a{fZ zwN;)&o1X>z9LsNLVHRDMJP{oboTEKy$BOMEW)&rN5_D+eX%7X(++JOw35xpVy8Lri zZ{Iu~X@I;scIjEVy6JH?3UDt6j-q%hOII-?LC07{QO+Y%h`>REJgr_2@_$qe^@01n z1M5@XznW=deO4S|yeU<Eyi?kdCi!`<M-9o5y=5h}H6%Pp!0(IvR1@|A{_`)RjvDt2 zUY3e^KF6tx<06#fjAyJi`+OE?i{wg&HACD;vS)&dAE@i0D87yOGj4aFDUf2Cz_gYz ztGV!EE}osBM@6|4F%G$Hn18}~Gw$Q9Fa`Qo&{g@x5enfjrAIm7<()X)(O_gnSp-Hx zINXc{QS#Gdt=rlG;h?RC&9g7~1O8=+1$Zio5)lPcj?Q}I5;#K2eWa+F72x?<$pNXp z1Ly>gB)xj=PZ_Tt$Wa<TZeOh0L1K6D<(Lc5L%LfQ=U#ZpAg#nD5`Si3)!ydJz$Nor zb9y7Or~j;fqdvl_{L-ix3odmX>w^7|h+gV$w970^0_=s{!syk)?=G+mo()6|w-yWN z>%+V)ZtfRP;S1kcjVHa674&EoJHxhX(G;~C$(_x?${|~(s<?%Nt0HfW!YJxB_V*I8 z&(4kM@Nz?FhcO{I$bVT~?2wX91Tnz2V;(V^AkV#Njsy$QZ}@Yi_usH>weH)O*mBMu zX+GN%P|uqM(AtKXz=v@)t*&Ryt+PPCr^zrfcZ+;OR#*Nt@OzB?AL}ylEAWj}tBBJg z+<J+o&6leau)(Zwi39IM>3G#@TBPTO6J)^89_=jfVvP?x>3{KUp1o1n>+|<R(1)B) z__mIdA#HQ3ns}_f_?=<LUEyO^%6O(C`XEH#F962)y9M1SDxT^Co+T9BBY(P_@*~O| z!a3a>a!Kp{R#tbRbYO}qV~8=G6g-57G^l%iTwQDoetkE8;8*j{Z(72x7WOHb-Ae_O z;)B1sSIxIIoqrwh{V8Ymy+h6SX<m)U(?;P&{(h(1@Y-*j(|*xDHSOfX2s#`rix=uD z9F}v+s=;BOvEnsxVcr%)JCyc1q}52g@#mqxU#YgdeyN@H^^^*ob4htzKw)QV3M15m zp&kDyihIDNbV&(H8<wXIecQT8{r*Hf&Emg&exIcU>wnvw7GM9`pEDe-51GnzAVp&} zpdWavXA}8K<>&Na`&CXjg<CB8xRt6Qub-@6N_>>#<q-~FVwntlWEl7&>naFOBF^r7 zNcE{hKQ-z&E$re2>7-==VNs_oJO;lO`J-(3zq5oN=<S!U#KEcp!I3!+1V<Q6u)EVa zI*u}sG=Bn+%F_UmQtZg5lc<*KQNy3gpnd}1{L5{b{@auITTbq0;Zwm-5iA{&f_|*p zDoEVdcj}(_?){Yabb4_x4&>(1O9xvaxu1zpe<NxP{j3S2t(E*UuWP68{0FAciEQX| z0J4ct0SHNVhsUnfRg#%_L_Py}Qy=Iz?3ot%hkwYenEBPZ9l6}d^SYL1e<W25imgNL zCFFjLsT5j_5MaxiPP+mwwop0tdW+t{Uz$vqG{Fp{u8LBem;yGzDU+cc6YGqR#C!Y5 zh^1EQWz5;xmVkJz)ZXErQ;&-^sOs+6RQOHdK^fiCcA#7zcnP5WSu~n`YGzr34VJM{ zv44TT8Tk5^_g}VHkI&>KJOdXy8>aFMQn==n3#qoCCRj8#9i*LmA=Ki!B~4yy`gh=; z&4eAOSRLG5EnL!5?MS$;tWx_yZH(bTEa`y>GY!Cs2jgXL2=xo~_#OKvTVa2!VuXuR zs`iiP)!j<4aR5$ouRDH$F^3)nOFb+nM}J-IX#|5_--7SpU%M6HoIEHVsWR-6kXf8g zRsEnIhbDk;lK^Cdmot8G<Y75R-(dKe3*-y*=5c*acd~tNPO7oHLyg;c=v1+UH31C_ znk)wT!d#1Uq7h7u(yKy6@qdAw``tCUB&#`D_+Nr%hX%cRHh0hIjEpV%AUH-!jDMY? zkchQ&X~u;oo`tl>#i6%0&A-{yRm`hrRi=R+Yffgv@+`k2wM7>#PCV^F42KehMQg@v z5D%>eoZGs3{N^}r#cE#@xYw~pZ>NkT@}podS+}|~se1%SY!4tNqUR<I4urw)s^Xp6 z0RPSCzjii%>}ZZSVfjpx9D8%X_J7#VvzOQmC0_0b`M5iYKo`94l$$bxsn@o^JMid# zq1}&^GkA&oa@oTwk=o^5D>*0+=Gz#`vw6*d<jh5UV|LQv4MV><{{W2r1^=~?`2lGW zm4B13(3UAEy78GQJ}tTEw9m41xtiC5dK>NukhycNKpb_TZo?wnCF>y?SbzTRJAV8A z`%3}RVaLe^_gR1op6Dtsth{e>XOi~QVKz*DpDmH9U&f>xLA#r!xjtyxy5`jHC(t(6 z|NXpjUwEWn2v2&VPYATuF6u>qykh`bk#A_&aiAZUt{+dZ*PgiP#3$RINb=8Qf2TnH zF81S!n^y<U`zvc#q7b<%R)0CmRb?tWRt*6LSdv#WNgcI$*r+0@KN9&fckPQg?;E*S z(@LuMC+BD;Y628-c59=iqroXSEXUw<&l|FxjO`Z_^@k()PQ3X-ZhSzsS@fHG*jmLU zUx29qbv!=oEO!r0Vl_V^pfPR3@xb*CVBfNFe<ZjmpBH<Ea3y+BqJQGv6Xrfz(w@Ca z(am8Zlk@a*aqE?~>VR{QY%NK?7vKKdGT#{jf6O%hh!0gBDY1imcLYt#Gd-Czb9K~M zo2i157d!$Ot_gI?M|j-wZ|I-R^9OO>PEp;1y#J^cFp#b<&%Pr_$UWcbgDe1*U^=cW z`!k62kE|E#g`{u0>wg>mEnf0(i~O6V>3MZKVQ?s!h89#XaMuEJio)CK8d@w1pX7tp zdsjnylmWL9l@R}#(7$Mv-U%PFSzGJbL(HLAU62Sqvi|B0B*twjrE^^Ax+tJ}Z{90? zYc>Ny#D6&DzZir4FcU`%$PsN2P61{}@`<lY>;f#6=DDyzrhh6aA6#~%OQ78%#ONPN z{=!)GwBSiss<e;y`rQy!7TR0h`6?)jjA0ap3%9Dudb#A<S_io;ee`F#@(b<HlVCAq zDYwt6M8o&^d}lA$Wn2aX1S&(w7(oRVP&NRlGXr;{kNh*8@j)N(B(%PHgJ2&!a)(xk zas}m1R_s2xOMhUny9GmZ5g4Z^<Dr;Viy8SteewLG{YmzI7Dpqn^2kBR1MU%&n8^2* zkqQc!DjkH`Oa$fKENQm(!I$}O%l`QI_#|`}rq!U#v^Oz(tL00dug7e{P+u($BG=`V zkM(eWp15%<Nrk=@5B`DFj}Kf=a!K?~DW^t1=@Ns`RDYfmIE`HrUJGFyK%|8p!M1|6 zRy~4m1onq>-O`Is@{k@Pd0d-fJN9;WJXNl<kUuKzI4w=@HU<5l4glcDbMpR=7XKrG z-|}To5_>v!TG<+Vagog3K@Ztb%ekso;JqR3x)3+W<yyCeHf%-7#Gftf7m0wt(JZHb z7=9#fo_~&v&(BDhj6^Ia@NsA!K}mqBZrPY$Xa1euXYy8a1OM65{z#4ezs~jVw8)d# z6M+#?31k;NpKdYg-&gb6m*6HbX1%MVn`RxQ89Dj|x!KO-pKa<73;2auF(Sj1^hXl7 z4VCL*HW8sDUbN#*UmDeS$;h7pHiwW*Xx~fFUVq6waVE{uCjGH%xYF_RHG+@4sHjJg z3QoR&0e#CiwO1R1CO<H!3%f%_zM;fx4etmXCw)}fF1s5dLl2yyn`t%!vRSxNrOYaD z&snYWhODrd(ocoC@ua_XT?bqg$M?UZNblSo>{2a=g=X(c)Tpt6Mx(LE_CrN5vBZX) ziJE9)mr+qfz1YEy1r*fSJJ>5=1x2x9ROJ7io!JX}m;Zip8sB_hnK$pfDZ6_&bLz`8 zKmT(t=~dp_W2Nyg$)?}N_laGcKhVk{u3+QQaNF=VhM2e3z1E)S-X>>e+{3dq_t)9_ za_5fjJ38Ml3tXvrG|g&TbgzpM-m~UK-pG7B?TYYyjijg-C-v70_mRBo2Uf4!9T!*e zW=2$JwS}kUzh8jYb#i)@GUBjEXn>bCzkDB8w*7jRTBYi3sZ!ymCykduU!4qGB>1tf zRZUzZ2x=#iyhx}nah*Sz(=Z2EKX5u4c~mNcUPJCJ5=6#TzDV%0GUzL+Eb*}s$+;VX zqr~iZVCtSf@NK%GAu5%iU>-{bEf#78P`lD^HY|38=xM<!l~w_&r(N>g%ZubJ791s{ zwddhS(QYc$Cwzadp91+~Rq}kXP**}Zb(&rGZUB_xD{|?#zseWPtG^_zkuN!1#N|Ct zxfE7}<{s#qMuy>U^sN`sFA=Iq@?zi3dv^%Gv>NGS+KGtXOaAEVL-LpK?zZzEuldJb zrJ5ap9|u*)3nC6nh5C|D3Z7*J9&%8rZq_mF#1JxIsSv^(I)AAkSbRSzX*qs>^YBtZ zCs}f`SHo9-;uk-?VF~^EK>0H0%O&KJEfd{{uuNb-^+{?kgN(C@l8o<{38Exp+}`O& zy{h6H1E;H0{;~l4ZP2^@L6J)i&vmM68cDYHK0{i<Nf8JgWyeOOOH3`dxf9zY?$&G9 z<Hl~YQK@=iG@;3O8T5~~OX#dLR}up=C4=r>&YJ0oFYB#<<LEaK%9lZZd7otZ@+5)% zU@6(2gbw)}lBoCb{}O+*PW=a4BgS^rHXZJR$0TG35i}C=+4I1m3DDF6Ukj|X>%%3< zuJadM&APs&ire#dh)(bN>{}N9H810u#5C`U^S_<hW%0ijeu$N$7$IL7ioXr|q6a{; zpV1{@%h8XoN<h$%!T5hR<O!5|vt-Gua&NMGxxjw!m0Y0Ii?2Zyh}8<&6XFU`8+@kJ zCC;C0NS_tlo3^<FUnirFyO1Wzk&37QI;c{39HcUwtx3dc!JE8XA^0#eTvmd-*Oigy zyQpnRmz7-j&Fg(!??Jd8HAd;c5^siIv{JTv8x`Xaz{mwF1@_y$<Rc|Ft<K0{$%2P8 zGJD#jzRE{@eQRZ!6Wwbv=u{cHW$4*{jq$aYQ(=QLen;10(38o6C+o`nWGK5I#>jI+ z)%L`174J&lV*7m&s3D0HRH{J5K>g5w;hpLO9}7H_HGLIi<@R80yH;_-uXxoTVuMik z)tMSTcQAu)AEdT13#ti4RdPpxEeRPCpwNDPBxMVgyb@*e*YYd`(@gYJS?(spQItSd ztQOpvdE4<%i`dCbXY9{vM^d_)cUAp;Q8iyE+l4PdReEe*0>d{<kv-NmMG!0>%~F$q z-_^ckPKuxlq$PU8)|5|IEmW!?h*1`v;d40lP6~I)`^d(-R$(mHqE?`y8!v-?>RjTo zM!-wLkhmr0Nw2Pu7K{q5jMD*saEbo&5PS31$ODkP2Btk<!W*xykxecE-Qs^q#4c5? zoy}4~`8$d9P8C8WWrqgD#chR_twg1&VKj(l<<u=|GG&uGh@4H8_1vc1F&9Z$s!&(r zPAR*6lK!4_Ok>l|FSxEIP9N3YWWZYPsM29=H{C^r{toe=)O7eM$L2rA^qRR=R(EzS zx^?drYfLIdoAWxk&2JsrtUgO^0i+-3Qe1e4E@<!!DbT~Lz8+AK`1Q!<)OB(iW<rF; zLmNgeT_<=-685GH_pN~mxyw+~$PyhHG|t|_JZx8t30{8@Q&)H`_Uj0;D-HT-L6ohb zBz$t@|I&w`X%0U4J!#`*&@Ze<E*XUC<Yl;pJqbI*yOS_zqxDoI{##4{m=lecLEpSS z!?!i?3f}C%+_8noh_(SL70SKw!qncFK?gRnur*6JC)=KlD&VlIs2MQbcp3DyzM^c6 zSq?e;1$W1FvLMkNEwtoi2MY&sc)8#x1;N?-qnmd?RKG$N1R4d<5PHHGs*>e@acuI! zMH81Iwv#cPDa&MRXD+;?lZ6v`Dl=46kJ{V=>9P@B3sMM=?8zDGY!G~zm0dQ#%Im!? zNMtVyzYx=zy6v3}vF+eY9fnEi%qx8)7B6ycgAgF~X-G)KnftIYu$M}O`O0`1^g{-4 zwbeGtGfuOOP&;K3r^jyOBXa*?QrS1~OKbQ=S)=AoCv7$fUgY3L9(48v2ha7vC?uOr zO|CO2)SKAg4=IEm-&^s_0S?KB7i${rqTb#zi$WVp_6MyTR_2H4XbF;2nQR|_<92S_ zB=|_!huVN_nlnjQ=P4)Ve)BoDVw2!0VTZatP)9&3{V2y;zs!QTJ+{!1&YO9{ug*yt za~3jBN14ifC1Y1^7Ca=mkLzXRIH5-w2t%c>%C>U3k2cF?t$Z{5-DWqZ(|J{r@y2QR z{(A_M2k1mKg=cb(a%|!j!JCE3>Mf9Q{~V`Z-Xdrj{TZd>+wxgo{I~Lc=VeWBIE`_n zRbDw!w0kCD16)nRjb!aaefj1EiQFcLBy%fYsLu14yT22pR6!Y3dT>66I&I@o@uaa& zNDMMR0fDbf7VUc~HC*a;_J{40zDJjj$C-9JTV+YsZpB=<ZJVH#f`@tqy^t<tk@((^ zM5POX#4=4TkA7(|-@wV|ye(bH=rp<dPDPt~2I-zIR3)zIf-T8O6I>(>$yLsen+g?= z5PVn`8ZYdmf|&;310-nGLCOCFBa-Q|oyL=V8T6&KjZnLWV}_3$GIVli{A5!Hx9WSN zV+r!B336JQ2_r)(KP$PxPaW6N44e;x!etfs+n`?&PEnZO(giQcp!2<+o-Ty20B7uw z6wsjh60|YCG5r5A=Dh3nj}5?64<BV^|F#Jq1;6crzcgY_1(7FwV3NO?k8&$vZL1C+ z7Bhax4`YV^|5{1pc7$u|=KpH2C5hX4_^c!?u2`cx^{_-&=5^!%Qn~}Xr&HT`P>#HD zc=k09m1<!J)1cH3VbF=umM&(KN9el_UNIQKW(ZQ{<dHOivU!o(BP>10<Q;rE`+d)w z{0)fvBi&3b`g$_MAKJl}N}sZ>eY2)I_KWjOmw`1GQ7BsmF8y8;)dU7OqNFR!v{eQZ zn7z)j3hBR74%{&`Cnp|YZK*r4Gp8#qmZ}@BkjuM}s&976Dd4;d=weQ8ze}$7{VBQn zHHxlER_@|H{4?`ZeS0h%J`XURUg9KZQ>My!H<*u>GF6FpEw$2Y&)P=QHn&&CHV2Zn zoBLx(s`X|sj8G|Dr%aZF_m%J+9#}e=O?>yhYvfov0K4=Vrg8nq&WaT57HW{;d*lK* zV-F&#Uu{nR#>>jXY}oHy)z@!_9Qh5iXqX~TmPI7(6<SI`{^@grhc1Y|6No+~D5wn= zKJ6NQGHBjjf&UIu%3f%m*^W2n?-fKzP<`#sV?JRTi)d&nsC8$KZPU!kfpp1`2f2R+ z+&HH{i98@^$@~mHKEK9HzI+`Sr^SX?IX#@<(AydEnaxKk;oc8SLQ1ri`FgMC;#UI} zB8L}Y;#5vpCBqn7T2Cu`GGm`?)Pj8wd0-@KJF-vS^xdN5V`CWEala7AA|YZw$Q3^^ za`q4C-q`)Jqy*Y_Wjt%!fqyWT3zR%!ibPI2AOuP!&ALzj6Tcxrk})qT=Pi;zks%T) z<J#ERj&RLqj8#>|fcBqBQ99x~&dU7gW5+I=4O=jkO+#ENr?Q80n00Rt$n&k+L0ETw z0V6k=VdY4=ACx;Zh_>}z#M%}e<mIcY$H1zO;N97ft}I{4gh6i~<SVW2=f{*)K|ppv zpeY9<GlfBeGUfPglL=+z+Zg%BOrBm#d#-JN3d0!*g~$}+Wzf$(!=Ra&e3tln<@iez z0No3RDQnKc^9*X2#k*kLa_u+)5|_A{&QPr{NziIP4Ie)tetcZqn4bpyFd=5lu>Y9m zNMIH$PPxWpZ^^cDB7?JJ*TvJeu7#|v?^(o28rq~mJ=~|x#cqi8^`WL+YK(;jO^ugX za{Y5S1Tn`xaq^@8i1$f(ZO8ed$1@;NIo73L7@JR#m3iqIadMqi3wU5WJfL(|7b|Nr zuCtYfTs<VO>q=mS<xzWUVt-huE#>~Ct+xlALlH^B;4056>pEJKUWbKX7W?|c;EZsy zCMk!78WLxnRxyL>Lh(r~K$Rg;E7+Q}akut2D{DTvO+Ji;A_q83nJbGzNm#bLg&v#@ zU$tmSrezBelA|payH1ILsyYZ%Om4=@px@YwxaA0V-Wzj_x;U`aebPEd&{(wZPkQGF z)tTv+a$tI&0oG*aG;4v_9N~-Ef8x4#J%A`X4Z#6Mk#yN|Yaqw=Jt9v?@kb!z@(@n{ z+~3;Xd^LK6Vot@t>C3GBN%;}E>bf2Uy>N^~7m4%#tR2l9Q&+$5^b6Q=3{_aU&`Q;l z#G`!6QRju>!XBX9;D#WD#d*JR%Sw;R<-q9}w4PnS>FtimA<&=F9T#%?l4Ej!Zm0BC zgwr1%<Jp|_H^{?QNX7?}R)2Hn<$O$BxEaB!fx#gPQwFb;u%Tvs%z9V0?`!yb6xcBS z#>=4pHkDlZTWBFgmBzisuRYK<5FcfTJYHu_3Y=^;WZrRZL|8ytK?-mcX4nbqIrfH( zE$P}>vpU8?WfyB@60K1N39BU~Ypv}_*a<oDTc1GBYdPCer9R|m&?N(*%ecT-=Gz-L z39BJFZnoyf08;|nrc43vM|_kkxiEJUeF__cOP;p&B>tqVKk+28pp>^AI?4AJ*IX)` z3o#?az&Xmr_H_*t^Rt(&o0;>&7B!8gXK3jNANu=$@@3H5_))g6S*j$Tg3fw@OwbMd zLrOoJju(SBfx}mAI@oKgGid$O@*EkB|Fc+HgOUTtpQm}5jz4yL^>A>Hg9*w}y;Xx8 zIgLu$x2df|rFB~CGY~VaCTsiD%+`*aG0yXIX<Km*)|PdKPa1)D_CKLJzC9gH%h8R2 z3~GOt`|#IqCc77Bp+gC3lbR<?WYFGc<;0IYizsMW-;P9ro223o=Ba~}erlXOqnDf& z+@<K)(zXAFA+TVIm8s~ZtsE#;LmHlwWp_CT8KHAHJppvd-68E9ZoLbEw-#HNIP1^m zWal{{MAG!7bX{n-<LJbEQ%%`1PE1p)=}tC8JkMvVPlMy{RD;y3Sga`P_19r!+yz)> zI4>{Dv(LlDe}yx0sgdk(0c76@Ms5KX$zB>u7+2|1P9d`fQfMv7UQ6ApOn;~!2KCCk z$r<QOLS{MpN_Xy<cOx!Bk@sNI^`cNy(s5|0ZP7_+XaNb>0T?f=9)^;o7v)(hhw}YC zjNG{>1W9~VHmX`UHdCnzBh)5Z_r{Z|mxS6(U(_Y=ecD3CT$1mSBvVSibh7UfKgOjh z<z6c96zxcNCcaml1rl&s&fW0K;4VrhT`miCBs<C$J?K~f+k0WERVKjGJ|zFLJoCBb zA|k5vb0LAVTy&(%FD?!wDpwXdm^QxdFExr}Ip~sOtNR=o-Uxo5onR_@{ZI+3A-8hn zfi1{`VcHqQEe~7A<_91@;EF6i{0iD;C$hG!2hkR1Y@0#bHqT;h7qVTPNRF}X5^W1y z!rH8^$`erFRgkMKXXIX2<;01j<i%?ldEadpXL9$MY{3!Q)?htr>wDM5d_g={Z&hmH zf+-N|&;Vl;NN00f7~92%F7_nqnk;hgHHa)ez}j5iqEl;(ZQE&E;(gZk;F>%`exl^V zoE&yt-m!GM4sz&2hMsv{?&cbhEw5C;EvsA%S>AOyxr-@X7s%;;H~2Bv4BZ*`(O9a5 zN1ArvQB95=b%WO_-#fme%2DeF;iz4CmNdU9NxdO7VZnRn1~lbGx{`vauEFNZlEj;+ z%_W0fRV3d7Csq4z%CQi53w*O5Kx-Q}fs|Kut90VL<g(xTOvGyeCTpA#7%zi<R554R za*I#HE8`qIn!wD4*z@5(<7Lo~e9p1=>bTjPZJN7hX>5jruO$>Hb0Yr@=j(c#*VWIN z9!VL<9ednH3N&h<<395f$F96B4<KFoSWHrpf?GIiC^C}OcR=3H$xZI?YFilD!P5pk zt-wcVeVjd$F#C=iyQx&dJ_km=ZX`Fq3r8N;xRJF3+`LHqT|pxy=GfJLyB5RNA&?xP zu=%|pVFSr06lV#0a_F?6WhgHhn4gut@bi(dT5^A&o4wh*KJiuGzMsK25q?w_^-@35 z=AKZOg-HB87#>`eQoKlNJ$GBO?Vh|m%|@H$H?_DH|JLr-q|Df8e;<v`-Ff51MTn7( z_qok8od5iB8gvge)7>JPGn6lJvoo{#&kZWet{6-DM2FHZZ+nuQ`@G)HJL;N|2$UyK zN{`Iw<3@JwbJvlee0h)GIv-}X7{KXY*1*h!e7OY7r;Qy)NR1*nkuSJNF7H+C^ejD0 z?}3m~?tZ7oa`Rj5a5G=XxoF<q%;}<1J$-K)icKewHU&bMRPb;AzGq(?c5-1gO{cpi zM=8pSY%Gv(pB^f}=oDO}qvK09xw|__cYY)>4R5txcmXkChfNmFf{d3zfA6JnG&K^Q zjr5p8$a?plw-pM3QeRaw`-gOZH9b)_VUY1M=m&oOci`PC$q^nvPO%*+C=~ojuLp7l z#8Gl|Crb7sn;-BMz|65h>*&1x5$;rquQGu`btFC$F?VsFyZygioBHLsuL!1646u@E z`_D!uchdi%JdTGRLbuB_lK2R^%O3JWC;#CQ>Gb@y4`z83660miFPqMwT^`AWLAqaH zH<yy@N$%Y>BD=K?=^27Bu5V<eWu>cx@*|~>_=7m%HGZ1m3m5K(3mYk*d*?AdfI5+Z zkNI6BJazqGB>Zs%o3#K1K4=9gc+8Jin=KD*g{7iuH$KYhb2gQtyh+C=e1Ur^_*~H( z*hx>=l>TyEN1<M1%R6^BviS*L@Aa8K<y;!pO4mo58gR!z@jCMS3HRYoUm96NBg!JM zuu-nyPG&P`v!`;fMm?nwaDtJ4c`8qLD=FDJmyypu<?H^c7uL*Zfid=jBb4Q-Umk;| z)bOahwvQ-+n7nJOZCH^Ub_tYxrI?X*Ej^GSc#Mig`~}*!tdzB>i+Nd3(|7P138&tL z$CTOK>m7r3E|$-bj%`YGgGbBeni^U5o<Wm}<wCQ)7|PoD5p@y#S6Iybx8?Ha)Cqy8 zQ<F?x-8_In!=K5sZs%u^@FJ3t6Q0Sdi`A5Twj(27dM2+fN+@|mS4Iwa&MiKqp7-Vo zV#*httEz~tzb2B%5=70syOwM_C58Fq1!smn#Q9_{z{)5)Fp1&?VqYTLCmrqH(^E1# zy7V1*$UP5=-eC-t%)U6fq{?v0Y<{Fs+P<uyFIYV3Jzj;45wjTE!kZrE6WY$G!l|zS zPlU$03bxaKFt+ky4}0kmC8=vE)h4BG16GUhRpPDYSHi~@dN`O(M7!*c7aRlcQ}8Me zF+MJ=#5?GbhokwjD)1#n#&&5X+~Aiy^_TqAec~=`MGxXWh4Q3hU6K*6Bd1@=A?#m@ z87ej1gG48LdXVm=f>x^9xv@ig{_dbs-9YLnw@kM(ICh1Mot5!n*(ls3DnK9yDEzki zD91j47M3#VSJ2n~yhQgTiLV4}()g8JB)ikb?X5hiF;XLc%5n{*A8#YkHRL7nbSC#- z$vt>U8(XxK8vRL~*D}5NYtTK~OLPx1`n7!He#71d1JJYmHB5V!_ccj<EpN*%Q0`GZ zIQN&=a#o0MKp)tf(>uT6x3~`l=j~pPoc|FOQdw;de#fzk-^k}(=~TvwzMOvV4PVR_ zmA>dw0RL@-9OX8z??8^N^;Rx=ZQer0xIvsA|CX=uPei1BI|#>|7rUC)o+cwWcJLuj z2eSq7<7kiEJJ3A{x|N&U-0_^x@|`?P;ydVmJb}}l&wJXM$#eF9H$4~A&x1auWx{e2 z=c{wU)76Zxi+1{|Eab&3q^~mWH1jxL%R*0kQu0n7=5lI`!+hQt@?P*~ffn^1^g4?; zJ@Gv+&d#ITNB@aVPQlcmJkR|riR^wa)L`6qDEG+ao@ClfPr)pEeM5_MyJ4XnW-Kh> zjF&<0x0bXn6N021df3+LR6i^uv#~@_s<PSaxy(o7MN-P-UZj^nA4&5h+pIKRW?s4b zdVfW2=(EQI6w1g5-tS3VKL|n0>SiAx<@an)lA_fJ=5<3)o9E=B`GXM3I4)6ApDUiE z<bx0_S#49%zj@4ec;M%E)5Dz_cRb0Ae{d8PRbNxZ%qR1@<aW9T$J!zTl(XK+`XsVk z@E|YdY8=Vxf8;Kw{evo&-;lN4`G*&<s)y>XJ&vw^XSN<FXwIO)ANd}&U8BA|YXTY> zU>alh+cIdCWR3ZGWb28|lCHy0HHJnRVqbS=Y_aP!4&>ZN+0Z*5Vd!^VSzCfZW4<o0 z+A%P%1W_;(k*my}e$kAr$0vCjAN>i^5_>Rm{5FjhNkN-5p=`)(dA%vD+JpI0X<4UU zM!bXhxT-%xWL94W3CBO!*mk4j7Q-2N`e&X1O^)<AHIFJmR4Nx-t41;CvCs0_=Ps2n zIF6D%i4*>i%)A+J{K94&gmh_c8vV1UQz#F{(dA$s{wJmA$ijyjd*bkhuMamQHVqwx zDzyj4$Vwajz>Pwx>}k2|FZCCQ2}`A94-)=`yQ*TFm(>_V+5u#QQbNWS2ED7+nosc+ zBfA@}fvqk|laj694#sxmi`@C!RPm?`Mz*iuoo}LQH7FDAYlro?QiA7x25s)5wI}^5 z<mee)0Wr3RSX-E<);xN`-#9*u#Xhs<eADQeeUh=I`DyJ)u~FnlDzeTK)|MKpwGI1^ zJBx+SFYUBNAQeI~62y2J^pmyA28-+wi)DX*)ZfQsxCR5E4BTvgfV9j07TI&Q%WjE+ z#kD$=mh{Vi^nr>;@JpAnLUjryZPa2-7LudY5HmBBTvUr1vPvzoQ|H{Kp1xX)N_QMi zDa&bC7=^NOx;Vx%wmncE%}`StXvxBPT5l3&A@Y@ZYYQlP-<0&T5ZNhi>xN&_d%!~a z2(_}OYg>_Z79zjbcmaHtbt1K-&2p`eSsm70TvaOwCQQVwC*|OO=%po2X<8o=Wht95 z*b<_?>j%>=Xt9{trL`f;(I^$!6|Giv--M&J-7%&Gea>KsK}YUbiu}=umsHS*NfKQ& zv#GG%&AcxGDwT&>^!zkgOIBafiX_fTwrPeH+_HHkNwyMeN`^G6mY4knQ(<j{b5%Zm z^e=2-4{Axao35If!k#O7u38hUQso>l9j&~>T2ijjd6O>Ivcmq>uzk}LEy=MKQ3b3; zb^sDR;eAvGs2PEeax^wSm#{ouo>BJT5-qW<BG#6Q53vc;8L-%VR^3$lM+efQipb91 z<HxwaeFoXhQ0JAC(G@KzuoXSX;dq@R`BFt>x6}0*mRp@+NZC-+%KK5r@GWgb_AbiU zla~Enp#Tg;{Zt0)SM?Zlj*ZCQ+eop2yJH$N@_8GPJ*QWvmg)m7@3iB}&W~)upjumz z%?P8L>?*{#sy5@JJc-JbpgMA4na*Cidm(uz`CIS9^^x=uh)(4)baqoJnxDzJ*}<-N zUr~ydw6zo2<DU=9_NVtniUcFVm9fydt&XVI>#9kLq`3L>Y({PohM};IsiKGNC9F3I zu@~8mse6t0bz6agtBW$NM0U{}I~hL{CCP34wfgQ`#G@xVp|l}?7{{Kp7x|5_0((S< zXAGz797OiWYxEbV*(zjcr>{+A_>bn;SO-}K-E&$Fnkvz~NS1@hU!e$ego{$9l2RZ^ zPe+lzl`@u++v^!Q)luXdlx#|Fwt$ge8PSeTAn)VkLH1tuq@$CZ)%|E&`;CkRZEhsR zNzR&d+7^(<+VY)5{#a`{C4Y05k;9yMjLg}Zy*>dh`wcEr#>ih!7&QAEFY^_D^AWd~ zuR#gYb~E+T;Ac7#;el>X?dD~E8${JN<&WLA*pDN0l_A#Jf~Z~O5Q}hut)UK-;zfqL zi2OB}1WLY8hwOBQ{2eZ$zZAB$e<{-JMXDY`HzE+~@`d-&>KdVT#MxD350~1%Sn~TY z<ev|s50%Jx8T8(vM!YQ<<0`UeKQ%E$jeDT*@4(bi(}<>3?pU)*_@l1!FyD2BncqiN zYMW;8GCyc&Tt2?xp&GakI@@%&J+5~p-dH!0zht<;4RWVUtJJpYzL%}}imrg-G_#C2 zJF@Vpmn*63&Wlj!#gMM_)r|ahruWoiO_)To+FcH-G<QhdnPe*UgFAN<^$hY~x|D7m zvWYV3$O{kAmUQ=!-89Mr0ykG~`^$qb7)T~<b3J3^tHo9Tq9@2daB??Kk&~xV^2c)w zo$e{Jhi@LwU9s{8;^G(7Thx2wWzgTvW4(CU#@m{FZsd*iI^MmMg6a8|hb}9zEvSK# z6J%tegI#vD64Mxs$ey{kzu4bqAiCWi0TQUdU%hL@JCRcwK38<Je81*1aIayQ68~eq z5pPXgv?5zBH=ZB+YAZ_WYAl(nDdeXWRKgF`^68;b@YOwofzQB{tnBoPCzbHqwel#O z(ZYAlODeUgbzFCo@CA=wAjumbP68CVf3+gf8c`%sI=%>NJ#OE@BII=f;GqiW4$%m; zB6$hk=3A;=u4Ne&aQREr1ZChn!c!Vl%}1TcUc)TcF0cO>E6)AML!~!rhj2Asa%~>v zg^ooxW#sr(-sYRm-dB2G&4ywT_b8P8`>P9Md*LN|)TWPU{q}m#%Z-?@C)YKt0qef? zR>jA}htz8rT8|tF!|r3Dx9r=k-lE_*KeiI!vbV_IW=s;$^lf~lCh%4#{TEw1kvqMu z9Y{`&;3%o~__a>GR*0~>C~wUbt`&bYVNbf|?Ly89a>m}KI(Ch(1X7D)S4n7^%azsJ zPy&uM!AmF#p~;h!_!=bioPal>g7KKkc2O3cEkd;6H&f{0ubiDor{mo6V};43wPCe2 zazknPu;rCFrH*&{4AT>RWcdqyApZ$o1*+`Y>f~GnP(?sVow)xZH){rrKaNjJMJG<J zHsuWP6WvI79=B}AzV&`Z$dJQ`BLuhcGU(T({0FBy$?+B4C7lbR96DrTG}<7?B8_~s zK9p?x56(b|$**Vox!u830cR+gY<8G3E$|Z=)3$5kfM38AX$5TxO?!8lGJWwA10+oj zD*v-bz*HaRwNNl6?KNeZ-p9?3Oz@YNn}7Jj<vaJAqFOu>T*-!q^2uR2MJ>owqG}N5 zT6kVNDu8Fpk<DpmZ>y2fu%wm3iqoe}Ik$)4p0Y7yDw1A*Ga6h0=rlspcp3Dnb4t!| z;!?^-b3mU7Gbe#59867(EObncTu?GqCk|tEPGn&qkDz&ClFK-7Ova~;f^)&;|Kikz zbZj5lu6+;F_f1G@Alx!A*OaF?P!y$cIqqV=V><N)OjLR#^r|US`QJJRve1tEa;4YC z21~(7&jOX1YIDPsGrOwjFR6QUWqOYz2!Y@5&bG-`^HwFMZq+z`{Nppv`=kH;@KJ6{ zUguZB+g1~OB>7LTw1{g2EKd4N>hP}xmGC?Ed5dP>uW-_z97RD;p+6>7_fsX_&|xt@ zjSq<#J0dP@d|W(UbD21NOz8NZ$D4ZoWXtN;Y`fx3p|#4r@E(2ZLdlZ4Qf00ru1EW} Z-Fvi)>P)K=YKQu`x-T|K31M_I^?zCXe7^ty diff --git a/Misc/NEWS.d/next/Library/2025-11-01-00-36-14.gh-issue-140874.eAWt3K.rst b/Misc/NEWS.d/next/Library/2025-11-01-00-36-14.gh-issue-140874.eAWt3K.rst new file mode 100644 index 00000000000..a48162de76b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-11-01-00-36-14.gh-issue-140874.eAWt3K.rst @@ -0,0 +1 @@ +Bump the version of pip bundled in ensurepip to version 25.3 From f701f98052e906af9a065d68bdf2398ef3b476d9 Mon Sep 17 00:00:00 2001 From: Ken Jin <kenjin@python.org> Date: Sun, 2 Nov 2025 00:22:59 +0800 Subject: [PATCH 354/373] gh-140312: Set lltrace on JIT debug builds (GH-140313) Co-authored-by: Mark Shannon <mark@hotpy.org> --- Tools/jit/template.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Tools/jit/template.c b/Tools/jit/template.c index 8f71010a1af..2f146014a1c 100644 --- a/Tools/jit/template.c +++ b/Tools/jit/template.c @@ -69,9 +69,11 @@ do { \ } while (0) #undef LLTRACE_RESUME_FRAME -#define LLTRACE_RESUME_FRAME() \ - do { \ - } while (0) +#ifdef Py_DEBUG +#define LLTRACE_RESUME_FRAME() (frame->lltrace = 0) +#else +#define LLTRACE_RESUME_FRAME() do {} while (0) +#endif #define PATCH_JUMP(ALIAS) \ do { \ From b1554146c29182803d1df23d6367c07a429d21ba Mon Sep 17 00:00:00 2001 From: Dino Viehland <dinoviehland@meta.com> Date: Sat, 1 Nov 2025 12:23:58 -0400 Subject: [PATCH 355/373] gh-140868: Don't rely on undefined left shift behavior in assert (#140869) Don't rely on undefined left shift behavior in assert --- Include/internal/pycore_stackref.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Include/internal/pycore_stackref.h b/Include/internal/pycore_stackref.h index 94fcb1d8aee..15a703a0820 100644 --- a/Include/internal/pycore_stackref.h +++ b/Include/internal/pycore_stackref.h @@ -403,7 +403,8 @@ PyStackRef_IsTaggedInt(_PyStackRef i) static inline _PyStackRef PyStackRef_TagInt(intptr_t i) { - assert(Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, (i << Py_TAGGED_SHIFT), Py_TAGGED_SHIFT) == i); + assert(Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, (intptr_t)(((uintptr_t)i) << Py_TAGGED_SHIFT), + Py_TAGGED_SHIFT) == i); return (_PyStackRef){ .bits = ((((uintptr_t)i) << Py_TAGGED_SHIFT) | Py_INT_TAG) }; } From 2f60b8f02fe7cb83dd589d9664460082c13e85ef Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya <141550576+XChaitanyaX@users.noreply.github.com> Date: Sat, 1 Nov 2025 22:11:23 +0530 Subject: [PATCH 356/373] gh-140513: Fail to compile if `_Py_TAIL_CALL_INTERP` is set but `preserve_none` and `musttail` do not exist. (GH-140548) Co-authored-by: Chris Eibl <138194463+chris-eibl@users.noreply.github.com> --- .../Build/2025-10-25-08-07-06.gh-issue-140513.6OhLTs.rst | 2 ++ Python/ceval_macros.h | 8 ++++++++ 2 files changed, 10 insertions(+) create mode 100644 Misc/NEWS.d/next/Build/2025-10-25-08-07-06.gh-issue-140513.6OhLTs.rst diff --git a/Misc/NEWS.d/next/Build/2025-10-25-08-07-06.gh-issue-140513.6OhLTs.rst b/Misc/NEWS.d/next/Build/2025-10-25-08-07-06.gh-issue-140513.6OhLTs.rst new file mode 100644 index 00000000000..1035ebf8d78 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2025-10-25-08-07-06.gh-issue-140513.6OhLTs.rst @@ -0,0 +1,2 @@ +Generate a clear compilation error when ``_Py_TAIL_CALL_INTERP`` is enabled but +either ``preserve_none`` or ``musttail`` is not supported. diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 8083913b1a1..868ab6f7558 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -79,6 +79,14 @@ #endif #if _Py_TAIL_CALL_INTERP +# if defined(__clang__) || defined(__GNUC__) +# if !_Py__has_attribute(preserve_none) || !_Py__has_attribute(musttail) +# error "This compiler does not have support for efficient tail calling." +# endif +# elif defined(_MSC_VER) && (_MSC_VER < 1950) +# error "You need at least VS 2026 / PlatformToolset v145 for tail calling." +# endif + // Note: [[clang::musttail]] works for GCC 15, but not __attribute__((musttail)) at the moment. # define Py_MUSTTAIL [[clang::musttail]] # define Py_PRESERVE_NONE_CC __attribute__((preserve_none)) From d12cbf2865d2845d238f697ddace83face814972 Mon Sep 17 00:00:00 2001 From: RayXu <140802139+F18-Maverick@users.noreply.github.com> Date: Sun, 2 Nov 2025 16:31:26 +0800 Subject: [PATCH 357/373] Docs: fix some grammatical errors in `Doc/c-api/dict.rst` (#140899) --- Doc/c-api/dict.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index 0fbe26b56c0..0abbd662dad 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -50,7 +50,7 @@ Dictionary Objects .. c:function:: int PyDict_Contains(PyObject *p, PyObject *key) - Determine if dictionary *p* contains *key*. If an item in *p* is matches + Determine if dictionary *p* contains *key*. If an item in *p* matches *key*, return ``1``, otherwise return ``0``. On error, return ``-1``. This is equivalent to the Python expression ``key in p``. @@ -198,7 +198,7 @@ Dictionary Objects .. c:function:: int PyDict_Pop(PyObject *p, PyObject *key, PyObject **result) Remove *key* from dictionary *p* and optionally return the removed value. - Do not raise :exc:`KeyError` if the key missing. + Do not raise :exc:`KeyError` if the key is missing. - If the key is present, set *\*result* to a new reference to the removed value if *result* is not ``NULL``, and return ``1``. @@ -207,7 +207,7 @@ Dictionary Objects - On error, raise an exception and return ``-1``. Similar to :meth:`dict.pop`, but without the default value and - not raising :exc:`KeyError` if the key missing. + not raising :exc:`KeyError` if the key is missing. .. versionadded:: 3.13 From da65f38a94c3da515ef7e5081cb5fe81ce97f98e Mon Sep 17 00:00:00 2001 From: Sergey Miryanov <sergey.miryanov@gmail.com> Date: Sun, 2 Nov 2025 16:04:49 +0500 Subject: [PATCH 358/373] gh-134786: raise error if `Py_TPFLAGS_MANAGED_WEAKREF` or `Py_TPFLAGS_MANAGED_DICT` is used without `Py_TPFLAGS_HAVE_GC` set (#135863) --- Doc/c-api/typeobj.rst | 4 ++- Doc/extending/newtypes.rst | 2 ++ Doc/whatsnew/3.15.rst | 8 +++++ Include/object.h | 2 +- Lib/test/test_capi/test_type.py | 7 ++++ ...-06-24-13-12-58.gh-issue-134786.MF0VVk.rst | 2 ++ Modules/_testcapimodule.c | 35 +++++++++++++++++++ Objects/typeobject.c | 14 ++++++++ 8 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-13-12-58.gh-issue-134786.MF0VVk.rst diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 5b7cf0c4502..59c26a713e4 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -1260,7 +1260,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) This bit indicates that instances of the class have a :attr:`~object.__dict__` attribute, and that the space for the dictionary is managed by the VM. - If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` should also be set. + If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` must also be set. The type traverse function must call :c:func:`PyObject_VisitManagedDict` and its clear function must call :c:func:`PyObject_ClearManagedDict`. @@ -1278,6 +1278,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) This bit indicates that instances of the class should be weakly referenceable. + If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` must also be set. + .. versionadded:: 3.12 **Inheritance:** diff --git a/Doc/extending/newtypes.rst b/Doc/extending/newtypes.rst index e3612f3a187..26085b5cebd 100644 --- a/Doc/extending/newtypes.rst +++ b/Doc/extending/newtypes.rst @@ -560,6 +560,8 @@ For an object to be weakly referenceable, the extension type must set the field. The legacy :c:member:`~PyTypeObject.tp_weaklistoffset` field should be left as zero. +If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` should also be set. + Concretely, here is how the statically declared type object would look:: static PyTypeObject TrivialType = { diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index f70345dd2b8..7338fb51964 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -951,6 +951,14 @@ New features (Contributed by Victor Stinner in :gh:`111489`.) +Changed C APIs +-------------- + +* If the :c:macro:`Py_TPFLAGS_MANAGED_DICT` or :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF` + flag is set then :c:macro:`Py_TPFLAGS_HAVE_GC` must be set too. + (Contributed by Sergey Miryanov in :gh:`134786`) + + Porting to Python 3.15 ---------------------- diff --git a/Include/object.h b/Include/object.h index 7f4b35df3b6..291e4f0a7ed 100644 --- a/Include/object.h +++ b/Include/object.h @@ -529,7 +529,7 @@ given type object has a specified feature. #define Py_TPFLAGS_INLINE_VALUES (1 << 2) /* Placement of weakref pointers are managed by the VM, not by the type. - * The VM will automatically set tp_weaklistoffset. + * The VM will automatically set tp_weaklistoffset. Implies Py_TPFLAGS_HAVE_GC. */ #define Py_TPFLAGS_MANAGED_WEAKREF (1 << 3) diff --git a/Lib/test/test_capi/test_type.py b/Lib/test/test_capi/test_type.py index 15fb4a93e2a..93874fbee32 100644 --- a/Lib/test/test_capi/test_type.py +++ b/Lib/test/test_capi/test_type.py @@ -274,3 +274,10 @@ def test_extension_managed_dict_type(self): obj.__dict__ = {'bar': 3} self.assertEqual(obj.__dict__, {'bar': 3}) self.assertEqual(obj.bar, 3) + + def test_extension_managed_weakref_nogc_type(self): + msg = ("type _testcapi.ManagedWeakrefNoGCType " + "has the Py_TPFLAGS_MANAGED_WEAKREF " + "flag but not Py_TPFLAGS_HAVE_GC flag") + with self.assertRaisesRegex(SystemError, msg): + _testcapi.create_managed_weakref_nogc_type() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-13-12-58.gh-issue-134786.MF0VVk.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-13-12-58.gh-issue-134786.MF0VVk.rst new file mode 100644 index 00000000000..664e4d2db38 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-13-12-58.gh-issue-134786.MF0VVk.rst @@ -0,0 +1,2 @@ +If :c:macro:`Py_TPFLAGS_MANAGED_DICT` and :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF` +are used, then :c:macro:`Py_TPFLAGS_HAVE_GC` must be used as well. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 4e73be20e1b..e29b9ae354b 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2562,6 +2562,39 @@ toggle_reftrace_printer(PyObject *ob, PyObject *arg) Py_RETURN_NONE; } + +typedef struct { + PyObject_HEAD +} ManagedWeakrefNoGCObject; + +static void +ManagedWeakrefNoGC_dealloc(PyObject *self) +{ + PyObject_ClearWeakRefs(self); + PyTypeObject *tp = Py_TYPE(self); + tp->tp_free(self); + Py_DECREF(tp); +} + +static PyType_Slot ManagedWeakrefNoGC_slots[] = { + {Py_tp_dealloc, ManagedWeakrefNoGC_dealloc}, + {0, 0} +}; + +static PyType_Spec ManagedWeakrefNoGC_spec = { + .name = "_testcapi.ManagedWeakrefNoGCType", + .basicsize = sizeof(ManagedWeakrefNoGCObject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_MANAGED_WEAKREF), + .slots = ManagedWeakrefNoGC_slots, +}; + +static PyObject * +create_managed_weakref_nogc_type(PyObject *self, PyObject *Py_UNUSED(args)) +{ + return PyType_FromSpec(&ManagedWeakrefNoGC_spec); +} + + static PyMethodDef TestMethods[] = { {"set_errno", set_errno, METH_VARARGS}, {"test_config", test_config, METH_NOARGS}, @@ -2656,6 +2689,8 @@ static PyMethodDef TestMethods[] = { {"test_atexit", test_atexit, METH_NOARGS}, {"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL}, {"toggle_reftrace_printer", toggle_reftrace_printer, METH_O}, + {"create_managed_weakref_nogc_type", + create_managed_weakref_nogc_type, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 5841deb454d..d5695015807 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -8898,6 +8898,13 @@ type_ready_preheader(PyTypeObject *type) type->tp_name); return -1; } + if (!(type->tp_flags & Py_TPFLAGS_HAVE_GC)) { + PyErr_Format(PyExc_SystemError, + "type %s has the Py_TPFLAGS_MANAGED_DICT flag " + "but not Py_TPFLAGS_HAVE_GC flag", + type->tp_name); + return -1; + } type->tp_dictoffset = -1; } if (type->tp_flags & Py_TPFLAGS_MANAGED_WEAKREF) { @@ -8910,6 +8917,13 @@ type_ready_preheader(PyTypeObject *type) type->tp_name); return -1; } + if (!(type->tp_flags & Py_TPFLAGS_HAVE_GC)) { + PyErr_Format(PyExc_SystemError, + "type %s has the Py_TPFLAGS_MANAGED_WEAKREF flag " + "but not Py_TPFLAGS_HAVE_GC flag", + type->tp_name); + return -1; + } type->tp_weaklistoffset = MANAGED_WEAKREF_OFFSET; } return 0; From 173cc53d9fdc596d7a19102fa407c829f9c71516 Mon Sep 17 00:00:00 2001 From: RayXu <140802139+F18-Maverick@users.noreply.github.com> Date: Sun, 2 Nov 2025 21:49:49 +0800 Subject: [PATCH 359/373] Docs: avoid informal formulation in `Doc/c-api/conversion.rst` (#140898) --- Doc/c-api/conversion.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/conversion.rst b/Doc/c-api/conversion.rst index c92ef4c653a..cc7a3d9d956 100644 --- a/Doc/c-api/conversion.rst +++ b/Doc/c-api/conversion.rst @@ -41,7 +41,7 @@ The return value (*rv*) for these functions should be interpreted as follows: ``rv + 1`` bytes would have been needed to succeed. ``str[size-1]`` is ``'\0'`` in this case. -* When ``rv < 0``, "something bad happened." ``str[size-1]`` is ``'\0'`` in +* When ``rv < 0``, the output conversion failed and ``str[size-1]`` is ``'\0'`` in this case too, but the rest of *str* is undefined. The exact cause of the error depends on the underlying platform. From 6d45cd8dbb07ae020ec07f2c3375dd06e52377f6 Mon Sep 17 00:00:00 2001 From: "Jiucheng(Oliver)" <git.jiucheng@gmail.com> Date: Sun, 2 Nov 2025 09:32:14 -0500 Subject: [PATCH 360/373] gh-135307: Fix email error when policy max_line_length is set to 0 or None (#135367) RDM: Like the change made in a earlier PR to the folder, we can/must use 'maxlen' as a stand in for 'unlimited' when computing line lengths when max_line_length is 0 or None; otherwise the computation results in a traceback. --- Lib/email/contentmanager.py | 12 +++++---- Lib/test/test_email/test_message.py | 26 +++++++++++++++++++ ...-06-10-18-02-29.gh-issue-135307.fXGrcK.rst | 2 ++ 3 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-06-10-18-02-29.gh-issue-135307.fXGrcK.rst diff --git a/Lib/email/contentmanager.py b/Lib/email/contentmanager.py index b4f5830bead..11d1536db27 100644 --- a/Lib/email/contentmanager.py +++ b/Lib/email/contentmanager.py @@ -2,6 +2,7 @@ import email.charset import email.message import email.errors +import sys from email import quoprimime class ContentManager: @@ -142,13 +143,15 @@ def _encode_base64(data, max_line_length): def _encode_text(string, charset, cte, policy): + # If max_line_length is 0 or None, there is no limit. + maxlen = policy.max_line_length or sys.maxsize lines = string.encode(charset).splitlines() linesep = policy.linesep.encode('ascii') def embedded_body(lines): return linesep.join(lines) + linesep def normal_body(lines): return b'\n'.join(lines) + b'\n' if cte is None: # Use heuristics to decide on the "best" encoding. - if max((len(x) for x in lines), default=0) <= policy.max_line_length: + if max(map(len, lines), default=0) <= maxlen: try: return '7bit', normal_body(lines).decode('ascii') except UnicodeDecodeError: @@ -156,8 +159,7 @@ def normal_body(lines): return b'\n'.join(lines) + b'\n' if policy.cte_type == '8bit': return '8bit', normal_body(lines).decode('ascii', 'surrogateescape') sniff = embedded_body(lines[:10]) - sniff_qp = quoprimime.body_encode(sniff.decode('latin-1'), - policy.max_line_length) + sniff_qp = quoprimime.body_encode(sniff.decode('latin-1'), maxlen) sniff_base64 = binascii.b2a_base64(sniff) # This is a little unfair to qp; it includes lineseps, base64 doesn't. if len(sniff_qp) > len(sniff_base64): @@ -172,9 +174,9 @@ def normal_body(lines): return b'\n'.join(lines) + b'\n' data = normal_body(lines).decode('ascii', 'surrogateescape') elif cte == 'quoted-printable': data = quoprimime.body_encode(normal_body(lines).decode('latin-1'), - policy.max_line_length) + maxlen) elif cte == 'base64': - data = _encode_base64(embedded_body(lines), policy.max_line_length) + data = _encode_base64(embedded_body(lines), maxlen) else: raise ValueError("Unknown content transfer encoding {}".format(cte)) return cte, data diff --git a/Lib/test/test_email/test_message.py b/Lib/test/test_email/test_message.py index b4128f70f18..56ad446694d 100644 --- a/Lib/test/test_email/test_message.py +++ b/Lib/test/test_email/test_message.py @@ -1004,6 +1004,32 @@ def test_folding_with_long_nospace_http_policy_1(self): parsed_msg = message_from_bytes(m.as_bytes(), policy=policy.default) self.assertEqual(parsed_msg['Message-ID'], m['Message-ID']) + def test_no_wrapping_max_line_length(self): + # Test that falsey 'max_line_length' are converted to sys.maxsize. + for n in [0, None]: + with self.subTest(max_line_length=n): + self.do_test_no_wrapping_max_line_length(n) + + def do_test_no_wrapping_max_line_length(self, falsey): + self.assertFalse(falsey) + pol = policy.default.clone(max_line_length=falsey) + subj = "S" * 100 + body = "B" * 100 + msg = EmailMessage(policy=pol) + msg["From"] = "a@ex.com" + msg["To"] = "b@ex.com" + msg["Subject"] = subj + msg.set_content(body) + + raw = msg.as_bytes() + self.assertNotIn(b"=\n", raw, + "Found fold indicator; wrapping not disabled") + + parsed = message_from_bytes(raw, policy=policy.default) + self.assertEqual(parsed["Subject"], subj) + parsed_body = parsed.get_body().get_content().rstrip('\n') + self.assertEqual(parsed_body, body) + def test_invalid_header_names(self): invalid_headers = [ ('Invalid Header', 'contains space'), diff --git a/Misc/NEWS.d/next/Library/2025-06-10-18-02-29.gh-issue-135307.fXGrcK.rst b/Misc/NEWS.d/next/Library/2025-06-10-18-02-29.gh-issue-135307.fXGrcK.rst new file mode 100644 index 00000000000..47e1feb5cbf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-06-10-18-02-29.gh-issue-135307.fXGrcK.rst @@ -0,0 +1,2 @@ +:mod:`email`: Fix exception in ``set_content()`` when encoding text +and max_line_length is set to ``0`` or ``None`` (unlimited). From 9e5628ad68b768e2a8f0bcd3204b0027e238c45c Mon Sep 17 00:00:00 2001 From: Sebastian Rittau <srittau@rittau.biz> Date: Sun, 2 Nov 2025 22:56:59 +0100 Subject: [PATCH 361/373] gh-140808: Remove __class_getitem__ from mailbox._ProxyFile (#140838) Co-authored-by: Emma Smith <emma@emmatyping.dev> --- Lib/mailbox.py | 2 -- Lib/test/test_genericalias.py | 4 ++-- .../Library/2025-10-31-16-25-13.gh-issue-140808.XBiQ4j.rst | 1 + 3 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-31-16-25-13.gh-issue-140808.XBiQ4j.rst diff --git a/Lib/mailbox.py b/Lib/mailbox.py index b00d9e8634c..4a44642765c 100644 --- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -2090,8 +2090,6 @@ def closed(self): return False return self._file.closed - __class_getitem__ = classmethod(GenericAlias) - class _PartialFile(_ProxyFile): """A read-only wrapper of part of a file.""" diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py index 4e08adaca05..9df9296e26a 100644 --- a/Lib/test/test_genericalias.py +++ b/Lib/test/test_genericalias.py @@ -17,7 +17,7 @@ from functools import partial, partialmethod, cached_property from graphlib import TopologicalSorter from logging import LoggerAdapter, StreamHandler -from mailbox import Mailbox, _PartialFile +from mailbox import Mailbox try: import ctypes except ImportError: @@ -117,7 +117,7 @@ class BaseTest(unittest.TestCase): Iterable, Iterator, Reversible, Container, Collection, - Mailbox, _PartialFile, + Mailbox, ContextVar, Token, Field, Set, MutableSet, diff --git a/Misc/NEWS.d/next/Library/2025-10-31-16-25-13.gh-issue-140808.XBiQ4j.rst b/Misc/NEWS.d/next/Library/2025-10-31-16-25-13.gh-issue-140808.XBiQ4j.rst new file mode 100644 index 00000000000..090f39c6e25 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-31-16-25-13.gh-issue-140808.XBiQ4j.rst @@ -0,0 +1 @@ +The internal class ``mailbox._ProxyFile`` is no longer a parameterized generic. From 31de83d5e2e17f4e9a37e08b384bab916e1da7c1 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya <141550576+XChaitanyaX@users.noreply.github.com> Date: Mon, 3 Nov 2025 03:28:16 +0530 Subject: [PATCH 362/373] gh-140693: Improve `argparse` documentation about controlling color (#140737) --- Doc/library/argparse.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index 418f514995d..9655db4f301 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -638,6 +638,11 @@ by setting ``color`` to ``False``:: ... help='an integer for the accumulator') >>> parser.parse_args(['--help']) +Note that when ``color=True``, colored output depends on both environment +variables and terminal capabilities. However, if ``color=False``, colored +output is always disabled, even if environment variables like ``FORCE_COLOR`` +are set. + .. versionadded:: 3.14 From e66f87ca73516efb4aec1f2f056d2a4efd73248a Mon Sep 17 00:00:00 2001 From: dr-carlos <77367421+dr-carlos@users.noreply.github.com> Date: Mon, 3 Nov 2025 09:45:47 +1030 Subject: [PATCH 363/373] gh-138425: Correctly partially evaluate global generics with undefined params in `ref.evaluate(format=Format.FORWARDREF)` (#138430) Co-authored-by: sobolevn <mail@sobolevn.me> --- Lib/annotationlib.py | 5 +++- Lib/test/test_annotationlib.py | 26 +++++++++++++++++++ ...-09-03-18-26-07.gh-issue-138425.cVE9Ho.rst | 2 ++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-09-03-18-26-07.gh-issue-138425.cVE9Ho.rst diff --git a/Lib/annotationlib.py b/Lib/annotationlib.py index 81886a0467d..16dbb128bc9 100644 --- a/Lib/annotationlib.py +++ b/Lib/annotationlib.py @@ -187,8 +187,11 @@ def evaluate( except Exception: if not is_forwardref_format: raise + + # All variables, in scoping order, should be checked before + # triggering __missing__ to create a _Stringifier. new_locals = _StringifierDict( - {**builtins.__dict__, **locals}, + {**builtins.__dict__, **globals, **locals}, globals=globals, owner=owner, is_class=self.__forward_is_class__, diff --git a/Lib/test/test_annotationlib.py b/Lib/test/test_annotationlib.py index 8da4ff096e7..7b08f58bfb8 100644 --- a/Lib/test/test_annotationlib.py +++ b/Lib/test/test_annotationlib.py @@ -1877,6 +1877,32 @@ def test_name_lookup_without_eval(self): self.assertEqual(exc.exception.name, "doesntexist") + def test_evaluate_undefined_generic(self): + # Test the codepath where have to eval() with undefined variables. + class C: + x: alias[int, undef] + + generic = get_annotations(C, format=Format.FORWARDREF)["x"].evaluate( + format=Format.FORWARDREF, + globals={"alias": dict} + ) + self.assertNotIsInstance(generic, ForwardRef) + self.assertIs(generic.__origin__, dict) + self.assertEqual(len(generic.__args__), 2) + self.assertIs(generic.__args__[0], int) + self.assertIsInstance(generic.__args__[1], ForwardRef) + + generic = get_annotations(C, format=Format.FORWARDREF)["x"].evaluate( + format=Format.FORWARDREF, + globals={"alias": Union}, + locals={"alias": dict} + ) + self.assertNotIsInstance(generic, ForwardRef) + self.assertIs(generic.__origin__, dict) + self.assertEqual(len(generic.__args__), 2) + self.assertIs(generic.__args__[0], int) + self.assertIsInstance(generic.__args__[1], ForwardRef) + def test_fwdref_invalid_syntax(self): fr = ForwardRef("if") with self.assertRaises(SyntaxError): diff --git a/Misc/NEWS.d/next/Library/2025-09-03-18-26-07.gh-issue-138425.cVE9Ho.rst b/Misc/NEWS.d/next/Library/2025-09-03-18-26-07.gh-issue-138425.cVE9Ho.rst new file mode 100644 index 00000000000..328e5988cb0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-03-18-26-07.gh-issue-138425.cVE9Ho.rst @@ -0,0 +1,2 @@ +Fix partial evaluation of :class:`annotationlib.ForwardRef` objects which rely +on names defined as globals. From 63e01d6bae9ddc9ff35aca2134945670eacef163 Mon Sep 17 00:00:00 2001 From: dr-carlos <77367421+dr-carlos@users.noreply.github.com> Date: Mon, 3 Nov 2025 11:50:30 +1030 Subject: [PATCH 364/373] gh-137969: Fix evaluation of `ref.evaluate(format=Format.FORWARDREF)` objects (#138075) Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com> --- Lib/annotationlib.py | 14 ++++++++------ Lib/test/test_annotationlib.py | 9 +++++++++ .../2025-08-22-23-50-38.gh-issue-137969.Fkvis3.rst | 2 ++ 3 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-08-22-23-50-38.gh-issue-137969.Fkvis3.rst diff --git a/Lib/annotationlib.py b/Lib/annotationlib.py index 16dbb128bc9..26e7c200248 100644 --- a/Lib/annotationlib.py +++ b/Lib/annotationlib.py @@ -159,12 +159,12 @@ def evaluate( type_params = getattr(owner, "__type_params__", None) # Type parameters exist in their own scope, which is logically - # between the locals and the globals. We simulate this by adding - # them to the globals. + # between the locals and the globals. + type_param_scope = {} if type_params is not None: - globals = dict(globals) for param in type_params: - globals[param.__name__] = param + type_param_scope[param.__name__] = param + if self.__extra_names__: locals = {**locals, **self.__extra_names__} @@ -172,6 +172,8 @@ def evaluate( if arg.isidentifier() and not keyword.iskeyword(arg): if arg in locals: return locals[arg] + elif arg in type_param_scope: + return type_param_scope[arg] elif arg in globals: return globals[arg] elif hasattr(builtins, arg): @@ -183,7 +185,7 @@ def evaluate( else: code = self.__forward_code__ try: - return eval(code, globals=globals, locals=locals) + return eval(code, globals=globals, locals={**type_param_scope, **locals}) except Exception: if not is_forwardref_format: raise @@ -191,7 +193,7 @@ def evaluate( # All variables, in scoping order, should be checked before # triggering __missing__ to create a _Stringifier. new_locals = _StringifierDict( - {**builtins.__dict__, **globals, **locals}, + {**builtins.__dict__, **globals, **type_param_scope, **locals}, globals=globals, owner=owner, is_class=self.__forward_is_class__, diff --git a/Lib/test/test_annotationlib.py b/Lib/test/test_annotationlib.py index 7b08f58bfb8..08f7161a273 100644 --- a/Lib/test/test_annotationlib.py +++ b/Lib/test/test_annotationlib.py @@ -1911,6 +1911,15 @@ def test_fwdref_invalid_syntax(self): with self.assertRaises(SyntaxError): fr.evaluate() + def test_re_evaluate_generics(self): + global alias + class C: + x: alias[int] + + evaluated = get_annotations(C, format=Format.FORWARDREF)["x"].evaluate(format=Format.FORWARDREF) + alias = list + self.assertEqual(evaluated.evaluate(), list[int]) + class TestAnnotationLib(unittest.TestCase): def test__all__(self): diff --git a/Misc/NEWS.d/next/Library/2025-08-22-23-50-38.gh-issue-137969.Fkvis3.rst b/Misc/NEWS.d/next/Library/2025-08-22-23-50-38.gh-issue-137969.Fkvis3.rst new file mode 100644 index 00000000000..59f9e6e3d33 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-08-22-23-50-38.gh-issue-137969.Fkvis3.rst @@ -0,0 +1,2 @@ +Fix :meth:`annotationlib.ForwardRef.evaluate` returning :class:`annotationlib.ForwardRef` +objects which do not update in new contexts. From 121c219e302365e63eef0aef81b94a3de7a37fec Mon Sep 17 00:00:00 2001 From: Frost Ming <me@frostming.com> Date: Mon, 3 Nov 2025 10:47:18 +0800 Subject: [PATCH 365/373] Remove redundant `sys.exit(2)` call in `pdb` CLI (#139948) --- Lib/pdb.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/pdb.py b/Lib/pdb.py index 4ee12d17a61..fdc74198582 100644 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -3603,7 +3603,6 @@ def main(): invalid_args = list(itertools.takewhile(lambda a: a.startswith('-'), args)) if invalid_args: parser.error(f"unrecognized arguments: {' '.join(invalid_args)}") - sys.exit(2) if opts.module: file = opts.module From 349de57839afcd1a1813a0cb53ba9cf1610ba7a5 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra <jelle.zijlstra@gmail.com> Date: Sun, 2 Nov 2025 21:35:15 -0800 Subject: [PATCH 366/373] Revert "gh-137969: Fix evaluation of `ref.evaluate(format=Format.FORWARDREF)` objects (#138075)" (#140930) This reverts commit 63e01d6bae9ddc9ff35aca2134945670eacef163. --- Lib/annotationlib.py | 14 ++++++-------- Lib/test/test_annotationlib.py | 9 --------- .../2025-08-22-23-50-38.gh-issue-137969.Fkvis3.rst | 2 -- 3 files changed, 6 insertions(+), 19 deletions(-) delete mode 100644 Misc/NEWS.d/next/Library/2025-08-22-23-50-38.gh-issue-137969.Fkvis3.rst diff --git a/Lib/annotationlib.py b/Lib/annotationlib.py index 26e7c200248..16dbb128bc9 100644 --- a/Lib/annotationlib.py +++ b/Lib/annotationlib.py @@ -159,12 +159,12 @@ def evaluate( type_params = getattr(owner, "__type_params__", None) # Type parameters exist in their own scope, which is logically - # between the locals and the globals. - type_param_scope = {} + # between the locals and the globals. We simulate this by adding + # them to the globals. if type_params is not None: + globals = dict(globals) for param in type_params: - type_param_scope[param.__name__] = param - + globals[param.__name__] = param if self.__extra_names__: locals = {**locals, **self.__extra_names__} @@ -172,8 +172,6 @@ def evaluate( if arg.isidentifier() and not keyword.iskeyword(arg): if arg in locals: return locals[arg] - elif arg in type_param_scope: - return type_param_scope[arg] elif arg in globals: return globals[arg] elif hasattr(builtins, arg): @@ -185,7 +183,7 @@ def evaluate( else: code = self.__forward_code__ try: - return eval(code, globals=globals, locals={**type_param_scope, **locals}) + return eval(code, globals=globals, locals=locals) except Exception: if not is_forwardref_format: raise @@ -193,7 +191,7 @@ def evaluate( # All variables, in scoping order, should be checked before # triggering __missing__ to create a _Stringifier. new_locals = _StringifierDict( - {**builtins.__dict__, **globals, **type_param_scope, **locals}, + {**builtins.__dict__, **globals, **locals}, globals=globals, owner=owner, is_class=self.__forward_is_class__, diff --git a/Lib/test/test_annotationlib.py b/Lib/test/test_annotationlib.py index 08f7161a273..7b08f58bfb8 100644 --- a/Lib/test/test_annotationlib.py +++ b/Lib/test/test_annotationlib.py @@ -1911,15 +1911,6 @@ def test_fwdref_invalid_syntax(self): with self.assertRaises(SyntaxError): fr.evaluate() - def test_re_evaluate_generics(self): - global alias - class C: - x: alias[int] - - evaluated = get_annotations(C, format=Format.FORWARDREF)["x"].evaluate(format=Format.FORWARDREF) - alias = list - self.assertEqual(evaluated.evaluate(), list[int]) - class TestAnnotationLib(unittest.TestCase): def test__all__(self): diff --git a/Misc/NEWS.d/next/Library/2025-08-22-23-50-38.gh-issue-137969.Fkvis3.rst b/Misc/NEWS.d/next/Library/2025-08-22-23-50-38.gh-issue-137969.Fkvis3.rst deleted file mode 100644 index 59f9e6e3d33..00000000000 --- a/Misc/NEWS.d/next/Library/2025-08-22-23-50-38.gh-issue-137969.Fkvis3.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :meth:`annotationlib.ForwardRef.evaluate` returning :class:`annotationlib.ForwardRef` -objects which do not update in new contexts. From 248ce9fa8c7a1ed1912e0db31172c8cd2c298b37 Mon Sep 17 00:00:00 2001 From: AN Long <aisk@users.noreply.github.com> Date: Mon, 3 Nov 2025 17:14:22 +0900 Subject: [PATCH 367/373] gh-140826: Compare winreg.HKEYType by the internal handle value (GH-140843) --- Doc/library/winreg.rst | 8 +++-- Lib/test/test_winreg.py | 27 +++++++++++++++ ...-11-01-00-34-53.gh-issue-140826.JEDd7U.rst | 2 ++ PC/winreg.c | 34 ++++++++++++++++--- 4 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-11-01-00-34-53.gh-issue-140826.JEDd7U.rst diff --git a/Doc/library/winreg.rst b/Doc/library/winreg.rst index 83c49876d26..df8fb83a018 100644 --- a/Doc/library/winreg.rst +++ b/Doc/library/winreg.rst @@ -771,8 +771,9 @@ Handle objects provide semantics for :meth:`~object.__bool__` -- thus :: will print ``Yes`` if the handle is currently valid (has not been closed or detached). -The object also support comparison semantics, so handle objects will compare -true if they both reference the same underlying Windows handle value. +The object also support equality comparison semantics, so handle objects will +compare equal if they both reference the same underlying Windows handle value. +Closed handle objects (those with a handle value of zero) always compare equal. Handle objects can be converted to an integer (e.g., using the built-in :func:`int` function), in which case the underlying Windows handle value is @@ -815,3 +816,6 @@ integer handle, and also disconnect the Windows handle from the handle object. will automatically close *key* when control leaves the :keyword:`with` block. +.. versionchanged:: next + Handle objects are now compared by their underlying Windows handle value + instead of object identity for equality comparisons. diff --git a/Lib/test/test_winreg.py b/Lib/test/test_winreg.py index 6f2a6ac900b..733d30b3922 100644 --- a/Lib/test/test_winreg.py +++ b/Lib/test/test_winreg.py @@ -209,6 +209,33 @@ def _test_named_args(self, key, sub_key): access=KEY_ALL_ACCESS) as okey: self.assertTrue(okey.handle != 0) + def test_hkey_comparison(self): + """Test HKEY comparison by handle value rather than object identity.""" + key1 = OpenKey(HKEY_CURRENT_USER, None) + key2 = OpenKey(HKEY_CURRENT_USER, None) + key3 = OpenKey(HKEY_LOCAL_MACHINE, None) + + self.addCleanup(CloseKey, key1) + self.addCleanup(CloseKey, key2) + self.addCleanup(CloseKey, key3) + + self.assertEqual(key1.handle, key2.handle) + self.assertTrue(key1 == key2) + self.assertFalse(key1 != key2) + + self.assertTrue(key1 != key3) + self.assertFalse(key1 == key3) + + # Closed keys should be equal (all have handle=0) + CloseKey(key1) + CloseKey(key2) + CloseKey(key3) + + self.assertEqual(key1.handle, 0) + self.assertEqual(key2.handle, 0) + self.assertEqual(key3.handle, 0) + self.assertEqual(key2, key3) + class LocalWinregTests(BaseWinregTests): diff --git a/Misc/NEWS.d/next/Library/2025-11-01-00-34-53.gh-issue-140826.JEDd7U.rst b/Misc/NEWS.d/next/Library/2025-11-01-00-34-53.gh-issue-140826.JEDd7U.rst new file mode 100644 index 00000000000..875d15f2f89 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-11-01-00-34-53.gh-issue-140826.JEDd7U.rst @@ -0,0 +1,2 @@ +Now :class:`!winreg.HKEYType` objects are compared by their underlying Windows +registry handle value instead of their object identity. diff --git a/PC/winreg.c b/PC/winreg.c index 8633f29670e..c7bc74728f1 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -181,13 +181,38 @@ PyHKEY_strFunc(PyObject *ob) return PyUnicode_FromFormat("<PyHKEY:%p>", pyhkey->hkey); } -static int -PyHKEY_compareFunc(PyObject *ob1, PyObject *ob2) +static PyObject * +PyHKEY_richcompare(PyObject *ob1, PyObject *ob2, int op) { + /* Both objects must be PyHKEY objects from the same module */ + if (Py_TYPE(ob1) != Py_TYPE(ob2)) { + Py_RETURN_NOTIMPLEMENTED; + } + PyHKEYObject *pyhkey1 = (PyHKEYObject *)ob1; PyHKEYObject *pyhkey2 = (PyHKEYObject *)ob2; - return pyhkey1 == pyhkey2 ? 0 : - (pyhkey1 < pyhkey2 ? -1 : 1); + HKEY hkey1 = pyhkey1->hkey; + HKEY hkey2 = pyhkey2->hkey; + int result; + + switch (op) { + case Py_EQ: + result = (hkey1 == hkey2); + break; + case Py_NE: + result = (hkey1 != hkey2); + break; + default: + /* Only support equality comparisons, not ordering */ + Py_RETURN_NOTIMPLEMENTED; + } + + if (result) { + Py_RETURN_TRUE; + } + else { + Py_RETURN_FALSE; + } } static Py_hash_t @@ -365,6 +390,7 @@ static PyType_Slot pyhkey_type_slots[] = { {Py_tp_traverse, _PyObject_VisitType}, {Py_tp_hash, PyHKEY_hashFunc}, {Py_tp_str, PyHKEY_strFunc}, + {Py_tp_richcompare, PyHKEY_richcompare}, // Number protocol {Py_nb_add, PyHKEY_binaryFailureFunc}, From 7a9437d98641e3c3749ab2fd9fb54eac7614f9af Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra <jelle.zijlstra@gmail.com> Date: Mon, 3 Nov 2025 06:50:37 -0800 Subject: [PATCH 368/373] gh-140348: Fix using | on unusual objects plus Unions (#140383) --- Lib/test/test_typing.py | 9 +++++++++ ...25-10-20-12-33-49.gh-issue-140348.SAKnQZ.rst | 3 +++ Objects/unionobject.c | 17 ++++++++++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-20-12-33-49.gh-issue-140348.SAKnQZ.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 4076004bc13..e896df51844 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -2283,6 +2283,15 @@ class Ints(enum.IntEnum): self.assertEqual(Union[Literal[1], Literal[Ints.B], Literal[True]].__args__, (Literal[1], Literal[Ints.B], Literal[True])) + def test_allow_non_types_in_or(self): + # gh-140348: Test that using | with a Union object allows things that are + # not allowed by is_unionable(). + U1 = Union[int, str] + self.assertEqual(U1 | float, Union[int, str, float]) + self.assertEqual(U1 | "float", Union[int, str, "float"]) + self.assertEqual(float | U1, Union[float, int, str]) + self.assertEqual("float" | U1, Union["float", int, str]) + class TupleTests(BaseTestCase): diff --git a/Misc/NEWS.d/next/Library/2025-10-20-12-33-49.gh-issue-140348.SAKnQZ.rst b/Misc/NEWS.d/next/Library/2025-10-20-12-33-49.gh-issue-140348.SAKnQZ.rst new file mode 100644 index 00000000000..16d5b2a8bf0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-20-12-33-49.gh-issue-140348.SAKnQZ.rst @@ -0,0 +1,3 @@ +Fix regression in Python 3.14.0 where using the ``|`` operator on a +:class:`typing.Union` object combined with an object that is not a type +would raise an error. diff --git a/Objects/unionobject.c b/Objects/unionobject.c index c4ece0fe09f..a47d6193d70 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -393,8 +393,23 @@ static PyGetSetDef union_properties[] = { {0} }; +static PyObject * +union_nb_or(PyObject *a, PyObject *b) +{ + unionbuilder ub; + if (!unionbuilder_init(&ub, true)) { + return NULL; + } + if (!unionbuilder_add_single(&ub, a) || + !unionbuilder_add_single(&ub, b)) { + unionbuilder_finalize(&ub); + return NULL; + } + return make_union(&ub); +} + static PyNumberMethods union_as_number = { - .nb_or = _Py_union_type_or, // Add __or__ function + .nb_or = union_nb_or, // Add __or__ function }; static const char* const cls_attrs[] = { From d590685297165d16df41003c3566b75a0a4ced61 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra <jelle.zijlstra@gmail.com> Date: Mon, 3 Nov 2025 06:54:23 -0800 Subject: [PATCH 369/373] gh-133879: Clean up What's New for 3.15 (#140435) Clean up What's New for 3.15 A bit early but I was reading through it and noticed some issues: - A few improvements were listed in the removals section - The "Porting to 3.15" section in the C API chapter had some changes that aren't about the C API - Some other typos and wording fixes --- Doc/deprecations/pending-removal-in-3.16.rst | 6 +- Doc/whatsnew/3.15.rst | 86 ++++++++++---------- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/Doc/deprecations/pending-removal-in-3.16.rst b/Doc/deprecations/pending-removal-in-3.16.rst index cdd76ee693f..b00c7002b03 100644 --- a/Doc/deprecations/pending-removal-in-3.16.rst +++ b/Doc/deprecations/pending-removal-in-3.16.rst @@ -63,9 +63,9 @@ Pending removal in Python 3.16 * :mod:`logging`: - Support for custom logging handlers with the *strm* argument is deprecated - and scheduled for removal in Python 3.16. Define handlers with the *stream* - argument instead. (Contributed by Mariusz Felisiak in :gh:`115032`.) + * Support for custom logging handlers with the *strm* argument is deprecated + and scheduled for removal in Python 3.16. Define handlers with the *stream* + argument instead. (Contributed by Mariusz Felisiak in :gh:`115032`.) * :mod:`mimetypes`: diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 7338fb51964..5379ac3abba 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -440,6 +440,14 @@ math (Contributed by Bénédikt Tran in :gh:`135853`.) +mimetypes +--------- + +* Add ``application/toml``. (Contributed by Gil Forcada in :gh:`139959`.) +* Rename ``application/x-texinfo`` to ``application/texinfo``. + (Contributed by Charlie Lin in :gh:`140165`) + + mmap ---- @@ -612,6 +620,17 @@ types as described in :pep:`667`. +unicodedata +----------- + +* The Unicode database has been updated to Unicode 17.0.0. + +* Add :func:`unicodedata.isxidstart` and :func:`unicodedata.isxidcontinue` + functions to check whether a character can start or continue a + `Unicode Standard Annex #31 <https://www.unicode.org/reports/tr31/>`_ identifier. + (Contributed by Stan Ulbrych in :gh:`129117`.) + + unittest -------- @@ -720,14 +739,6 @@ importlib.resources (Contributed by Semyon Moroz in :gh:`138044`) -mimetypes ---------- - -* Add ``application/toml``. (Contributed by Gil Forcada in :gh:`139959`.) -* Rename ``application/x-texinfo`` to ``application/texinfo``. - (Contributed by Charlie Lin in :gh:`140165`) - - pathlib ------- @@ -777,7 +788,7 @@ typing (Contributed by Bénédikt Tran in :gh:`133817`.) * Using ``TD = TypedDict("TD")`` or ``TD = TypedDict("TD", None)`` to - construct a :class:`~typing.TypedDict` type with zero field is no + construct a :class:`~typing.TypedDict` type with zero fields is no longer supported. Use ``class TD(TypedDict): pass`` or ``TD = TypedDict("TD", {})`` instead. (Contributed by Bénédikt Tran in :gh:`133823`.) @@ -810,17 +821,6 @@ typing (Contributed by Nikita Sobolev in :gh:`133601`.) -unicodedata ------------ - -* The Unicode database has been updated to Unicode 17.0.0. - -* Add :func:`unicodedata.isxidstart` and :func:`unicodedata.isxidcontinue` - functions to check whether a character can start or continue a - `Unicode Standard Annex #31 <https://www.unicode.org/reports/tr31/>`_ identifier. - (Contributed by Stan Ulbrych in :gh:`129117`.) - - wave ---- @@ -847,7 +847,7 @@ New deprecations * CLI: * Deprecate :option:`-b` and :option:`!-bb` command-line options - and schedule them to become no-op in Python 3.17. + and schedule them to become no-ops in Python 3.17. These were primarily helpers for the Python 2 -> 3 transition. Starting with Python 3.17, no :exc:`BytesWarning` will be raised for these cases; use a type checker instead. @@ -858,8 +858,8 @@ New deprecations * In hash function constructors such as :func:`~hashlib.new` or the direct hash-named constructors such as :func:`~hashlib.md5` and - :func:`~hashlib.sha256`, their optional initial data parameter could - also be passed a keyword argument named ``data=`` or ``string=`` in + :func:`~hashlib.sha256`, the optional initial data parameter could + also be passed as a keyword argument named ``data=`` or ``string=`` in various :mod:`hashlib` implementations. Support for the ``string`` keyword argument name is now deprecated and @@ -962,31 +962,11 @@ Changed C APIs Porting to Python 3.15 ---------------------- -* :class:`sqlite3.Connection` APIs has been cleaned up. - - * All parameters of :func:`sqlite3.connect` except *database* are now keyword-only. - * The first three parameters of methods :meth:`~sqlite3.Connection.create_function` - and :meth:`~sqlite3.Connection.create_aggregate` are now positional-only. - * The first parameter of methods :meth:`~sqlite3.Connection.set_authorizer`, - :meth:`~sqlite3.Connection.set_progress_handler` and - :meth:`~sqlite3.Connection.set_trace_callback` is now positional-only. - - (Contributed by Serhiy Storchaka in :gh:`133595`.) - * Private functions promoted to public C APIs: The |pythoncapi_compat_project| can be used to get most of these new functions on Python 3.14 and older. -* :data:`resource.RLIM_INFINITY` is now always positive. - Passing a negative integer value that corresponded to its old value - (such as ``-1`` or ``-3``, depending on platform) to - :func:`resource.setrlimit` and :func:`resource.prlimit` is now deprecated. - (Contributed by Serhiy Storchaka in :gh:`137044`.) - -* :meth:`~mmap.mmap.resize` has been removed on platforms that don't support the - underlying syscall, instead of raising a :exc:`SystemError`. - Removed C APIs -------------- @@ -1106,3 +1086,23 @@ Porting to Python 3.15 This section lists previously described changes and other bugfixes that may require changes to your code. + +* :class:`sqlite3.Connection` APIs has been cleaned up. + + * All parameters of :func:`sqlite3.connect` except *database* are now keyword-only. + * The first three parameters of methods :meth:`~sqlite3.Connection.create_function` + and :meth:`~sqlite3.Connection.create_aggregate` are now positional-only. + * The first parameter of methods :meth:`~sqlite3.Connection.set_authorizer`, + :meth:`~sqlite3.Connection.set_progress_handler` and + :meth:`~sqlite3.Connection.set_trace_callback` is now positional-only. + + (Contributed by Serhiy Storchaka in :gh:`133595`.) + +* :data:`resource.RLIM_INFINITY` is now always positive. + Passing a negative integer value that corresponded to its old value + (such as ``-1`` or ``-3``, depending on platform) to + :func:`resource.setrlimit` and :func:`resource.prlimit` is now deprecated. + (Contributed by Serhiy Storchaka in :gh:`137044`.) + +* :meth:`~mmap.mmap.resize` has been removed on platforms that don't support the + underlying syscall, instead of raising a :exc:`SystemError`. From b1027d4762435b97546c122dd94290d707b3ff39 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra <jelle.zijlstra@gmail.com> Date: Mon, 3 Nov 2025 07:22:32 -0800 Subject: [PATCH 370/373] gh-138151: Fix annotationlib handling of multiple nonlocals (#138164) --- Lib/annotationlib.py | 40 +++++++++++++------ Lib/test/test_annotationlib.py | 15 +++++++ ...-08-26-08-17-56.gh-issue-138151.I6CdAk.rst | 3 ++ 3 files changed, 45 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-08-26-08-17-56.gh-issue-138151.I6CdAk.rst diff --git a/Lib/annotationlib.py b/Lib/annotationlib.py index 16dbb128bc9..2166dbff0ee 100644 --- a/Lib/annotationlib.py +++ b/Lib/annotationlib.py @@ -85,6 +85,9 @@ def __init__( # These are always set to None here but may be non-None if a ForwardRef # is created through __class__ assignment on a _Stringifier object. self.__globals__ = None + # This may be either a cell object (for a ForwardRef referring to a single name) + # or a dict mapping cell names to cell objects (for a ForwardRef containing references + # to multiple names). self.__cell__ = None self.__extra_names__ = None # These are initially None but serve as a cache and may be set to a non-None @@ -117,7 +120,7 @@ def evaluate( is_forwardref_format = True case _: raise NotImplementedError(format) - if self.__cell__ is not None: + if isinstance(self.__cell__, types.CellType): try: return self.__cell__.cell_contents except ValueError: @@ -160,11 +163,18 @@ def evaluate( # Type parameters exist in their own scope, which is logically # between the locals and the globals. We simulate this by adding - # them to the globals. - if type_params is not None: + # them to the globals. Similar reasoning applies to nonlocals stored in cells. + if type_params is not None or isinstance(self.__cell__, dict): globals = dict(globals) + if type_params is not None: for param in type_params: globals[param.__name__] = param + if isinstance(self.__cell__, dict): + for cell_name, cell_value in self.__cell__.items(): + try: + globals[cell_name] = cell_value.cell_contents + except ValueError: + pass if self.__extra_names__: locals = {**locals, **self.__extra_names__} @@ -202,7 +212,7 @@ def evaluate( except Exception: return self else: - new_locals.transmogrify() + new_locals.transmogrify(self.__cell__) return result def _evaluate(self, globalns, localns, type_params=_sentinel, *, recursive_guard): @@ -274,7 +284,7 @@ def __hash__(self): self.__forward_module__, id(self.__globals__), # dictionaries are not hashable, so hash by identity self.__forward_is_class__, - self.__cell__, + tuple(sorted(self.__cell__.items())) if isinstance(self.__cell__, dict) else self.__cell__, self.__owner__, tuple(sorted(self.__extra_names__.items())) if self.__extra_names__ else None, )) @@ -642,13 +652,15 @@ def __missing__(self, key): self.stringifiers.append(fwdref) return fwdref - def transmogrify(self): + def transmogrify(self, cell_dict): for obj in self.stringifiers: obj.__class__ = ForwardRef obj.__stringifier_dict__ = None # not needed for ForwardRef if isinstance(obj.__ast_node__, str): obj.__arg__ = obj.__ast_node__ obj.__ast_node__ = None + if cell_dict is not None and obj.__cell__ is None: + obj.__cell__ = cell_dict def create_unique_name(self): name = f"__annotationlib_name_{self.next_id}__" @@ -712,7 +724,7 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): globals = _StringifierDict({}, format=format) is_class = isinstance(owner, type) - closure = _build_closure( + closure, _ = _build_closure( annotate, owner, is_class, globals, allow_evaluation=False ) func = types.FunctionType( @@ -756,7 +768,7 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): is_class=is_class, format=format, ) - closure = _build_closure( + closure, cell_dict = _build_closure( annotate, owner, is_class, globals, allow_evaluation=True ) func = types.FunctionType( @@ -774,7 +786,7 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): except Exception: pass else: - globals.transmogrify() + globals.transmogrify(cell_dict) return result # Try again, but do not provide any globals. This allows us to return @@ -786,7 +798,7 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): is_class=is_class, format=format, ) - closure = _build_closure( + closure, cell_dict = _build_closure( annotate, owner, is_class, globals, allow_evaluation=False ) func = types.FunctionType( @@ -797,7 +809,7 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): kwdefaults=annotate.__kwdefaults__, ) result = func(Format.VALUE_WITH_FAKE_GLOBALS) - globals.transmogrify() + globals.transmogrify(cell_dict) if _is_evaluate: if isinstance(result, ForwardRef): return result.evaluate(format=Format.FORWARDREF) @@ -822,14 +834,16 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): def _build_closure(annotate, owner, is_class, stringifier_dict, *, allow_evaluation): if not annotate.__closure__: - return None + return None, None freevars = annotate.__code__.co_freevars new_closure = [] + cell_dict = {} for i, cell in enumerate(annotate.__closure__): if i < len(freevars): name = freevars[i] else: name = "__cell__" + cell_dict[name] = cell new_cell = None if allow_evaluation: try: @@ -850,7 +864,7 @@ def _build_closure(annotate, owner, is_class, stringifier_dict, *, allow_evaluat stringifier_dict.stringifiers.append(fwdref) new_cell = types.CellType(fwdref) new_closure.append(new_cell) - return tuple(new_closure) + return tuple(new_closure), cell_dict def _stringify_single(anno): diff --git a/Lib/test/test_annotationlib.py b/Lib/test/test_annotationlib.py index 7b08f58bfb8..fd5d43b09b9 100644 --- a/Lib/test/test_annotationlib.py +++ b/Lib/test/test_annotationlib.py @@ -1194,6 +1194,21 @@ class RaisesAttributeError: }, ) + def test_nonlocal_in_annotation_scope(self): + class Demo: + nonlocal sequence_b + x: sequence_b + y: sequence_b[int] + + fwdrefs = get_annotations(Demo, format=Format.FORWARDREF) + + self.assertIsInstance(fwdrefs["x"], ForwardRef) + self.assertIsInstance(fwdrefs["y"], ForwardRef) + + sequence_b = list + self.assertIs(fwdrefs["x"].evaluate(), list) + self.assertEqual(fwdrefs["y"].evaluate(), list[int]) + def test_raises_error_from_value(self): # test that if VALUE is the only supported format, but raises an error # that error is propagated from get_annotations diff --git a/Misc/NEWS.d/next/Library/2025-08-26-08-17-56.gh-issue-138151.I6CdAk.rst b/Misc/NEWS.d/next/Library/2025-08-26-08-17-56.gh-issue-138151.I6CdAk.rst new file mode 100644 index 00000000000..de29f536afc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-08-26-08-17-56.gh-issue-138151.I6CdAk.rst @@ -0,0 +1,3 @@ +In :mod:`annotationlib`, improve evaluation of forward references to +nonlocal variables that are not yet defined when the annotations are +initially evaluated. From 478b8dab0b40f08c3ded40bf988ec49a50f4c8fb Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Mon, 3 Nov 2025 15:47:52 +0000 Subject: [PATCH 371/373] Docs: Fix typo in codecs documentation (GH-140883) --- Doc/library/codecs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst index 2a5994b11d8..305e5d07a35 100644 --- a/Doc/library/codecs.rst +++ b/Doc/library/codecs.rst @@ -1088,7 +1088,7 @@ alias for the ``'utf_8'`` codec. refer to the source :source:`aliases.py <Lib/encodings/aliases.py>` file. On Windows, ``cpXXX`` codecs are available for all code pages. -But only codecs listed in the following table are guarantead to exist on +But only codecs listed in the following table are guaranteed to exist on other platforms. .. impl-detail:: From b373d3494c587cf27b31f3dff89a8d96f7d29b9d Mon Sep 17 00:00:00 2001 From: Yongzi Li <204532581+Yzi-Li@users.noreply.github.com> Date: Mon, 3 Nov 2025 23:48:10 +0800 Subject: [PATCH 372/373] Docs: Fix a typo in `idle.rst` (Chitespace -> Whitespace) (GH-140946) Fix typo in idle.rst --- Doc/library/idle.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index fabea611e0e..e547c96b580 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -204,7 +204,7 @@ New Indent Width Open a dialog to change indent width. The accepted default by the Python community is 4 spaces. -Strip Trailing Chitespace +Strip Trailing Whitespace Remove trailing space and other whitespace characters after the last non-whitespace character of a line by applying str.rstrip to each line, including lines within multiline strings. Except for Shell windows, From 4e2ff4ac4cc725572e1458a91418deeadd03d8aa Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski <savannah@python.org> Date: Mon, 3 Nov 2025 10:01:44 -0800 Subject: [PATCH 373/373] GH-136895: Update JIT builds to use LLVM 20 (#140329) Co-authored-by: Emma Harper Smith <emma@emmatyping.dev> --- .github/workflows/jit.yml | 6 +- ...-10-19-10-32-28.gh-issue-136895.HfsEh0.rst | 1 + PCbuild/get_external.py | 63 ++++++++++++---- PCbuild/get_externals.bat | 8 ++- Python/jit.c | 72 +++++++++++++++---- Tools/jit/README.md | 20 +++--- Tools/jit/_llvm.py | 4 +- Tools/jit/_stencils.py | 17 +++++ Tools/jit/_targets.py | 10 +-- 9 files changed, 151 insertions(+), 50 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-19-10-32-28.gh-issue-136895.HfsEh0.rst diff --git a/.github/workflows/jit.yml b/.github/workflows/jit.yml index c32bf4fd63c..151b17e8442 100644 --- a/.github/workflows/jit.yml +++ b/.github/workflows/jit.yml @@ -68,7 +68,7 @@ jobs: - true - false llvm: - - 19 + - 20 include: - target: i686-pc-windows-msvc/msvc architecture: Win32 @@ -138,7 +138,7 @@ jobs: fail-fast: false matrix: llvm: - - 19 + - 20 steps: - uses: actions/checkout@v4 with: @@ -166,7 +166,7 @@ jobs: fail-fast: false matrix: llvm: - - 19 + - 20 steps: - uses: actions/checkout@v4 with: diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-19-10-32-28.gh-issue-136895.HfsEh0.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-19-10-32-28.gh-issue-136895.HfsEh0.rst new file mode 100644 index 00000000000..fffc264a865 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-19-10-32-28.gh-issue-136895.HfsEh0.rst @@ -0,0 +1 @@ +Update JIT compilation to use LLVM 20 at build time. diff --git a/PCbuild/get_external.py b/PCbuild/get_external.py index a78aa6a2304..07970624e86 100755 --- a/PCbuild/get_external.py +++ b/PCbuild/get_external.py @@ -3,6 +3,7 @@ import argparse import os import pathlib +import shutil import sys import time import urllib.error @@ -22,15 +23,13 @@ def retrieve_with_retries(download_location, output_path, reporthook, ) except (urllib.error.URLError, ConnectionError) as ex: if attempt == max_retries: - msg = f"Download from {download_location} failed." - raise OSError(msg) from ex + raise OSError(f'Download from {download_location} failed.') from ex time.sleep(2.25**attempt) else: return resp - def fetch_zip(commit_hash, zip_dir, *, org='python', binary=False, verbose): - repo = f'cpython-{"bin" if binary else "source"}-deps' + repo = 'cpython-bin-deps' if binary else 'cpython-source-deps' url = f'https://github.com/{org}/{repo}/archive/{commit_hash}.zip' reporthook = None if verbose: @@ -44,6 +43,23 @@ def fetch_zip(commit_hash, zip_dir, *, org='python', binary=False, verbose): return filename +def fetch_release(tag, tarball_dir, *, org='python', verbose=False): + url = f'https://github.com/{org}/cpython-bin-deps/releases/download/{tag}/{tag}.tar.xz' + reporthook = None + if verbose: + reporthook = print + tarball_dir.mkdir(parents=True, exist_ok=True) + output_path = tarball_dir / f'{tag}.tar.xz' + retrieve_with_retries(url, output_path, reporthook) + return output_path + + +def extract_tarball(externals_dir, tarball_path, tag): + output_path = externals_dir / tag + shutil.unpack_archive(os.fspath(tarball_path), os.fspath(output_path)) + return output_path + + def extract_zip(externals_dir, zip_path): with zipfile.ZipFile(os.fspath(zip_path)) as zf: zf.extractall(os.fspath(externals_dir)) @@ -55,6 +71,8 @@ def parse_args(): p.add_argument('-v', '--verbose', action='store_true') p.add_argument('-b', '--binary', action='store_true', help='Is the dependency in the binary repo?') + p.add_argument('-r', '--release', action='store_true', + help='Download from GitHub release assets instead of branch') p.add_argument('-O', '--organization', help='Organization owning the deps repos', default='python') p.add_argument('-e', '--externals-dir', type=pathlib.Path, @@ -67,15 +85,36 @@ def parse_args(): def main(): args = parse_args() - zip_path = fetch_zip( - args.tag, - args.externals_dir / 'zips', - org=args.organization, - binary=args.binary, - verbose=args.verbose, - ) final_name = args.externals_dir / args.tag - extracted = extract_zip(args.externals_dir, zip_path) + + # Check if the dependency already exists in externals/ directory + # (either already downloaded/extracted, or checked into the git tree) + if final_name.exists(): + if args.verbose: + print(f'{args.tag} already exists at {final_name}, skipping download.') + return + + # Determine download method: release artifacts for large deps (like LLVM), + # otherwise zip download from GitHub branches + if args.release: + tarball_path = fetch_release( + args.tag, + args.externals_dir / 'tarballs', + org=args.organization, + verbose=args.verbose, + ) + extracted = extract_tarball(args.externals_dir, tarball_path, args.tag) + else: + # Use zip download from GitHub branches + # (cpython-bin-deps if --binary, cpython-source-deps otherwise) + zip_path = fetch_zip( + args.tag, + args.externals_dir / 'zips', + org=args.organization, + binary=args.binary, + verbose=args.verbose, + ) + extracted = extract_zip(args.externals_dir, zip_path) for wait in [1, 2, 3, 5, 8, 0]: try: extracted.replace(final_name) diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 50a227b563a..319024e0f50 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -82,7 +82,7 @@ if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.4 if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-3.0.18 if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.15.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 -if NOT "%IncludeLLVM%"=="false" set binaries=%binaries% llvm-19.1.7.0 +if NOT "%IncludeLLVM%"=="false" set binaries=%binaries% llvm-20.1.8.0 for %%b in (%binaries%) do ( if exist "%EXTERNALS_DIR%\%%b" ( @@ -92,7 +92,11 @@ for %%b in (%binaries%) do ( git clone --depth 1 https://github.com/%ORG%/cpython-bin-deps --branch %%b "%EXTERNALS_DIR%\%%b" ) else ( echo.Fetching %%b... - %PYTHON% -E "%PCBUILD%\get_external.py" -b -O %ORG% -e "%EXTERNALS_DIR%" %%b + if "%%b"=="llvm-20.1.8.0" ( + %PYTHON% -E "%PCBUILD%\get_external.py" --release --organization %ORG% --externals-dir "%EXTERNALS_DIR%" %%b + ) else ( + %PYTHON% -E "%PCBUILD%\get_external.py" --binary --organization %ORG% --externals-dir "%EXTERNALS_DIR%" %%b + ) ) ) diff --git a/Python/jit.c b/Python/jit.c index c3f3d686013..279e1ce6a0d 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -444,17 +444,42 @@ patch_x86_64_32rx(unsigned char *location, uint64_t value) } void patch_aarch64_trampoline(unsigned char *location, int ordinal, jit_state *state); +void patch_x86_64_trampoline(unsigned char *location, int ordinal, jit_state *state); #include "jit_stencils.h" #if defined(__aarch64__) || defined(_M_ARM64) #define TRAMPOLINE_SIZE 16 #define DATA_ALIGN 8 +#elif defined(__x86_64__) && defined(__APPLE__) + // LLVM 20 on macOS x86_64 debug builds: GOT entries may exceed ±2GB PC-relative + // range. + #define TRAMPOLINE_SIZE 16 // 14 bytes + 2 bytes padding for alignment + #define DATA_ALIGN 8 #else #define TRAMPOLINE_SIZE 0 #define DATA_ALIGN 1 #endif +// Get the trampoline memory location for a given symbol ordinal. +static unsigned char * +get_trampoline_slot(int ordinal, jit_state *state) +{ + const uint32_t symbol_mask = 1 << (ordinal % 32); + const uint32_t trampoline_mask = state->trampolines.mask[ordinal / 32]; + assert(symbol_mask & trampoline_mask); + + // Count the number of set bits in the trampoline mask lower than ordinal + int index = _Py_popcount32(trampoline_mask & (symbol_mask - 1)); + for (int i = 0; i < ordinal / 32; i++) { + index += _Py_popcount32(state->trampolines.mask[i]); + } + + unsigned char *trampoline = state->trampolines.mem + index * TRAMPOLINE_SIZE; + assert((size_t)(index + 1) * TRAMPOLINE_SIZE <= state->trampolines.size); + return trampoline; +} + // Generate and patch AArch64 trampolines. The symbols to jump to are stored // in the jit_stencils.h in the symbols_map. void @@ -471,20 +496,8 @@ patch_aarch64_trampoline(unsigned char *location, int ordinal, jit_state *state) return; } - // Masking is done modulo 32 as the mask is stored as an array of uint32_t - const uint32_t symbol_mask = 1 << (ordinal % 32); - const uint32_t trampoline_mask = state->trampolines.mask[ordinal / 32]; - assert(symbol_mask & trampoline_mask); - - // Count the number of set bits in the trampoline mask lower than ordinal, - // this gives the index into the array of trampolines. - int index = _Py_popcount32(trampoline_mask & (symbol_mask - 1)); - for (int i = 0; i < ordinal / 32; i++) { - index += _Py_popcount32(state->trampolines.mask[i]); - } - - uint32_t *p = (uint32_t*)(state->trampolines.mem + index * TRAMPOLINE_SIZE); - assert((size_t)(index + 1) * TRAMPOLINE_SIZE <= state->trampolines.size); + // Out of range - need a trampoline + uint32_t *p = (uint32_t *)get_trampoline_slot(ordinal, state); /* Generate the trampoline @@ -501,6 +514,37 @@ patch_aarch64_trampoline(unsigned char *location, int ordinal, jit_state *state) patch_aarch64_26r(location, (uintptr_t)p); } +// Generate and patch x86_64 trampolines. +void +patch_x86_64_trampoline(unsigned char *location, int ordinal, jit_state *state) +{ + uint64_t value = (uintptr_t)symbols_map[ordinal]; + int64_t range = (int64_t)value - 4 - (int64_t)location; + + // If we are in range of 32 signed bits, we can patch directly + if (range >= -(1LL << 31) && range < (1LL << 31)) { + patch_32r(location, value - 4); + return; + } + + // Out of range - need a trampoline + unsigned char *trampoline = get_trampoline_slot(ordinal, state); + + /* Generate the trampoline (14 bytes, padded to 16): + 0: ff 25 00 00 00 00 jmp *(%rip) + 6: XX XX XX XX XX XX XX XX (64-bit target address) + + Reference: https://wiki.osdev.org/X86-64_Instruction_Encoding#FF (JMP r/m64) + */ + trampoline[0] = 0xFF; + trampoline[1] = 0x25; + memset(trampoline + 2, 0, 4); + memcpy(trampoline + 6, &value, 8); + + // Patch the call site to call the trampoline instead + patch_32r(location, (uintptr_t)trampoline - 4); +} + static void combine_symbol_mask(const symbol_mask src, symbol_mask dest) { diff --git a/Tools/jit/README.md b/Tools/jit/README.md index 35c7ffd7a28..d83b09aab59 100644 --- a/Tools/jit/README.md +++ b/Tools/jit/README.md @@ -9,32 +9,32 @@ ## Installing LLVM The JIT compiler does not require end users to install any third-party dependencies, but part of it must be *built* using LLVM[^why-llvm]. You are *not* required to build the rest of CPython using LLVM, or even the same version of LLVM (in fact, this is uncommon). -LLVM version 19 is the officially supported version. You can modify if needed using the `LLVM_VERSION` env var during configure. Both `clang` and `llvm-readobj` need to be installed and discoverable (version suffixes, like `clang-19`, are okay). It's highly recommended that you also have `llvm-objdump` available, since this allows the build script to dump human-readable assembly for the generated code. +LLVM version 20 is the officially supported version. You can modify if needed using the `LLVM_VERSION` env var during configure. Both `clang` and `llvm-readobj` need to be installed and discoverable (version suffixes, like `clang-19`, are okay). It's highly recommended that you also have `llvm-objdump` available, since this allows the build script to dump human-readable assembly for the generated code. It's easy to install all of the required tools: ### Linux -Install LLVM 19 on Ubuntu/Debian: +Install LLVM 20 on Ubuntu/Debian: ```sh wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh -sudo ./llvm.sh 19 +sudo ./llvm.sh 20 ``` -Install LLVM 19 on Fedora Linux 40 or newer: +Install LLVM 20 on Fedora Linux 40 or newer: ```sh -sudo dnf install 'clang(major) = 19' 'llvm(major) = 19' +sudo dnf install 'clang(major) = 20' 'llvm(major) = 20' ``` ### macOS -Install LLVM 19 with [Homebrew](https://brew.sh): +Install LLVM 20 with [Homebrew](https://brew.sh): ```sh -brew install llvm@19 +brew install llvm@20 ``` Homebrew won't add any of the tools to your `$PATH`. That's okay; the build script knows how to find them. @@ -43,18 +43,18 @@ ### Windows LLVM is downloaded automatically (along with other external binary dependencies) by `PCbuild\build.bat`. -Otherwise, you can install LLVM 19 [by searching for it on LLVM's GitHub releases page](https://github.com/llvm/llvm-project/releases?q=19), clicking on "Assets", downloading the appropriate Windows installer for your platform (likely the file ending with `-win64.exe`), and running it. **When installing, be sure to select the option labeled "Add LLVM to the system PATH".** +Otherwise, you can install LLVM 20 [by searching for it on LLVM's GitHub releases page](https://github.com/llvm/llvm-project/releases?q=20), clicking on "Assets", downloading the appropriate Windows installer for your platform (likely the file ending with `-win64.exe`), and running it. **When installing, be sure to select the option labeled "Add LLVM to the system PATH".** Alternatively, you can use [chocolatey](https://chocolatey.org): ```sh -choco install llvm --version=19.1.0 +choco install llvm --version=20.1.8 ``` ### Dev Containers If you are working on CPython in a [Codespaces instance](https://devguide.python.org/getting-started/setup-building/#using-codespaces), there's no -need to install LLVM as the Fedora 41 base image includes LLVM 19 out of the box. +need to install LLVM as the Fedora 42 base image includes LLVM 20 out of the box. ## Building diff --git a/Tools/jit/_llvm.py b/Tools/jit/_llvm.py index bc3b50ffe61..54c2bf86a36 100644 --- a/Tools/jit/_llvm.py +++ b/Tools/jit/_llvm.py @@ -11,8 +11,8 @@ import _targets -_LLVM_VERSION = "19" -_EXTERNALS_LLVM_TAG = "llvm-19.1.7.0" +_LLVM_VERSION = "20" +_EXTERNALS_LLVM_TAG = "llvm-20.1.8.0" _P = typing.ParamSpec("_P") _R = typing.TypeVar("_R") diff --git a/Tools/jit/_stencils.py b/Tools/jit/_stencils.py index 777db7366b1..e717365b6b9 100644 --- a/Tools/jit/_stencils.py +++ b/Tools/jit/_stencils.py @@ -253,6 +253,23 @@ def process_relocations(self, known_symbols: dict[str, int]) -> None: self._trampolines.add(ordinal) hole.addend = ordinal hole.symbol = None + # x86_64 Darwin trampolines for external symbols + elif ( + hole.kind == "X86_64_RELOC_BRANCH" + and hole.value is HoleValue.ZERO + and hole.symbol not in self.symbols + ): + hole.func = "patch_x86_64_trampoline" + hole.need_state = True + assert hole.symbol is not None + if hole.symbol in known_symbols: + ordinal = known_symbols[hole.symbol] + else: + ordinal = len(known_symbols) + known_symbols[hole.symbol] = ordinal + self._trampolines.add(ordinal) + hole.addend = ordinal + hole.symbol = None self.data.pad(8) for stencil in [self.code, self.data]: for hole in stencil.holes: diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index dcc0abaf23f..a76d8ff2792 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -166,10 +166,6 @@ async def _compile( "-fno-asynchronous-unwind-tables", # Don't call built-in functions that we can't find or patch: "-fno-builtin", - # Emit relaxable 64-bit calls/jumps, so we don't have to worry about - # about emitting in-range trampolines for out-of-range targets. - # We can probably remove this and emit trampolines in the future: - "-fno-plt", # Don't call stack-smashing canaries that we can't find or patch: "-fno-stack-protector", "-std=c11", @@ -571,14 +567,14 @@ def get_target(host: str) -> _COFF32 | _COFF64 | _ELF | _MachO: elif re.fullmatch(r"aarch64-pc-windows-msvc", host): host = "aarch64-pc-windows-msvc" condition = "defined(_M_ARM64)" - args = ["-fms-runtime-lib=dll", "-fplt"] + args = ["-fms-runtime-lib=dll"] optimizer = _optimizers.OptimizerAArch64 target = _COFF64(host, condition, args=args, optimizer=optimizer) elif re.fullmatch(r"aarch64-.*-linux-gnu", host): host = "aarch64-unknown-linux-gnu" condition = "defined(__aarch64__) && defined(__linux__)" # -mno-outline-atomics: Keep intrinsics from being emitted. - args = ["-fpic", "-mno-outline-atomics"] + args = ["-fpic", "-mno-outline-atomics", "-fno-plt"] optimizer = _optimizers.OptimizerAArch64 target = _ELF(host, condition, args=args, optimizer=optimizer) elif re.fullmatch(r"i686-pc-windows-msvc", host): @@ -602,7 +598,7 @@ def get_target(host: str) -> _COFF32 | _COFF64 | _ELF | _MachO: elif re.fullmatch(r"x86_64-.*-linux-gnu", host): host = "x86_64-unknown-linux-gnu" condition = "defined(__x86_64__) && defined(__linux__)" - args = ["-fno-pic", "-mcmodel=medium", "-mlarge-data-threshold=0"] + args = ["-fno-pic", "-mcmodel=medium", "-mlarge-data-threshold=0", "-fno-plt"] optimizer = _optimizers.OptimizerX86 target = _ELF(host, condition, args=args, optimizer=optimizer) else: