From e52cc80f7fc3a560bf3d0053e0821a2db070cdd1 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 18 Nov 2023 23:05:49 +0000 Subject: [PATCH 001/228] gh-112213: Add @critical_section target directive to Argument Clinic (gh-112232) Co-authored-by: Alex Waygood --- Lib/test/clinic.test.c | 170 +++++++++++++++++++++++++++++++++++++++++ Tools/clinic/clinic.py | 25 +++++- 2 files changed, 191 insertions(+), 4 deletions(-) diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index da730c5b2495c8..81f88c4d1535ce 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -5542,3 +5542,173 @@ test_critical_section_meth_o(PyObject *module, PyObject *arg) static PyObject * test_critical_section_meth_o_impl(PyObject *module, PyObject *a) /*[clinic end generated code: output=7a9d7420802d1202 input=376533f51eceb6c3]*/ + +/*[clinic input] +@critical_section a +test_critical_section_object + a: object(subclass_of="&PyUnicode_Type") + / +test_critical_section_object +[clinic start generated code]*/ + +PyDoc_STRVAR(test_critical_section_object__doc__, +"test_critical_section_object($module, a, /)\n" +"--\n" +"\n" +"test_critical_section_object"); + +#define TEST_CRITICAL_SECTION_OBJECT_METHODDEF \ + {"test_critical_section_object", (PyCFunction)test_critical_section_object, METH_O, test_critical_section_object__doc__}, + +static PyObject * +test_critical_section_object_impl(PyObject *module, PyObject *a); + +static PyObject * +test_critical_section_object(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *a; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("test_critical_section_object", "argument", "str", arg); + goto exit; + } + a = arg; + Py_BEGIN_CRITICAL_SECTION(a); + return_value = test_critical_section_object_impl(module, a); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +static PyObject * +test_critical_section_object_impl(PyObject *module, PyObject *a) +/*[clinic end generated code: output=ec06df92232b0fb5 input=6f67f91b523c875f]*/ + +PyDoc_STRVAR(test_critical_section_object__doc__, +"test_critical_section_object($module, a, /)\n" +"--\n" +"\n" +"test_critical_section_object"); + +#define TEST_CRITICAL_SECTION_OBJECT_METHODDEF \ + {"test_critical_section_object", (PyCFunction)test_critical_section_object, METH_O, test_critical_section_object__doc__}, + +static PyObject * +test_critical_section_object_impl(PyObject *module, PyObject *a); + +static PyObject * +test_critical_section_object(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *a; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("test_critical_section_object", "argument", "str", arg); + goto exit; + } + a = arg; + Py_BEGIN_CRITICAL_SECTION(a); + return_value = test_critical_section_object_impl(module, a); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +/*[clinic input] +@critical_section a b +test_critical_section_object2 + a: object(subclass_of="&PyUnicode_Type") + b: object(subclass_of="&PyUnicode_Type") + / +test_critical_section_object2 +[clinic start generated code]*/ + +PyDoc_STRVAR(test_critical_section_object2__doc__, +"test_critical_section_object2($module, a, b, /)\n" +"--\n" +"\n" +"test_critical_section_object2"); + +#define TEST_CRITICAL_SECTION_OBJECT2_METHODDEF \ + {"test_critical_section_object2", _PyCFunction_CAST(test_critical_section_object2), METH_FASTCALL, test_critical_section_object2__doc__}, + +static PyObject * +test_critical_section_object2_impl(PyObject *module, PyObject *a, + PyObject *b); + +static PyObject * +test_critical_section_object2(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b; + + if (!_PyArg_CheckPositional("test_critical_section_object2", nargs, 2, 2)) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("test_critical_section_object2", "argument 1", "str", args[0]); + goto exit; + } + a = args[0]; + if (!PyUnicode_Check(args[1])) { + _PyArg_BadArgument("test_critical_section_object2", "argument 2", "str", args[1]); + goto exit; + } + b = args[1]; + Py_BEGIN_CRITICAL_SECTION2(a, b); + return_value = test_critical_section_object2_impl(module, a, b); + Py_END_CRITICAL_SECTION2(); + +exit: + return return_value; +} + +static PyObject * +test_critical_section_object2_impl(PyObject *module, PyObject *a, + PyObject *b) +/*[clinic end generated code: output=d73a1657c18df17a input=638824e41419a466]*/ + +PyDoc_STRVAR(test_critical_section_object2__doc__, +"test_critical_section_object2($module, a, b, /)\n" +"--\n" +"\n" +"test_critical_section_object2"); + +#define TEST_CRITICAL_SECTION_OBJECT2_METHODDEF \ + {"test_critical_section_object2", _PyCFunction_CAST(test_critical_section_object2), METH_FASTCALL, test_critical_section_object2__doc__}, + +static PyObject * +test_critical_section_object2_impl(PyObject *module, PyObject *a, + PyObject *b); + +static PyObject * +test_critical_section_object2(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b; + + if (!_PyArg_CheckPositional("test_critical_section_object2", nargs, 2, 2)) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("test_critical_section_object2", "argument 1", "str", args[0]); + goto exit; + } + a = args[0]; + if (!PyUnicode_Check(args[1])) { + _PyArg_BadArgument("test_critical_section_object2", "argument 2", "str", args[1]); + goto exit; + } + b = args[1]; + Py_BEGIN_CRITICAL_SECTION2(a, b); + return_value = test_critical_section_object2_impl(module, a, b); + Py_END_CRITICAL_SECTION2(); + +exit: + return return_value; +} diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index a205a5182ee99f..f9326c1b2ca635 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -1866,8 +1866,18 @@ def render_function( assert isinstance(f_self.converter, self_converter), "No self parameter in " + repr(f.full_name) + "!" if f.critical_section: - data.lock.append('Py_BEGIN_CRITICAL_SECTION({self_name});') - data.unlock.append('Py_END_CRITICAL_SECTION();') + match len(f.target_critical_section): + case 0: + lock = 'Py_BEGIN_CRITICAL_SECTION({self_name});' + unlock = 'Py_END_CRITICAL_SECTION();' + case 1: + lock = 'Py_BEGIN_CRITICAL_SECTION({target_critical_section});' + unlock = 'Py_END_CRITICAL_SECTION();' + case _: + lock = 'Py_BEGIN_CRITICAL_SECTION2({target_critical_section});' + unlock = 'Py_END_CRITICAL_SECTION2();' + data.lock.append(lock) + data.unlock.append(unlock) last_group = 0 first_optional = len(selfless) @@ -1922,6 +1932,7 @@ def render_function( template_dict['docstring'] = self.docstring_for_c_string(f) template_dict['self_name'] = template_dict['self_type'] = template_dict['self_type_check'] = '' + template_dict['target_critical_section'] = ', '.join(f.target_critical_section) for converter in converters: converter.set_template_dict(template_dict) @@ -2970,6 +2981,7 @@ class Function: # those accurately with inspect.Signature in 3.4. docstring_only: bool = False critical_section: bool = False + target_critical_section: list[str] = dc.field(default_factory=list) def __post_init__(self) -> None: self.parent = self.cls or self.module @@ -5160,6 +5172,7 @@ def reset(self) -> None: self.parameter_continuation = '' self.preserve_output = False self.critical_section = False + self.target_critical_section: list[str] = [] def directive_version(self, required: str) -> None: global version @@ -5288,7 +5301,10 @@ def at_classmethod(self) -> None: fail("Can't set @classmethod, function is not a normal callable") self.kind = CLASS_METHOD - def at_critical_section(self) -> None: + def at_critical_section(self, *args: str) -> None: + if len(args) > 2: + fail("Up to 2 critical section variables are supported") + self.target_critical_section.extend(args) self.critical_section = True def at_staticmethod(self) -> None: @@ -5514,7 +5530,8 @@ def state_modulename_name(self, line: str) -> None: self.function = Function(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename, return_converter=return_converter, kind=self.kind, coexist=self.coexist, - critical_section=self.critical_section) + critical_section=self.critical_section, + target_critical_section=self.target_critical_section) self.block.signatures.append(self.function) # insert a self converter automatically From 1a969b4f55f92a17bec82ce0366021a53afdb2c3 Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 19 Nov 2023 07:21:04 +0800 Subject: [PATCH 002/228] gh-111965: Use critical sections to make io.TextIOWrapper thread safe (gh-112193) --- Modules/_io/clinic/textio.c.h | 86 +++++++++++++++++++--- Modules/_io/textio.c | 134 ++++++++++++++++++++++++++-------- 2 files changed, 181 insertions(+), 39 deletions(-) diff --git a/Modules/_io/clinic/textio.c.h b/Modules/_io/clinic/textio.c.h index b24a1669f9b344..76bf484f657656 100644 --- a/Modules/_io/clinic/textio.c.h +++ b/Modules/_io/clinic/textio.c.h @@ -630,7 +630,9 @@ _io_TextIOWrapper_reconfigure(textio *self, PyObject *const *args, Py_ssize_t na } write_through_obj = args[4]; skip_optional_kwonly: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_TextIOWrapper_reconfigure_impl(self, encoding, errors, newline_obj, line_buffering_obj, write_through_obj); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -650,7 +652,13 @@ _io_TextIOWrapper_detach_impl(textio *self); static PyObject * _io_TextIOWrapper_detach(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_detach_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_detach_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_TextIOWrapper_write__doc__, @@ -675,7 +683,9 @@ _io_TextIOWrapper_write(textio *self, PyObject *arg) goto exit; } text = arg; + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_TextIOWrapper_write_impl(self, text); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -708,7 +718,9 @@ _io_TextIOWrapper_read(textio *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_TextIOWrapper_read_impl(self, n); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -750,7 +762,9 @@ _io_TextIOWrapper_readline(textio *self, PyObject *const *args, Py_ssize_t nargs size = ival; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_TextIOWrapper_readline_impl(self, size); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -804,7 +818,9 @@ _io_TextIOWrapper_seek(textio *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_TextIOWrapper_seek_impl(self, cookieObj, whence); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -828,7 +844,13 @@ _io_TextIOWrapper_tell_impl(textio *self); static PyObject * _io_TextIOWrapper_tell(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_tell_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_tell_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_TextIOWrapper_truncate__doc__, @@ -856,7 +878,9 @@ _io_TextIOWrapper_truncate(textio *self, PyObject *const *args, Py_ssize_t nargs } pos = args[0]; skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_TextIOWrapper_truncate_impl(self, pos); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -876,7 +900,13 @@ _io_TextIOWrapper_fileno_impl(textio *self); static PyObject * _io_TextIOWrapper_fileno(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_fileno_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_fileno_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_TextIOWrapper_seekable__doc__, @@ -893,7 +923,13 @@ _io_TextIOWrapper_seekable_impl(textio *self); static PyObject * _io_TextIOWrapper_seekable(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_seekable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_seekable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_TextIOWrapper_readable__doc__, @@ -910,7 +946,13 @@ _io_TextIOWrapper_readable_impl(textio *self); static PyObject * _io_TextIOWrapper_readable(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_readable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_readable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_TextIOWrapper_writable__doc__, @@ -927,7 +969,13 @@ _io_TextIOWrapper_writable_impl(textio *self); static PyObject * _io_TextIOWrapper_writable(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_writable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_writable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_TextIOWrapper_isatty__doc__, @@ -944,7 +992,13 @@ _io_TextIOWrapper_isatty_impl(textio *self); static PyObject * _io_TextIOWrapper_isatty(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_isatty_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_isatty_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_TextIOWrapper_flush__doc__, @@ -961,7 +1015,13 @@ _io_TextIOWrapper_flush_impl(textio *self); static PyObject * _io_TextIOWrapper_flush(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_flush_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_flush_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_TextIOWrapper_close__doc__, @@ -978,6 +1038,12 @@ _io_TextIOWrapper_close_impl(textio *self); static PyObject * _io_TextIOWrapper_close(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_close_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_close_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } -/*[clinic end generated code: output=e58ce89b7354e77a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ec8ccae78ec3b379 input=a9049054013a1b77]*/ diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 8d19198502fe12..9f4155a5dd63ee 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -7,14 +7,15 @@ */ #include "Python.h" -#include "pycore_call.h" // _PyObject_CallMethod() -#include "pycore_codecs.h" // _PyCodecInfo_GetIncrementalDecoder() -#include "pycore_fileutils.h" // _Py_GetLocaleEncoding() -#include "pycore_interp.h" // PyInterpreterState.fs_codec -#include "pycore_long.h" // _PyLong_GetZero() -#include "pycore_object.h" // _PyObject_GC_UNTRACK() -#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() -#include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_call.h" // _PyObject_CallMethod() +#include "pycore_codecs.h" // _PyCodecInfo_GetIncrementalDecoder() +#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION() +#include "pycore_fileutils.h" // _Py_GetLocaleEncoding() +#include "pycore_interp.h" // PyInterpreterState.fs_codec +#include "pycore_long.h" // _PyLong_GetZero() +#include "pycore_object.h" // _PyObject_GC_UNTRACK() +#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() +#include "pycore_pystate.h" // _PyInterpreterState_GET() #include "_iomodule.h" @@ -1327,6 +1328,7 @@ textiowrapper_change_encoding(textio *self, PyObject *encoding, } /*[clinic input] +@critical_section _io.TextIOWrapper.reconfigure * encoding: object = None @@ -1346,7 +1348,7 @@ _io_TextIOWrapper_reconfigure_impl(textio *self, PyObject *encoding, PyObject *errors, PyObject *newline_obj, PyObject *line_buffering_obj, PyObject *write_through_obj) -/*[clinic end generated code: output=52b812ff4b3d4b0f input=671e82136e0f5822]*/ +/*[clinic end generated code: output=52b812ff4b3d4b0f input=dc3bd35ebda702a7]*/ { int line_buffering; int write_through; @@ -1531,12 +1533,13 @@ textiowrapper_closed_get(textio *self, void *context); /*[clinic input] +@critical_section _io.TextIOWrapper.detach [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_detach_impl(textio *self) -/*[clinic end generated code: output=7ba3715cd032d5f2 input=e5a71fbda9e1d9f9]*/ +/*[clinic end generated code: output=7ba3715cd032d5f2 input=c908a3b4ef203b0f]*/ { PyObject *buffer; CHECK_ATTACHED(self); @@ -1622,6 +1625,7 @@ _textiowrapper_writeflush(textio *self) } /*[clinic input] +@critical_section _io.TextIOWrapper.write text: unicode / @@ -1629,7 +1633,7 @@ _io.TextIOWrapper.write static PyObject * _io_TextIOWrapper_write_impl(textio *self, PyObject *text) -/*[clinic end generated code: output=d2deb0d50771fcec input=fdf19153584a0e44]*/ +/*[clinic end generated code: output=d2deb0d50771fcec input=73ec95c5c4a3489c]*/ { PyObject *ret; PyObject *b; @@ -1933,6 +1937,7 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint) } /*[clinic input] +@critical_section _io.TextIOWrapper.read size as n: Py_ssize_t(accept={int, NoneType}) = -1 / @@ -1940,7 +1945,7 @@ _io.TextIOWrapper.read static PyObject * _io_TextIOWrapper_read_impl(textio *self, Py_ssize_t n) -/*[clinic end generated code: output=7e651ce6cc6a25a6 input=123eecbfe214aeb8]*/ +/*[clinic end generated code: output=7e651ce6cc6a25a6 input=67d14c5661121377]*/ { PyObject *result = NULL, *chunks = NULL; @@ -2308,6 +2313,7 @@ _textiowrapper_readline(textio *self, Py_ssize_t limit) } /*[clinic input] +@critical_section _io.TextIOWrapper.readline size: Py_ssize_t = -1 / @@ -2315,7 +2321,7 @@ _io.TextIOWrapper.readline static PyObject * _io_TextIOWrapper_readline_impl(textio *self, Py_ssize_t size) -/*[clinic end generated code: output=344afa98804e8b25 input=56c7172483b36db6]*/ +/*[clinic end generated code: output=344afa98804e8b25 input=b65bab871dc3ddba]*/ { CHECK_ATTACHED(self); return _textiowrapper_readline(self, size); @@ -2454,6 +2460,7 @@ _textiowrapper_encoder_setstate(textio *self, cookie_type *cookie) } /*[clinic input] +@critical_section _io.TextIOWrapper.seek cookie as cookieObj: object Zero or an opaque number returned by tell(). @@ -2478,7 +2485,7 @@ and may raise exceptions. static PyObject * _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) -/*[clinic end generated code: output=0a15679764e2d04d input=0f68adcb02cf2823]*/ +/*[clinic end generated code: output=0a15679764e2d04d input=4bea78698be23d7e]*/ { PyObject *posobj; cookie_type cookie; @@ -2665,6 +2672,7 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) } /*[clinic input] +@critical_section _io.TextIOWrapper.tell Return the stream position as an opaque number. @@ -2675,7 +2683,7 @@ previous stream position. static PyObject * _io_TextIOWrapper_tell_impl(textio *self) -/*[clinic end generated code: output=4f168c08bf34ad5f input=0852d627d76fb520]*/ +/*[clinic end generated code: output=4f168c08bf34ad5f input=415d6b4e4f8e6e8c]*/ { PyObject *res; PyObject *posobj = NULL; @@ -2901,6 +2909,7 @@ _io_TextIOWrapper_tell_impl(textio *self) } /*[clinic input] +@critical_section _io.TextIOWrapper.truncate pos: object = None / @@ -2908,7 +2917,7 @@ _io.TextIOWrapper.truncate static PyObject * _io_TextIOWrapper_truncate_impl(textio *self, PyObject *pos) -/*[clinic end generated code: output=90ec2afb9bb7745f input=56ec8baa65aea377]*/ +/*[clinic end generated code: output=90ec2afb9bb7745f input=8bddb320834c93ee]*/ { CHECK_ATTACHED(self) @@ -2988,72 +2997,78 @@ textiowrapper_repr(textio *self) /* Inquiries */ /*[clinic input] +@critical_section _io.TextIOWrapper.fileno [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_fileno_impl(textio *self) -/*[clinic end generated code: output=21490a4c3da13e6c input=c488ca83d0069f9b]*/ +/*[clinic end generated code: output=21490a4c3da13e6c input=515e1196aceb97ab]*/ { CHECK_ATTACHED(self); return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(fileno)); } /*[clinic input] +@critical_section _io.TextIOWrapper.seekable [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_seekable_impl(textio *self) -/*[clinic end generated code: output=ab223dbbcffc0f00 input=8b005ca06e1fca13]*/ +/*[clinic end generated code: output=ab223dbbcffc0f00 input=71c4c092736c549b]*/ { CHECK_ATTACHED(self); return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(seekable)); } /*[clinic input] +@critical_section _io.TextIOWrapper.readable [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_readable_impl(textio *self) -/*[clinic end generated code: output=72ff7ba289a8a91b input=0704ea7e01b0d3eb]*/ +/*[clinic end generated code: output=72ff7ba289a8a91b input=80438d1f01b0a89b]*/ { CHECK_ATTACHED(self); return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(readable)); } /*[clinic input] +@critical_section _io.TextIOWrapper.writable [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_writable_impl(textio *self) -/*[clinic end generated code: output=a728c71790d03200 input=c41740bc9d8636e8]*/ +/*[clinic end generated code: output=a728c71790d03200 input=9d6c22befb0c340a]*/ { CHECK_ATTACHED(self); return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(writable)); } /*[clinic input] +@critical_section _io.TextIOWrapper.isatty [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_isatty_impl(textio *self) -/*[clinic end generated code: output=12be1a35bace882e input=fb68d9f2c99bbfff]*/ +/*[clinic end generated code: output=12be1a35bace882e input=7f83ff04d4d1733d]*/ { CHECK_ATTACHED(self); return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(isatty)); } /*[clinic input] +@critical_section _io.TextIOWrapper.flush [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_flush_impl(textio *self) -/*[clinic end generated code: output=59de9165f9c2e4d2 input=928c60590694ab85]*/ +/*[clinic end generated code: output=59de9165f9c2e4d2 input=3ac3bf521bfed59d]*/ { CHECK_ATTACHED(self); CHECK_CLOSED(self); @@ -3064,12 +3079,13 @@ _io_TextIOWrapper_flush_impl(textio *self) } /*[clinic input] +@critical_section _io.TextIOWrapper.close [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_close_impl(textio *self) -/*[clinic end generated code: output=056ccf8b4876e4f4 input=9c2114315eae1948]*/ +/*[clinic end generated code: output=056ccf8b4876e4f4 input=8e12d7079d5ac5c1]*/ { PyObject *res; int r; @@ -3150,21 +3166,41 @@ textiowrapper_iternext(textio *self) } static PyObject * -textiowrapper_name_get(textio *self, void *context) +textiowrapper_name_get_impl(textio *self, void *context) { CHECK_ATTACHED(self); return PyObject_GetAttr(self->buffer, &_Py_ID(name)); } static PyObject * -textiowrapper_closed_get(textio *self, void *context) +textiowrapper_name_get(textio *self, void *context) +{ + PyObject *result = NULL; + Py_BEGIN_CRITICAL_SECTION(self); + result = textiowrapper_name_get_impl(self, context); + Py_END_CRITICAL_SECTION(); + return result; +} + +static PyObject * +textiowrapper_closed_get_impl(textio *self, void *context) { CHECK_ATTACHED(self); return PyObject_GetAttr(self->buffer, &_Py_ID(closed)); } static PyObject * -textiowrapper_newlines_get(textio *self, void *context) +textiowrapper_closed_get(textio *self, void *context) +{ + PyObject *result = NULL; + Py_BEGIN_CRITICAL_SECTION(self); + result = textiowrapper_closed_get_impl(self, context); + Py_END_CRITICAL_SECTION(); + return result; +} + +static PyObject * +textiowrapper_newlines_get_impl(textio *self, void *context) { PyObject *res; CHECK_ATTACHED(self); @@ -3177,21 +3213,51 @@ textiowrapper_newlines_get(textio *self, void *context) } static PyObject * -textiowrapper_errors_get(textio *self, void *context) +textiowrapper_newlines_get(textio *self, void *context) +{ + PyObject *result = NULL; + Py_BEGIN_CRITICAL_SECTION(self); + result = textiowrapper_newlines_get_impl(self, context); + Py_END_CRITICAL_SECTION(); + return result; +} + +static PyObject * +textiowrapper_errors_get_impl(textio *self, void *context) { CHECK_INITIALIZED(self); return Py_NewRef(self->errors); } static PyObject * -textiowrapper_chunk_size_get(textio *self, void *context) +textiowrapper_errors_get(textio *self, void *context) +{ + PyObject *result = NULL; + Py_BEGIN_CRITICAL_SECTION(self); + result = textiowrapper_errors_get_impl(self, context); + Py_END_CRITICAL_SECTION(); + return result; +} + +static PyObject * +textiowrapper_chunk_size_get_impl(textio *self, void *context) { CHECK_ATTACHED(self); return PyLong_FromSsize_t(self->chunk_size); } +static PyObject * +textiowrapper_chunk_size_get(textio *self, void *context) +{ + PyObject *result = NULL; + Py_BEGIN_CRITICAL_SECTION(self); + result = textiowrapper_chunk_size_get_impl(self, context); + Py_END_CRITICAL_SECTION(); + return result; +} + static int -textiowrapper_chunk_size_set(textio *self, PyObject *arg, void *context) +textiowrapper_chunk_size_set_impl(textio *self, PyObject *arg, void *context) { Py_ssize_t n; CHECK_ATTACHED_INT(self); @@ -3211,6 +3277,16 @@ textiowrapper_chunk_size_set(textio *self, PyObject *arg, void *context) return 0; } +static int +textiowrapper_chunk_size_set(textio *self, PyObject *arg, void *context) +{ + int result = 0; + Py_BEGIN_CRITICAL_SECTION(self); + result = textiowrapper_chunk_size_set_impl(self, arg, context); + Py_END_CRITICAL_SECTION(); + return result; +} + static PyMethodDef incrementalnewlinedecoder_methods[] = { _IO_INCREMENTALNEWLINEDECODER_DECODE_METHODDEF _IO_INCREMENTALNEWLINEDECODER_GETSTATE_METHODDEF From b8c952af7281ef9d94e292df1fedc51085635a5e Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sun, 19 Nov 2023 01:13:58 +0000 Subject: [PATCH 003/228] gh-111903: Update AC to support "pycore_critical_section.h" header (gh-112251) --- Modules/_functoolsmodule.c | 1 - Modules/_io/bufferedio.c | 1 - Modules/_io/clinic/bufferedio.c.h | 3 ++- Modules/_io/clinic/textio.c.h | 3 ++- Modules/_io/textio.c | 1 - Modules/clinic/_functoolsmodule.c.h | 3 ++- Tools/clinic/clinic.py | 3 ++- 7 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 2592c4d7f75631..9ab847165dc097 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -1,6 +1,5 @@ #include "Python.h" #include "pycore_call.h" // _PyObject_CallNoArgs() -#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION #include "pycore_dict.h" // _PyDict_Pop_KnownHash() #include "pycore_long.h" // _PyLong_GetZero() #include "pycore_moduleobject.h" // _PyModule_GetState() diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 169b2b3d105669..d7123ab005aa62 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -10,7 +10,6 @@ #include "Python.h" #include "pycore_bytesobject.h" // _PyBytes_Join() #include "pycore_call.h" // _PyObject_CallNoArgs() -#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION() #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "pycore_pyerrors.h" // _Py_FatalErrorFormat() #include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing() diff --git a/Modules/_io/clinic/bufferedio.c.h b/Modules/_io/clinic/bufferedio.c.h index 3fe03506b6cfa5..dd2fbb403c4a28 100644 --- a/Modules/_io/clinic/bufferedio.c.h +++ b/Modules/_io/clinic/bufferedio.c.h @@ -7,6 +7,7 @@ preserve # include "pycore_runtime.h" // _Py_ID() #endif #include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_BadArgument() PyDoc_STRVAR(_io__BufferedIOBase_readinto__doc__, @@ -1081,4 +1082,4 @@ _io_BufferedRandom___init__(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=b23847480eba3d9b input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2d5f735188df3163 input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/textio.c.h b/Modules/_io/clinic/textio.c.h index 76bf484f657656..675e0ed2eab75e 100644 --- a/Modules/_io/clinic/textio.c.h +++ b/Modules/_io/clinic/textio.c.h @@ -7,6 +7,7 @@ preserve # include "pycore_runtime.h" // _Py_ID() #endif #include "pycore_abstract.h" // _Py_convert_optional_to_ssize_t() +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() PyDoc_STRVAR(_io__TextIOBase_detach__doc__, @@ -1046,4 +1047,4 @@ _io_TextIOWrapper_close(textio *self, PyObject *Py_UNUSED(ignored)) return return_value; } -/*[clinic end generated code: output=ec8ccae78ec3b379 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8781a91be6d99e2c input=a9049054013a1b77]*/ diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 9f4155a5dd63ee..545f467b7f0257 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -9,7 +9,6 @@ #include "Python.h" #include "pycore_call.h" // _PyObject_CallMethod() #include "pycore_codecs.h" // _PyCodecInfo_GetIncrementalDecoder() -#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION() #include "pycore_fileutils.h" // _Py_GetLocaleEncoding() #include "pycore_interp.h" // PyInterpreterState.fs_codec #include "pycore_long.h" // _PyLong_GetZero() diff --git a/Modules/clinic/_functoolsmodule.c.h b/Modules/clinic/_functoolsmodule.c.h index 11fe0439c08a5e..e98984dc4d3a09 100644 --- a/Modules/clinic/_functoolsmodule.c.h +++ b/Modules/clinic/_functoolsmodule.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(_functools_cmp_to_key__doc__, @@ -113,4 +114,4 @@ _functools__lru_cache_wrapper_cache_clear(PyObject *self, PyObject *Py_UNUSED(ig return return_value; } -/*[clinic end generated code: output=5e3207fa0d28cdb1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=755265bb6d5ea751 input=a9049054013a1b77]*/ diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index f9326c1b2ca635..b1dfcfea92a0d0 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -1111,7 +1111,8 @@ def output_templates( if include: clinic.add_include(include.filename, include.reason, condition=include.condition) - + if f.critical_section: + clinic.add_include('pycore_critical_section.h', 'Py_BEGIN_CRITICAL_SECTION()') has_option_groups = parameters and (parameters[0].group or parameters[-1].group) simple_return = (f.return_converter.type == 'PyObject *' and not f.critical_section) From 2bcc0f7d348fd978c8e7c7c377fcdcb7b050cb1d Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sun, 19 Nov 2023 01:43:51 +0000 Subject: [PATCH 004/228] gh-112213: Update _weakref module to use new AC feature (gh-112250) --- Modules/_weakref.c | 36 ++++++++++++------------------------ Modules/clinic/_weakref.c.h | 20 +++++++++++++++++++- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/Modules/_weakref.c b/Modules/_weakref.c index 90c7afcc248ff3..7225dbc9ce4a1b 100644 --- a/Modules/_weakref.c +++ b/Modules/_weakref.c @@ -1,5 +1,4 @@ #include "Python.h" -#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION() #include "pycore_dict.h" // _PyDict_DelItemIf() #include "pycore_object.h" // _PyObject_GET_WEAKREFS_LISTPTR() #include "pycore_weakref.h" // _PyWeakref_IS_DEAD() @@ -15,7 +14,7 @@ module _weakref #include "clinic/_weakref.c.h" /*[clinic input] - +@critical_section object _weakref.getweakrefcount -> Py_ssize_t object: object @@ -26,17 +25,13 @@ Return the number of weak references to 'object'. static Py_ssize_t _weakref_getweakrefcount_impl(PyObject *module, PyObject *object) -/*[clinic end generated code: output=301806d59558ff3e input=cedb69711b6a2507]*/ +/*[clinic end generated code: output=301806d59558ff3e input=6535a580f1d0ebdc]*/ { - PyWeakReference **list; - - if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(object))) + if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(object))) { return 0; - Py_ssize_t count; - Py_BEGIN_CRITICAL_SECTION(object); - list = GET_WEAKREFS_LISTPTR(object); - count = _PyWeakref_GetWeakrefCount(*list); - Py_END_CRITICAL_SECTION(); + } + PyWeakReference **list = GET_WEAKREFS_LISTPTR(object); + Py_ssize_t count = _PyWeakref_GetWeakrefCount(*list); return count; } @@ -48,11 +43,7 @@ is_dead_weakref(PyObject *value) PyErr_SetString(PyExc_TypeError, "not a weakref"); return -1; } - int is_dead; - Py_BEGIN_CRITICAL_SECTION(value); - is_dead = _PyWeakref_IS_DEAD(value); - Py_END_CRITICAL_SECTION(); - return is_dead; + return _PyWeakref_IS_DEAD(value); } /*[clinic input] @@ -86,6 +77,7 @@ _weakref__remove_dead_weakref_impl(PyObject *module, PyObject *dct, /*[clinic input] +@critical_section object _weakref.getweakrefs object: object / @@ -94,21 +86,19 @@ Return a list of all weak reference objects pointing to 'object'. [clinic start generated code]*/ static PyObject * -_weakref_getweakrefs(PyObject *module, PyObject *object) -/*[clinic end generated code: output=25c7731d8e011824 input=00c6d0e5d3206693]*/ +_weakref_getweakrefs_impl(PyObject *module, PyObject *object) +/*[clinic end generated code: output=5ec268989fb8f035 input=3dea95b8f5b31bbb]*/ { if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(object))) { return PyList_New(0); } - PyObject *result; - Py_BEGIN_CRITICAL_SECTION(object); PyWeakReference **list = GET_WEAKREFS_LISTPTR(object); Py_ssize_t count = _PyWeakref_GetWeakrefCount(*list); - result = PyList_New(count); + PyObject *result = PyList_New(count); if (result == NULL) { - goto exit; + return NULL; } PyWeakReference *current = *list; @@ -116,8 +106,6 @@ _weakref_getweakrefs(PyObject *module, PyObject *object) PyList_SET_ITEM(result, i, Py_NewRef(current)); current = current->wr_next; } -exit: - Py_END_CRITICAL_SECTION(); return result; } diff --git a/Modules/clinic/_weakref.c.h b/Modules/clinic/_weakref.c.h index 8d7bc5dc936610..550b6c4d71a015 100644 --- a/Modules/clinic/_weakref.c.h +++ b/Modules/clinic/_weakref.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(_weakref_getweakrefcount__doc__, @@ -22,7 +23,9 @@ _weakref_getweakrefcount(PyObject *module, PyObject *object) PyObject *return_value = NULL; Py_ssize_t _return_value; + Py_BEGIN_CRITICAL_SECTION(object); _return_value = _weakref_getweakrefcount_impl(module, object); + Py_END_CRITICAL_SECTION(); if ((_return_value == -1) && PyErr_Occurred()) { goto exit; } @@ -76,6 +79,21 @@ PyDoc_STRVAR(_weakref_getweakrefs__doc__, #define _WEAKREF_GETWEAKREFS_METHODDEF \ {"getweakrefs", (PyCFunction)_weakref_getweakrefs, METH_O, _weakref_getweakrefs__doc__}, +static PyObject * +_weakref_getweakrefs_impl(PyObject *module, PyObject *object); + +static PyObject * +_weakref_getweakrefs(PyObject *module, PyObject *object) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(object); + return_value = _weakref_getweakrefs_impl(module, object); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(_weakref_proxy__doc__, "proxy($module, object, callback=None, /)\n" "--\n" @@ -112,4 +130,4 @@ _weakref_proxy(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=60f59adc1dc9eab8 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=d5d30707212a9870 input=a9049054013a1b77]*/ From 18c692946953e586db432fd06c856531a2b05127 Mon Sep 17 00:00:00 2001 From: DPR Date: Sun, 19 Nov 2023 11:21:34 +0800 Subject: [PATCH 005/228] gh-112186: Improve test case `test_loop_is_closed_resource_warnings` (#112187) --- Lib/test/test_asyncio/test_streams.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index ccb7dbf667c320..b408cd1f7da205 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -1122,13 +1122,10 @@ async def inner(httpd): self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx)) with test_utils.run_test_server() as httpd: - try: + with self.assertRaises(RuntimeError): + # This exception is caused by `self.loop.stop()` as expected. self.loop.run_until_complete(inner(httpd)) - # This exception is caused by `self.loop.stop()` as expected. - except RuntimeError: - pass - finally: - gc.collect() + gc.collect() self.assertEqual(messages, []) From adedcfa06b553242d8033f6d9bebbcb3bc0dbb4d Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Sun, 19 Nov 2023 15:20:38 +1100 Subject: [PATCH 006/228] gh-79871: IDLE - Fix and test debugger module (#11451) Add docstrings to the debugger module. Fix two bugs: initialize Idb.botframe (should be in Bdb); In Idb.in_rpc_code, check whether prev_frame is None before trying to use it. Make other code changes. Expand test_debugger coverage from 19% to 66%. --------- Co-authored-by: Terry Jan Reedy --- Lib/idlelib/debugger.py | 179 ++++++++---- Lib/idlelib/idle_test/test_debugger.py | 276 +++++++++++++++++- Lib/idlelib/pyshell.py | 16 +- Lib/idlelib/stackviewer.py | 2 + .../2019-01-07-06-18-25.bpo-35668.JimxP5.rst | 4 + 5 files changed, 400 insertions(+), 77 deletions(-) create mode 100644 Misc/NEWS.d/next/IDLE/2019-01-07-06-18-25.bpo-35668.JimxP5.rst diff --git a/Lib/idlelib/debugger.py b/Lib/idlelib/debugger.py index a92bb98d908d46..f487b4c4b16a60 100644 --- a/Lib/idlelib/debugger.py +++ b/Lib/idlelib/debugger.py @@ -1,3 +1,20 @@ +"""Debug user code with a GUI interface to a subclass of bdb.Bdb. + +The Idb idb and Debugger gui instances each need a reference to each +other or to an rpc proxy for each other. + +If IDLE is started with '-n', so that user code and idb both run in the +IDLE process, Debugger is called without an idb. Debugger.__init__ +calls Idb with its incomplete self. Idb.__init__ stores gui and gui +then stores idb. + +If IDLE is started normally, so that user code executes in a separate +process, debugger_r.start_remote_debugger is called, executing in the +IDLE process. It calls 'start the debugger' in the remote process, +which calls Idb with a gui proxy. Then Debugger is called in the IDLE +for more. +""" + import bdb import os @@ -10,66 +27,95 @@ class Idb(bdb.Bdb): + "Supply user_line and user_exception functions for Bdb." def __init__(self, gui): - self.gui = gui # An instance of Debugger or proxy of remote. - bdb.Bdb.__init__(self) + self.gui = gui # An instance of Debugger or proxy thereof. + super().__init__() def user_line(self, frame): - if self.in_rpc_code(frame): + """Handle a user stopping or breaking at a line. + + Convert frame to a string and send it to gui. + """ + if _in_rpc_code(frame): self.set_step() return - message = self.__frame2message(frame) + message = _frame2message(frame) try: self.gui.interaction(message, frame) except TclError: # When closing debugger window with [x] in 3.x pass - def user_exception(self, frame, info): - if self.in_rpc_code(frame): + def user_exception(self, frame, exc_info): + """Handle an the occurrence of an exception.""" + if _in_rpc_code(frame): self.set_step() return - message = self.__frame2message(frame) - self.gui.interaction(message, frame, info) - - def in_rpc_code(self, frame): - if frame.f_code.co_filename.count('rpc.py'): - return True - else: - prev_frame = frame.f_back - prev_name = prev_frame.f_code.co_filename - if 'idlelib' in prev_name and 'debugger' in prev_name: - # catch both idlelib/debugger.py and idlelib/debugger_r.py - # on both Posix and Windows - return False - return self.in_rpc_code(prev_frame) - - def __frame2message(self, frame): - code = frame.f_code - filename = code.co_filename - lineno = frame.f_lineno - basename = os.path.basename(filename) - message = f"{basename}:{lineno}" - if code.co_name != "?": - message = f"{message}: {code.co_name}()" - return message + message = _frame2message(frame) + self.gui.interaction(message, frame, exc_info) + +def _in_rpc_code(frame): + "Determine if debugger is within RPC code." + if frame.f_code.co_filename.count('rpc.py'): + return True # Skip this frame. + else: + prev_frame = frame.f_back + if prev_frame is None: + return False + prev_name = prev_frame.f_code.co_filename + if 'idlelib' in prev_name and 'debugger' in prev_name: + # catch both idlelib/debugger.py and idlelib/debugger_r.py + # on both Posix and Windows + return False + return _in_rpc_code(prev_frame) + +def _frame2message(frame): + """Return a message string for frame.""" + code = frame.f_code + filename = code.co_filename + lineno = frame.f_lineno + basename = os.path.basename(filename) + message = f"{basename}:{lineno}" + if code.co_name != "?": + message = f"{message}: {code.co_name}()" + return message class Debugger: - - vstack = vsource = vlocals = vglobals = None + """The debugger interface. + + This class handles the drawing of the debugger window and + the interactions with the underlying debugger session. + """ + vstack = None + vsource = None + vlocals = None + vglobals = None + stackviewer = None + localsviewer = None + globalsviewer = None def __init__(self, pyshell, idb=None): + """Instantiate and draw a debugger window. + + :param pyshell: An instance of the PyShell Window + :type pyshell: :class:`idlelib.pyshell.PyShell` + + :param idb: An instance of the IDLE debugger (optional) + :type idb: :class:`idlelib.debugger.Idb` + """ if idb is None: idb = Idb(self) self.pyshell = pyshell self.idb = idb # If passed, a proxy of remote instance. self.frame = None self.make_gui() - self.interacting = 0 + self.interacting = False self.nesting_level = 0 def run(self, *args): + """Run the debugger.""" # Deal with the scenario where we've already got a program running # in the debugger and we want to start another. If that is the case, # our second 'run' was invoked from an event dispatched not from @@ -104,12 +150,13 @@ def run(self, *args): self.root.after(100, lambda: self.run(*args)) return try: - self.interacting = 1 + self.interacting = True return self.idb.run(*args) finally: - self.interacting = 0 + self.interacting = False def close(self, event=None): + """Close the debugger and window.""" try: self.quit() except Exception: @@ -127,6 +174,7 @@ def close(self, event=None): self.top.destroy() def make_gui(self): + """Draw the debugger gui on the screen.""" pyshell = self.pyshell self.flist = pyshell.flist self.root = root = pyshell.root @@ -135,11 +183,11 @@ def make_gui(self): self.top.wm_iconname("Debug") top.wm_protocol("WM_DELETE_WINDOW", self.close) self.top.bind("", self.close) - # + self.bframe = bframe = Frame(top) self.bframe.pack(anchor="w") self.buttons = bl = [] - # + self.bcont = b = Button(bframe, text="Go", command=self.cont) bl.append(b) self.bstep = b = Button(bframe, text="Step", command=self.step) @@ -150,14 +198,14 @@ def make_gui(self): bl.append(b) self.bret = b = Button(bframe, text="Quit", command=self.quit) bl.append(b) - # + for b in bl: b.configure(state="disabled") b.pack(side="left") - # + self.cframe = cframe = Frame(bframe) self.cframe.pack(side="left") - # + if not self.vstack: self.__class__.vstack = BooleanVar(top) self.vstack.set(1) @@ -180,20 +228,20 @@ def make_gui(self): self.bglobals = Checkbutton(cframe, text="Globals", command=self.show_globals, variable=self.vglobals) self.bglobals.grid(row=1, column=1) - # + self.status = Label(top, anchor="w") self.status.pack(anchor="w") self.error = Label(top, anchor="w") self.error.pack(anchor="w", fill="x") self.errorbg = self.error.cget("background") - # + self.fstack = Frame(top, height=1) self.fstack.pack(expand=1, fill="both") self.flocals = Frame(top) self.flocals.pack(expand=1, fill="both") self.fglobals = Frame(top, height=1) self.fglobals.pack(expand=1, fill="both") - # + if self.vstack.get(): self.show_stack() if self.vlocals.get(): @@ -204,7 +252,7 @@ def make_gui(self): def interaction(self, message, frame, info=None): self.frame = frame self.status.configure(text=message) - # + if info: type, value, tb = info try: @@ -223,28 +271,28 @@ def interaction(self, message, frame, info=None): tb = None bg = self.errorbg self.error.configure(text=m1, background=bg) - # + sv = self.stackviewer if sv: stack, i = self.idb.get_stack(self.frame, tb) sv.load_stack(stack, i) - # + self.show_variables(1) - # + if self.vsource.get(): self.sync_source_line() - # + for b in self.buttons: b.configure(state="normal") - # + self.top.wakeup() # Nested main loop: Tkinter's main loop is not reentrant, so use # Tcl's vwait facility, which reenters the event loop until an - # event handler sets the variable we're waiting on + # event handler sets the variable we're waiting on. self.nesting_level += 1 self.root.tk.call('vwait', '::idledebugwait') self.nesting_level -= 1 - # + for b in self.buttons: b.configure(state="disabled") self.status.configure(text="") @@ -288,8 +336,6 @@ def quit(self): def abort_loop(self): self.root.tk.call('set', '::idledebugwait', '1') - stackviewer = None - def show_stack(self): if not self.stackviewer and self.vstack.get(): self.stackviewer = sv = StackViewer(self.fstack, self.flist, self) @@ -311,9 +357,6 @@ def show_frame(self, stackitem): self.frame = stackitem[0] # lineno is stackitem[1] self.show_variables() - localsviewer = None - globalsviewer = None - def show_locals(self): lv = self.localsviewer if self.vlocals.get(): @@ -354,26 +397,32 @@ def show_variables(self, force=0): if gv: gv.load_dict(gdict, force, self.pyshell.interp.rpcclt) - def set_breakpoint_here(self, filename, lineno): + def set_breakpoint(self, filename, lineno): + """Set a filename-lineno breakpoint in the debugger. + + Called from self.load_breakpoints and EW.setbreakpoint + """ self.idb.set_break(filename, lineno) - def clear_breakpoint_here(self, filename, lineno): + def clear_breakpoint(self, filename, lineno): self.idb.clear_break(filename, lineno) def clear_file_breaks(self, filename): self.idb.clear_all_file_breaks(filename) def load_breakpoints(self): - "Load PyShellEditorWindow breakpoints into subprocess debugger" + """Load PyShellEditorWindow breakpoints into subprocess debugger.""" for editwin in self.pyshell.flist.inversedict: filename = editwin.io.filename try: for lineno in editwin.breakpoints: - self.set_breakpoint_here(filename, lineno) + self.set_breakpoint(filename, lineno) except AttributeError: continue + class StackViewer(ScrolledList): + "Code stack viewer for debugger GUI." def __init__(self, master, flist, gui): if macosx.isAquaTk(): @@ -414,12 +463,12 @@ def load_stack(self, stack, index=None): self.select(index) def popup_event(self, event): - "override base method" + "Override base method." if self.stack: return ScrolledList.popup_event(self, event) def fill_menu(self): - "override base method" + "Override base method." menu = self.menu menu.add_command(label="Go to source line", command=self.goto_source_line) @@ -427,12 +476,12 @@ def fill_menu(self): command=self.show_stack_frame) def on_select(self, index): - "override base method" + "Override base method." if 0 <= index < len(self.stack): self.gui.show_frame(self.stack[index]) def on_double(self, index): - "override base method" + "Override base method." self.show_source(index) def goto_source_line(self): @@ -457,6 +506,7 @@ def show_source(self, index): class NamespaceViewer: + "Global/local namespace viewer for debugger GUI." def __init__(self, master, title, dict=None): width = 0 @@ -544,6 +594,7 @@ def load_dict(self, dict, force=0, rpc_client=None): def close(self): self.frame.destroy() + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_debugger', verbosity=2, exit=False) diff --git a/Lib/idlelib/idle_test/test_debugger.py b/Lib/idlelib/idle_test/test_debugger.py index 35efb3411c73b5..db01a893cb1980 100644 --- a/Lib/idlelib/idle_test/test_debugger.py +++ b/Lib/idlelib/idle_test/test_debugger.py @@ -1,11 +1,279 @@ "Test debugger, coverage 19%" from idlelib import debugger -import unittest -from test.support import requires -requires('gui') +from collections import namedtuple +from textwrap import dedent from tkinter import Tk +from test.support import requires +import unittest +from unittest import mock +from unittest.mock import Mock, patch + +"""A test python script for the debug tests.""" +TEST_CODE = dedent(""" + i = 1 + i += 2 + if i == 3: + print(i) + """) + + +class MockFrame: + "Minimal mock frame." + + def __init__(self, code, lineno): + self.f_code = code + self.f_lineno = lineno + + +class IdbTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.gui = Mock() + cls.idb = debugger.Idb(cls.gui) + + # Create test and code objects to simulate a debug session. + code_obj = compile(TEST_CODE, 'idlelib/file.py', mode='exec') + frame1 = MockFrame(code_obj, 1) + frame1.f_back = None + frame2 = MockFrame(code_obj, 2) + frame2.f_back = frame1 + cls.frame = frame2 + cls.msg = 'file.py:2: ()' + + def test_init(self): + # Test that Idb.__init_ calls Bdb.__init__. + idb = debugger.Idb(None) + self.assertIsNone(idb.gui) + self.assertTrue(hasattr(idb, 'breaks')) + + def test_user_line(self): + # Test that .user_line() creates a string message for a frame. + self.gui.interaction = Mock() + self.idb.user_line(self.frame) + self.gui.interaction.assert_called_once_with(self.msg, self.frame) + + def test_user_exception(self): + # Test that .user_exception() creates a string message for a frame. + exc_info = (type(ValueError), ValueError(), None) + self.gui.interaction = Mock() + self.idb.user_exception(self.frame, exc_info) + self.gui.interaction.assert_called_once_with( + self.msg, self.frame, exc_info) + + +class FunctionTest(unittest.TestCase): + # Test module functions together. + + def test_functions(self): + rpc_obj = compile(TEST_CODE,'rpc.py', mode='exec') + rpc_frame = MockFrame(rpc_obj, 2) + rpc_frame.f_back = rpc_frame + self.assertTrue(debugger._in_rpc_code(rpc_frame)) + self.assertEqual(debugger._frame2message(rpc_frame), + 'rpc.py:2: ()') + + code_obj = compile(TEST_CODE, 'idlelib/debugger.py', mode='exec') + code_frame = MockFrame(code_obj, 1) + code_frame.f_back = None + self.assertFalse(debugger._in_rpc_code(code_frame)) + self.assertEqual(debugger._frame2message(code_frame), + 'debugger.py:1: ()') + + code_frame.f_back = code_frame + self.assertFalse(debugger._in_rpc_code(code_frame)) + code_frame.f_back = rpc_frame + self.assertTrue(debugger._in_rpc_code(code_frame)) + + +class DebuggerTest(unittest.TestCase): + "Tests for Debugger that do not need a real root." + + @classmethod + def setUpClass(cls): + cls.pyshell = Mock() + cls.pyshell.root = Mock() + cls.idb = Mock() + with patch.object(debugger.Debugger, 'make_gui'): + cls.debugger = debugger.Debugger(cls.pyshell, cls.idb) + cls.debugger.root = Mock() + + def test_cont(self): + self.debugger.cont() + self.idb.set_continue.assert_called_once() + + def test_step(self): + self.debugger.step() + self.idb.set_step.assert_called_once() + + def test_quit(self): + self.debugger.quit() + self.idb.set_quit.assert_called_once() + + def test_next(self): + with patch.object(self.debugger, 'frame') as frame: + self.debugger.next() + self.idb.set_next.assert_called_once_with(frame) + + def test_ret(self): + with patch.object(self.debugger, 'frame') as frame: + self.debugger.ret() + self.idb.set_return.assert_called_once_with(frame) + + def test_clear_breakpoint(self): + self.debugger.clear_breakpoint('test.py', 4) + self.idb.clear_break.assert_called_once_with('test.py', 4) + + def test_clear_file_breaks(self): + self.debugger.clear_file_breaks('test.py') + self.idb.clear_all_file_breaks.assert_called_once_with('test.py') + + def test_set_load_breakpoints(self): + # Test the .load_breakpoints() method calls idb. + FileIO = namedtuple('FileIO', 'filename') + + class MockEditWindow(object): + def __init__(self, fn, breakpoints): + self.io = FileIO(fn) + self.breakpoints = breakpoints + + self.pyshell.flist = Mock() + self.pyshell.flist.inversedict = ( + MockEditWindow('test1.py', [4, 4]), + MockEditWindow('test2.py', [13, 44, 45]), + ) + self.debugger.set_breakpoint('test0.py', 1) + self.idb.set_break.assert_called_once_with('test0.py', 1) + self.debugger.load_breakpoints() # Call set_breakpoint 5 times. + self.idb.set_break.assert_has_calls( + [mock.call('test0.py', 1), + mock.call('test1.py', 4), + mock.call('test1.py', 4), + mock.call('test2.py', 13), + mock.call('test2.py', 44), + mock.call('test2.py', 45)]) + + def test_sync_source_line(self): + # Test that .sync_source_line() will set the flist.gotofileline with fixed frame. + test_code = compile(TEST_CODE, 'test_sync.py', 'exec') + test_frame = MockFrame(test_code, 1) + self.debugger.frame = test_frame + + self.debugger.flist = Mock() + with patch('idlelib.debugger.os.path.exists', return_value=True): + self.debugger.sync_source_line() + self.debugger.flist.gotofileline.assert_called_once_with('test_sync.py', 1) + + +class DebuggerGuiTest(unittest.TestCase): + """Tests for debugger.Debugger that need tk root. + + close needs debugger.top set in make_gui. + """ + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = root = Tk() + root.withdraw() + cls.pyshell = Mock() + cls.pyshell.root = root + cls.idb = Mock() +# stack tests fail with debugger here. +## cls.debugger = debugger.Debugger(cls.pyshell, cls.idb) +## cls.debugger.root = root +## # real root needed for real make_gui +## # run, interacting, abort_loop + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + del cls.root + + def setUp(self): + self.debugger = debugger.Debugger(self.pyshell, self.idb) + self.debugger.root = self.root + # real root needed for real make_gui + # run, interacting, abort_loop + + def test_run_debugger(self): + self.debugger.run(1, 'two') + self.idb.run.assert_called_once_with(1, 'two') + self.assertEqual(self.debugger.interacting, 0) + + def test_close(self): + # Test closing the window in an idle state. + self.debugger.close() + self.pyshell.close_debugger.assert_called_once() + + def test_show_stack(self): + self.debugger.show_stack() + self.assertEqual(self.debugger.stackviewer.gui, self.debugger) + + def test_show_stack_with_frame(self): + test_frame = MockFrame(None, None) + self.debugger.frame = test_frame + + # Reset the stackviewer to force it to be recreated. + self.debugger.stackviewer = None + self.idb.get_stack.return_value = ([], 0) + self.debugger.show_stack() + + # Check that the newly created stackviewer has the test gui as a field. + self.assertEqual(self.debugger.stackviewer.gui, self.debugger) + self.idb.get_stack.assert_called_once_with(test_frame, None) + + +class StackViewerTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.root.withdraw() + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + del cls.root + + def setUp(self): + self.code = compile(TEST_CODE, 'test_stackviewer.py', 'exec') + self.stack = [ + (MockFrame(self.code, 1), 1), + (MockFrame(self.code, 2), 2) + ] + # Create a stackviewer and load the test stack. + self.sv = debugger.StackViewer(self.root, None, None) + self.sv.load_stack(self.stack) + + def test_init(self): + # Test creation of StackViewer. + gui = None + flist = None + master_window = self.root + sv = debugger.StackViewer(master_window, flist, gui) + self.assertTrue(hasattr(sv, 'stack')) + + def test_load_stack(self): + # Test the .load_stack() method against a fixed test stack. + # Check the test stack is assigned and the list contains the repr of them. + self.assertEqual(self.sv.stack, self.stack) + self.assertTrue('?.(), line 1:' in self.sv.get(0)) + self.assertEqual(self.sv.get(1), '?.(), line 2: ') + + def test_show_source(self): + # Test the .show_source() method against a fixed test stack. + # Patch out the file list to monitor it + self.sv.flist = Mock() + # Patch out isfile to pretend file exists. + with patch('idlelib.debugger.os.path.isfile', return_value=True) as isfile: + self.sv.show_source(1) + isfile.assert_called_once_with('test_stackviewer.py') + self.sv.flist.open.assert_called_once_with('test_stackviewer.py') + class NameSpaceTest(unittest.TestCase): @@ -23,7 +291,5 @@ def test_init(self): debugger.NamespaceViewer(self.root, 'Test') -# Other classes are Idb, Debugger, and StackViewer. - if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 7a2707935b60c9..00b3732a7bc4eb 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -133,8 +133,8 @@ class PyShellEditorWindow(EditorWindow): def __init__(self, *args): self.breakpoints = [] EditorWindow.__init__(self, *args) - self.text.bind("<>", self.set_breakpoint_here) - self.text.bind("<>", self.clear_breakpoint_here) + self.text.bind("<>", self.set_breakpoint_event) + self.text.bind("<>", self.clear_breakpoint_event) self.text.bind("<>", self.flist.open_shell) #TODO: don't read/write this from/to .idlerc when testing @@ -155,8 +155,8 @@ def filename_changed_hook(old_hook=self.io.filename_change_hook, ("Copy", "<>", "rmenu_check_copy"), ("Paste", "<>", "rmenu_check_paste"), (None, None, None), - ("Set Breakpoint", "<>", None), - ("Clear Breakpoint", "<>", None) + ("Set Breakpoint", "<>", None), + ("Clear Breakpoint", "<>", None) ] def color_breakpoint_text(self, color=True): @@ -181,11 +181,11 @@ def set_breakpoint(self, lineno): self.breakpoints.append(lineno) try: # update the subprocess debugger debug = self.flist.pyshell.interp.debugger - debug.set_breakpoint_here(filename, lineno) + debug.set_breakpoint(filename, lineno) except: # but debugger may not be active right now.... pass - def set_breakpoint_here(self, event=None): + def set_breakpoint_event(self, event=None): text = self.text filename = self.io.filename if not filename: @@ -194,7 +194,7 @@ def set_breakpoint_here(self, event=None): lineno = int(float(text.index("insert"))) self.set_breakpoint(lineno) - def clear_breakpoint_here(self, event=None): + def clear_breakpoint_event(self, event=None): text = self.text filename = self.io.filename if not filename: @@ -209,7 +209,7 @@ def clear_breakpoint_here(self, event=None): "insert lineend +1char") try: debug = self.flist.pyshell.interp.debugger - debug.clear_breakpoint_here(filename, lineno) + debug.clear_breakpoint(filename, lineno) except: pass diff --git a/Lib/idlelib/stackviewer.py b/Lib/idlelib/stackviewer.py index 4858cc682a4f45..f8e60fd9b6d818 100644 --- a/Lib/idlelib/stackviewer.py +++ b/Lib/idlelib/stackviewer.py @@ -1,3 +1,5 @@ +# Rename to stackbrowser or possibly consolidate with browser. + import linecache import os diff --git a/Misc/NEWS.d/next/IDLE/2019-01-07-06-18-25.bpo-35668.JimxP5.rst b/Misc/NEWS.d/next/IDLE/2019-01-07-06-18-25.bpo-35668.JimxP5.rst new file mode 100644 index 00000000000000..8bb5420517d55f --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-01-07-06-18-25.bpo-35668.JimxP5.rst @@ -0,0 +1,4 @@ +Add docstrings to the IDLE debugger module. Fix two bugs: +initialize Idb.botframe (should be in Bdb); in Idb.in_rpc_code, +check whether prev_frame is None before trying to use it. +Greatly expand test_debugger. From 14fd86a59d0d91fe72641efeb14a59d99127dec3 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 19 Nov 2023 01:39:26 -0500 Subject: [PATCH 007/228] IDLE: Fix test_debugger bug and buildbot failures (#112258) Missing "requires('gui')" causes Tk() to fail when no gui. This caused CI Hypothesis test to fail, but I did not understand the its error message. Then buildbots failed. IdbTest failed on draft Bdb replacement because so different. Simplified version works on old and new. --- Lib/idlelib/idle_test/test_debugger.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Lib/idlelib/idle_test/test_debugger.py b/Lib/idlelib/idle_test/test_debugger.py index db01a893cb1980..d1c9638dd5d711 100644 --- a/Lib/idlelib/idle_test/test_debugger.py +++ b/Lib/idlelib/idle_test/test_debugger.py @@ -1,4 +1,7 @@ -"Test debugger, coverage 19%" +"""Test debugger, coverage 66% + +Try to make tests pass with draft bdbx, which may replace bdb in 3.13+. +""" from idlelib import debugger from collections import namedtuple @@ -44,10 +47,8 @@ def setUpClass(cls): cls.msg = 'file.py:2: ()' def test_init(self): - # Test that Idb.__init_ calls Bdb.__init__. - idb = debugger.Idb(None) - self.assertIsNone(idb.gui) - self.assertTrue(hasattr(idb, 'breaks')) + self.assertIs(self.idb.gui, self.gui) + # Won't test super call since two Bdbs are very different. def test_user_line(self): # Test that .user_line() creates a string message for a frame. @@ -279,6 +280,7 @@ class NameSpaceTest(unittest.TestCase): @classmethod def setUpClass(cls): + requires('gui') cls.root = Tk() cls.root.withdraw() From 6bf8f20344333af70acb566998d3e9f44c889555 Mon Sep 17 00:00:00 2001 From: Alex Ptakhin Date: Sun, 19 Nov 2023 10:56:54 +0100 Subject: [PATCH 008/228] gh-110383: Fix documentation profile cumtime fix (#112221) Co-authored-by: Hugo van Kemenade --- Doc/library/profile.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/profile.rst b/Doc/library/profile.rst index 4c60a1e0d781b0..cc059b66fcb84b 100644 --- a/Doc/library/profile.rst +++ b/Doc/library/profile.rst @@ -82,8 +82,8 @@ the following:: The first line indicates that 214 calls were monitored. Of those calls, 207 were :dfn:`primitive`, meaning that the call was not induced via recursion. The -next line: ``Ordered by: cumulative time``, indicates that the text string in the -far right column was used to sort the output. The column headings include: +next line: ``Ordered by: cumulative time`` indicates the output is sorted +by the ``cumtime`` values. The column headings include: ncalls for the number of calls. From a6d25de375087e27777ebc1c0bd106d532ef9083 Mon Sep 17 00:00:00 2001 From: Unique-Usman <86585626+Unique-Usman@users.noreply.github.com> Date: Sun, 19 Nov 2023 17:50:10 +0530 Subject: [PATCH 009/228] gh-110383: Explained which error message is generated when there is an unhandled exception (#111574) Co-authored-by: Hugo van Kemenade --- Doc/tutorial/errors.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/tutorial/errors.rst b/Doc/tutorial/errors.rst index 1ec59767e9ce12..4058ebe8efdb42 100644 --- a/Doc/tutorial/errors.rst +++ b/Doc/tutorial/errors.rst @@ -108,8 +108,7 @@ The :keyword:`try` statement works as follows. * If an exception occurs which does not match the exception named in the *except clause*, it is passed on to outer :keyword:`try` statements; if no handler is - found, it is an *unhandled exception* and execution stops with a message as - shown above. + found, it is an *unhandled exception* and execution stops with an error message. A :keyword:`try` statement may have more than one *except clause*, to specify handlers for different exceptions. At most one handler will be executed. From 77d9f1e6d9aad637667264c16c83d255526cc1ba Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 19 Nov 2023 20:34:40 +0800 Subject: [PATCH 010/228] gh-111965: Using critical sections to make ``io.StringIO`` thread safe. (gh-112116) --- Modules/_io/clinic/stringio.c.h | 120 ++++++++++++++++++++++++++++++-- Modules/_io/stringio.c | 104 +++++++++++++++++++++------ 2 files changed, 194 insertions(+), 30 deletions(-) diff --git a/Modules/_io/clinic/stringio.c.h b/Modules/_io/clinic/stringio.c.h index 571ec5117147d5..8e5c687dc6a55f 100644 --- a/Modules/_io/clinic/stringio.c.h +++ b/Modules/_io/clinic/stringio.c.h @@ -7,6 +7,7 @@ preserve # include "pycore_runtime.h" // _Py_ID() #endif #include "pycore_abstract.h" // _Py_convert_optional_to_ssize_t() +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_CheckPositional() PyDoc_STRVAR(_io_StringIO_getvalue__doc__, @@ -24,7 +25,13 @@ _io_StringIO_getvalue_impl(stringio *self); static PyObject * _io_StringIO_getvalue(stringio *self, PyObject *Py_UNUSED(ignored)) { - return _io_StringIO_getvalue_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_getvalue_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_StringIO_tell__doc__, @@ -42,7 +49,13 @@ _io_StringIO_tell_impl(stringio *self); static PyObject * _io_StringIO_tell(stringio *self, PyObject *Py_UNUSED(ignored)) { - return _io_StringIO_tell_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_tell_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_StringIO_read__doc__, @@ -76,7 +89,9 @@ _io_StringIO_read(stringio *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_StringIO_read_impl(self, size); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -112,7 +127,9 @@ _io_StringIO_readline(stringio *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_StringIO_readline_impl(self, size); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -150,7 +167,9 @@ _io_StringIO_truncate(stringio *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_StringIO_truncate_impl(self, size); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -204,7 +223,9 @@ _io_StringIO_seek(stringio *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_StringIO_seek_impl(self, pos, whence); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -222,6 +243,21 @@ PyDoc_STRVAR(_io_StringIO_write__doc__, #define _IO_STRINGIO_WRITE_METHODDEF \ {"write", (PyCFunction)_io_StringIO_write, METH_O, _io_StringIO_write__doc__}, +static PyObject * +_io_StringIO_write_impl(stringio *self, PyObject *obj); + +static PyObject * +_io_StringIO_write(stringio *self, PyObject *obj) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_write_impl(self, obj); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(_io_StringIO_close__doc__, "close($self, /)\n" "--\n" @@ -242,7 +278,13 @@ _io_StringIO_close_impl(stringio *self); static PyObject * _io_StringIO_close(stringio *self, PyObject *Py_UNUSED(ignored)) { - return _io_StringIO_close_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_close_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_StringIO___init____doc__, @@ -330,7 +372,13 @@ _io_StringIO_readable_impl(stringio *self); static PyObject * _io_StringIO_readable(stringio *self, PyObject *Py_UNUSED(ignored)) { - return _io_StringIO_readable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_readable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_StringIO_writable__doc__, @@ -348,7 +396,13 @@ _io_StringIO_writable_impl(stringio *self); static PyObject * _io_StringIO_writable(stringio *self, PyObject *Py_UNUSED(ignored)) { - return _io_StringIO_writable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_writable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_StringIO_seekable__doc__, @@ -366,6 +420,58 @@ _io_StringIO_seekable_impl(stringio *self); static PyObject * _io_StringIO_seekable(stringio *self, PyObject *Py_UNUSED(ignored)) { - return _io_StringIO_seekable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_seekable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_io_StringIO___getstate____doc__, +"__getstate__($self, /)\n" +"--\n" +"\n"); + +#define _IO_STRINGIO___GETSTATE___METHODDEF \ + {"__getstate__", (PyCFunction)_io_StringIO___getstate__, METH_NOARGS, _io_StringIO___getstate____doc__}, + +static PyObject * +_io_StringIO___getstate___impl(stringio *self); + +static PyObject * +_io_StringIO___getstate__(stringio *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO___getstate___impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_io_StringIO___setstate____doc__, +"__setstate__($self, state, /)\n" +"--\n" +"\n"); + +#define _IO_STRINGIO___SETSTATE___METHODDEF \ + {"__setstate__", (PyCFunction)_io_StringIO___setstate__, METH_O, _io_StringIO___setstate____doc__}, + +static PyObject * +_io_StringIO___setstate___impl(stringio *self, PyObject *state); + +static PyObject * +_io_StringIO___setstate__(stringio *self, PyObject *state) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO___setstate___impl(self, state); + Py_END_CRITICAL_SECTION(); + + return return_value; } -/*[clinic end generated code: output=f56aa7f8a271acf6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5c8d67f4408a1e6e input=a9049054013a1b77]*/ diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index 1856b07108bab6..0aa5e34cd7c8b2 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -45,6 +45,10 @@ typedef struct { _PyIO_State *module_state; } stringio; +#define clinic_state() (find_io_state_by_def(Py_TYPE(self))) +#include "clinic/stringio.c.h" +#undef clinic_state + static int _io_StringIO___init__(PyObject *self, PyObject *args, PyObject *kwargs); #define CHECK_INITIALIZED(self) \ @@ -263,6 +267,7 @@ write_str(stringio *self, PyObject *obj) } /*[clinic input] +@critical_section _io.StringIO.getvalue Retrieve the entire contents of the object. @@ -270,7 +275,7 @@ Retrieve the entire contents of the object. static PyObject * _io_StringIO_getvalue_impl(stringio *self) -/*[clinic end generated code: output=27b6a7bfeaebce01 input=d23cb81d6791cf88]*/ +/*[clinic end generated code: output=27b6a7bfeaebce01 input=fb5dee06b8d467f3]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -281,6 +286,7 @@ _io_StringIO_getvalue_impl(stringio *self) } /*[clinic input] +@critical_section _io.StringIO.tell Tell the current file position. @@ -288,7 +294,7 @@ Tell the current file position. static PyObject * _io_StringIO_tell_impl(stringio *self) -/*[clinic end generated code: output=2e87ac67b116c77b input=ec866ebaff02f405]*/ +/*[clinic end generated code: output=2e87ac67b116c77b input=98a08f3e2dae3550]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -296,6 +302,7 @@ _io_StringIO_tell_impl(stringio *self) } /*[clinic input] +@critical_section _io.StringIO.read size: Py_ssize_t(accept={int, NoneType}) = -1 / @@ -308,7 +315,7 @@ is reached. Return an empty string at EOF. static PyObject * _io_StringIO_read_impl(stringio *self, Py_ssize_t size) -/*[clinic end generated code: output=ae8cf6002f71626c input=0921093383dfb92d]*/ +/*[clinic end generated code: output=ae8cf6002f71626c input=9fbef45d8aece8e7]*/ { Py_ssize_t n; Py_UCS4 *output; @@ -368,6 +375,7 @@ _stringio_readline(stringio *self, Py_ssize_t limit) } /*[clinic input] +@critical_section _io.StringIO.readline size: Py_ssize_t(accept={int, NoneType}) = -1 / @@ -379,7 +387,7 @@ Returns an empty string if EOF is hit immediately. static PyObject * _io_StringIO_readline_impl(stringio *self, Py_ssize_t size) -/*[clinic end generated code: output=cabd6452f1b7e85d input=a5bd70bf682aa276]*/ +/*[clinic end generated code: output=cabd6452f1b7e85d input=4d14b8495dea1d98]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -427,6 +435,7 @@ stringio_iternext(stringio *self) } /*[clinic input] +@critical_section _io.StringIO.truncate pos as size: Py_ssize_t(accept={int, NoneType}, c_default="self->pos") = None / @@ -440,7 +449,7 @@ Returns the new absolute position. static PyObject * _io_StringIO_truncate_impl(stringio *self, Py_ssize_t size) -/*[clinic end generated code: output=eb3aef8e06701365 input=5505cff90ca48b96]*/ +/*[clinic end generated code: output=eb3aef8e06701365 input=461b872dce238452]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -462,6 +471,7 @@ _io_StringIO_truncate_impl(stringio *self, Py_ssize_t size) } /*[clinic input] +@critical_section _io.StringIO.seek pos: Py_ssize_t whence: int = 0 @@ -478,7 +488,7 @@ Returns the new absolute position. static PyObject * _io_StringIO_seek_impl(stringio *self, Py_ssize_t pos, int whence) -/*[clinic end generated code: output=e9e0ac9a8ae71c25 input=e3855b24e7cae06a]*/ +/*[clinic end generated code: output=e9e0ac9a8ae71c25 input=c75ced09343a00d7]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -515,6 +525,7 @@ _io_StringIO_seek_impl(stringio *self, Py_ssize_t pos, int whence) } /*[clinic input] +@critical_section _io.StringIO.write s as obj: object / @@ -526,8 +537,8 @@ the length of the string. [clinic start generated code]*/ static PyObject * -_io_StringIO_write(stringio *self, PyObject *obj) -/*[clinic end generated code: output=0deaba91a15b94da input=cf96f3b16586e669]*/ +_io_StringIO_write_impl(stringio *self, PyObject *obj) +/*[clinic end generated code: output=d53b1d841d7db288 input=1561272c0da4651f]*/ { Py_ssize_t size; @@ -547,6 +558,7 @@ _io_StringIO_write(stringio *self, PyObject *obj) } /*[clinic input] +@critical_section _io.StringIO.close Close the IO object. @@ -559,7 +571,7 @@ This method has no effect if the file is already closed. static PyObject * _io_StringIO_close_impl(stringio *self) -/*[clinic end generated code: output=04399355cbe518f1 input=cbc10b45f35d6d46]*/ +/*[clinic end generated code: output=04399355cbe518f1 input=305d19aa29cc40b9]*/ { self->closed = 1; /* Free up some memory */ @@ -756,6 +768,7 @@ _io_StringIO___init___impl(stringio *self, PyObject *value, /* Properties and pseudo-properties */ /*[clinic input] +@critical_section _io.StringIO.readable Returns True if the IO object can be read. @@ -763,7 +776,7 @@ Returns True if the IO object can be read. static PyObject * _io_StringIO_readable_impl(stringio *self) -/*[clinic end generated code: output=b19d44dd8b1ceb99 input=39ce068b224c21ad]*/ +/*[clinic end generated code: output=b19d44dd8b1ceb99 input=6cd2ffd65a8e8763]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -771,6 +784,7 @@ _io_StringIO_readable_impl(stringio *self) } /*[clinic input] +@critical_section _io.StringIO.writable Returns True if the IO object can be written. @@ -778,7 +792,7 @@ Returns True if the IO object can be written. static PyObject * _io_StringIO_writable_impl(stringio *self) -/*[clinic end generated code: output=13e4dd77187074ca input=7a691353aac38835]*/ +/*[clinic end generated code: output=13e4dd77187074ca input=1b3c63dbaa761c69]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -786,6 +800,7 @@ _io_StringIO_writable_impl(stringio *self) } /*[clinic input] +@critical_section _io.StringIO.seekable Returns True if the IO object can be seeked. @@ -793,7 +808,7 @@ Returns True if the IO object can be seeked. static PyObject * _io_StringIO_seekable_impl(stringio *self) -/*[clinic end generated code: output=4d20b4641c756879 input=4c606d05b32952e6]*/ +/*[clinic end generated code: output=4d20b4641c756879 input=a820fad2cf085fc3]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -812,8 +827,15 @@ _io_StringIO_seekable_impl(stringio *self) supported. */ +/*[clinic input] +@critical_section +_io.StringIO.__getstate__ + +[clinic start generated code]*/ + static PyObject * -stringio_getstate(stringio *self, PyObject *Py_UNUSED(ignored)) +_io_StringIO___getstate___impl(stringio *self) +/*[clinic end generated code: output=780be4a996410199 input=76f27255ef83bb92]*/ { PyObject *initvalue = _io_StringIO_getvalue_impl(self); PyObject *dict; @@ -839,8 +861,17 @@ stringio_getstate(stringio *self, PyObject *Py_UNUSED(ignored)) return state; } +/*[clinic input] +@critical_section +_io.StringIO.__setstate__ + + state: object + / +[clinic start generated code]*/ + static PyObject * -stringio_setstate(stringio *self, PyObject *state) +_io_StringIO___setstate___impl(stringio *self, PyObject *state) +/*[clinic end generated code: output=cb3962bc6d5c5609 input=8a27784b11b82e47]*/ { PyObject *initarg; PyObject *position_obj; @@ -941,14 +972,24 @@ stringio_setstate(stringio *self, PyObject *state) static PyObject * -stringio_closed(stringio *self, void *context) +stringio_closed_impl(stringio *self, void *context) { CHECK_INITIALIZED(self); return PyBool_FromLong(self->closed); } static PyObject * -stringio_line_buffering(stringio *self, void *context) +stringio_closed(stringio *self, void *context) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(self); + result = stringio_closed_impl(self, context); + Py_END_CRITICAL_SECTION(); + return result; +} + +static PyObject * +stringio_line_buffering_impl(stringio *self, void *context) { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -956,18 +997,35 @@ stringio_line_buffering(stringio *self, void *context) } static PyObject * -stringio_newlines(stringio *self, void *context) +stringio_line_buffering(stringio *self, void *context) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(self); + result = stringio_line_buffering_impl(self, context); + Py_END_CRITICAL_SECTION(); + return result; +} + +static PyObject * +stringio_newlines_impl(stringio *self, void *context) { CHECK_INITIALIZED(self); CHECK_CLOSED(self); - if (self->decoder == NULL) + if (self->decoder == NULL) { Py_RETURN_NONE; + } return PyObject_GetAttr(self->decoder, &_Py_ID(newlines)); } -#define clinic_state() (find_io_state_by_def(Py_TYPE(self))) -#include "clinic/stringio.c.h" -#undef clinic_state +static PyObject * +stringio_newlines(stringio *self, void *context) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(self); + result = stringio_newlines_impl(self, context); + Py_END_CRITICAL_SECTION(); + return result; +} static struct PyMethodDef stringio_methods[] = { _IO_STRINGIO_CLOSE_METHODDEF @@ -983,8 +1041,8 @@ static struct PyMethodDef stringio_methods[] = { _IO_STRINGIO_READABLE_METHODDEF _IO_STRINGIO_WRITABLE_METHODDEF - {"__getstate__", (PyCFunction)stringio_getstate, METH_NOARGS}, - {"__setstate__", (PyCFunction)stringio_setstate, METH_O}, + _IO_STRINGIO___GETSTATE___METHODDEF + _IO_STRINGIO___SETSTATE___METHODDEF {NULL, NULL} /* sentinel */ }; From f8129146ef9e1b71609ef4becc5d508061970733 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sun, 19 Nov 2023 21:30:07 +0300 Subject: [PATCH 011/228] gh-112266: Remove `(if defined)` part from `__dict__` and `__weakref__` docstrings (#112268) --- Lib/test/test_pydoc.py | 28 +++++++++---------- ...-11-19-15-57-23.gh-issue-112266.BSJMbR.rst | 2 ++ Objects/typeobject.c | 8 +++--- 3 files changed, 20 insertions(+), 18 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-19-15-57-23.gh-issue-112266.BSJMbR.rst diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 70c5ebd694ca88..745717f492e07a 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -43,8 +43,8 @@ class nonascii: if test.support.HAVE_DOCSTRINGS: expected_data_docstrings = ( - 'dictionary for instance variables (if defined)', - 'list of weak references to the object (if defined)', + 'dictionary for instance variables', + 'list of weak references to the object', ) * 2 else: expected_data_docstrings = ('', '', '', '') @@ -108,10 +108,10 @@ class C(builtins.object) | Data descriptors defined here: | | __dict__ - | dictionary for instance variables (if defined) + | dictionary for instance variables | | __weakref__ - | list of weak references to the object (if defined) + | list of weak references to the object FUNCTIONS doc_func() @@ -169,16 +169,16 @@ class A(builtins.object) Data descriptors defined here: __dict__ - dictionary for instance variables (if defined) + dictionary for instance variables __weakref__ - list of weak references to the object (if defined) + list of weak references to the object class B(builtins.object) Data descriptors defined here: __dict__ - dictionary for instance variables (if defined) + dictionary for instance variables __weakref__ - list of weak references to the object (if defined) + list of weak references to the object Data and other attributes defined here: NO_MEANING = 'eggs' __annotations__ = {'NO_MEANING': } @@ -195,9 +195,9 @@ class C(builtins.object) __class_getitem__(item) from builtins.type Data descriptors defined here: __dict__ - dictionary for instance variables (if defined) + dictionary for instance variables __weakref__ - list of weak references to the object (if defined) + list of weak references to the object Functions doc_func() @@ -829,10 +829,10 @@ class B(A) | Data descriptors inherited from A: | | __dict__ - | dictionary for instance variables (if defined) + | dictionary for instance variables | | __weakref__ - | list of weak references to the object (if defined) + | list of weak references to the object ''' % __name__) doc = pydoc.render_doc(B, renderer=pydoc.HTMLDoc()) @@ -861,9 +861,9 @@ class B(A) Data descriptors inherited from A: __dict__ - dictionary for instance variables (if defined) + dictionary for instance variables __weakref__ - list of weak references to the object (if defined) + list of weak references to the object """ as_text = html2text(doc) expected_lines = [line.strip() for line in expected_text.split("\n") if line] diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-19-15-57-23.gh-issue-112266.BSJMbR.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-19-15-57-23.gh-issue-112266.BSJMbR.rst new file mode 100644 index 00000000000000..18433db9bb976e --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-19-15-57-23.gh-issue-112266.BSJMbR.rst @@ -0,0 +1,2 @@ +Change docstrings of :attr:`~object.__dict__` and +:attr:`~object.__weakref__`. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 4464b5af8cd15b..f5975a3cd90cb4 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3006,21 +3006,21 @@ subtype_getweakref(PyObject *obj, void *context) static PyGetSetDef subtype_getsets_full[] = { {"__dict__", subtype_dict, subtype_setdict, - PyDoc_STR("dictionary for instance variables (if defined)")}, + PyDoc_STR("dictionary for instance variables")}, {"__weakref__", subtype_getweakref, NULL, - PyDoc_STR("list of weak references to the object (if defined)")}, + PyDoc_STR("list of weak references to the object")}, {0} }; static PyGetSetDef subtype_getsets_dict_only[] = { {"__dict__", subtype_dict, subtype_setdict, - PyDoc_STR("dictionary for instance variables (if defined)")}, + PyDoc_STR("dictionary for instance variables")}, {0} }; static PyGetSetDef subtype_getsets_weakref_only[] = { {"__weakref__", subtype_getweakref, NULL, - PyDoc_STR("list of weak references to the object (if defined)")}, + PyDoc_STR("list of weak references to the object")}, {0} }; From 7c9f2677fbb8805f7d531fb96731339727e8ea20 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sun, 19 Nov 2023 22:36:45 +0000 Subject: [PATCH 012/228] gh-111926: Update _PyWeakref_IS_DEAD to be thread-safe (gh-112267) --- Include/internal/pycore_weakref.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Include/internal/pycore_weakref.h b/Include/internal/pycore_weakref.h index 51b2bb6b11ede9..eacbe14c903289 100644 --- a/Include/internal/pycore_weakref.h +++ b/Include/internal/pycore_weakref.h @@ -8,6 +8,8 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif +#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION() + static inline PyObject* _PyWeakref_GET_REF(PyObject *ref_obj) { assert(PyWeakref_Check(ref_obj)); PyWeakReference *ref = _Py_CAST(PyWeakReference*, ref_obj); @@ -35,15 +37,20 @@ static inline PyObject* _PyWeakref_GET_REF(PyObject *ref_obj) { static inline int _PyWeakref_IS_DEAD(PyObject *ref_obj) { assert(PyWeakref_Check(ref_obj)); + int is_dead; + Py_BEGIN_CRITICAL_SECTION(ref_obj); PyWeakReference *ref = _Py_CAST(PyWeakReference*, ref_obj); PyObject *obj = ref->wr_object; if (obj == Py_None) { // clear_weakref() was called - return 1; + is_dead = 1; } - - // See _PyWeakref_GET_REF() for the rationale of this test - return (Py_REFCNT(obj) == 0); + else { + // See _PyWeakref_GET_REF() for the rationale of this test + is_dead = (Py_REFCNT(obj) == 0); + } + Py_END_CRITICAL_SECTION(); + return is_dead; } extern Py_ssize_t _PyWeakref_GetWeakrefCount(PyWeakReference *head); From ce1096f974d3158a92e050f9226700775b8db398 Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Sun, 19 Nov 2023 23:37:13 +0100 Subject: [PATCH 013/228] gh-73561: Omit interface scope from IPv6 when used as Host header (#93324) Omit the `@interface_scope` from an IPv6 address when used as Host header by `http.client`. --------- Co-authored-by: Gregory P. Smith [Google LLC] --- Lib/http/client.py | 12 ++++++++++-- Lib/test/test_httplib.py | 16 ++++++++++++++++ ...2022-05-28-20-55-07.gh-issue-73561.YRmAvy.rst | 1 + 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-05-28-20-55-07.gh-issue-73561.YRmAvy.rst diff --git a/Lib/http/client.py b/Lib/http/client.py index b35b1d6368aae7..7bb5d824bb6da4 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -172,6 +172,13 @@ def _encode(data, name='data'): "if you want to send it encoded in UTF-8." % (name.title(), data[err.start:err.end], name)) from None +def _strip_ipv6_iface(enc_name: bytes) -> bytes: + """Remove interface scope from IPv6 address.""" + enc_name, percent, _ = enc_name.partition(b"%") + if percent: + assert enc_name.startswith(b'['), enc_name + enc_name += b']' + return enc_name class HTTPMessage(email.message.Message): # XXX The only usage of this method is in @@ -1194,7 +1201,7 @@ def putrequest(self, method, url, skip_host=False, netloc_enc = netloc.encode("ascii") except UnicodeEncodeError: netloc_enc = netloc.encode("idna") - self.putheader('Host', netloc_enc) + self.putheader('Host', _strip_ipv6_iface(netloc_enc)) else: if self._tunnel_host: host = self._tunnel_host @@ -1211,8 +1218,9 @@ def putrequest(self, method, url, skip_host=False, # As per RFC 273, IPv6 address should be wrapped with [] # when used as Host header - if host.find(':') >= 0: + if ":" in host: host_enc = b'[' + host_enc + b']' + host_enc = _strip_ipv6_iface(host_enc) if port == self.default_port: self.putheader('Host', host_enc) diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 5d5832b62b2f94..caa4c76a913a01 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -283,6 +283,22 @@ def test_ipv6host_header(self): conn.request('GET', '/foo') self.assertTrue(sock.data.startswith(expected)) + expected = b'GET /foo HTTP/1.1\r\nHost: [fe80::]\r\n' \ + b'Accept-Encoding: identity\r\n\r\n' + conn = client.HTTPConnection('[fe80::%2]') + sock = FakeSocket('') + conn.sock = sock + conn.request('GET', '/foo') + self.assertTrue(sock.data.startswith(expected)) + + expected = b'GET /foo HTTP/1.1\r\nHost: [fe80::]:81\r\n' \ + b'Accept-Encoding: identity\r\n\r\n' + conn = client.HTTPConnection('[fe80::%2]:81') + sock = FakeSocket('') + conn.sock = sock + conn.request('GET', '/foo') + self.assertTrue(sock.data.startswith(expected)) + def test_malformed_headers_coped_with(self): # Issue 19996 body = "HTTP/1.1 200 OK\r\nFirst: val\r\n: nval\r\nSecond: val\r\n\r\n" diff --git a/Misc/NEWS.d/next/Library/2022-05-28-20-55-07.gh-issue-73561.YRmAvy.rst b/Misc/NEWS.d/next/Library/2022-05-28-20-55-07.gh-issue-73561.YRmAvy.rst new file mode 100644 index 00000000000000..5e00b7d20b8ca8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-05-28-20-55-07.gh-issue-73561.YRmAvy.rst @@ -0,0 +1 @@ +Omit the interface scope from an IPv6 address when used as Host header by :mod:`http.client`. From 56e59a49ae4d9f518c5cc918aefe7eeee11736b4 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Mon, 20 Nov 2023 05:27:33 -0800 Subject: [PATCH 014/228] GH-111807: Lower the parser stack depth under WASI debug builds (#112225) --- .../2023-11-17-16-49-32.gh-issue-111807.QvjP9_.rst | 1 + Parser/parser.c | 6 +++++- Tools/peg_generator/pegen/c_generator.py | 6 +++++- 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-17-16-49-32.gh-issue-111807.QvjP9_.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-17-16-49-32.gh-issue-111807.QvjP9_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-17-16-49-32.gh-issue-111807.QvjP9_.rst new file mode 100644 index 00000000000000..6f075845e11b86 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-17-16-49-32.gh-issue-111807.QvjP9_.rst @@ -0,0 +1 @@ +Lower the max parser stack depth to 1000 under WASI debug builds. diff --git a/Parser/parser.c b/Parser/parser.c index ca8e9d0e23661c..d27ddd3d3e3835 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -8,7 +8,11 @@ #endif #ifdef __wasi__ -# define MAXSTACK 4000 +# ifdef Py_DEBUG +# define MAXSTACK 1000 +# else +# define MAXSTACK 4000 +# endif #else # define MAXSTACK 6000 #endif diff --git a/Tools/peg_generator/pegen/c_generator.py b/Tools/peg_generator/pegen/c_generator.py index 301949bdae96fe..7cdd5debe9a225 100644 --- a/Tools/peg_generator/pegen/c_generator.py +++ b/Tools/peg_generator/pegen/c_generator.py @@ -38,7 +38,11 @@ #endif #ifdef __wasi__ -# define MAXSTACK 4000 +# ifdef Py_DEBUG +# define MAXSTACK 1000 +# else +# define MAXSTACK 4000 +# endif #else # define MAXSTACK 6000 #endif From 1c8f912ebdfdb146cd7dd2d7a3a67d2c5045ddb0 Mon Sep 17 00:00:00 2001 From: Crowthebird <78076854+thatbirdguythatuknownot@users.noreply.github.com> Date: Mon, 20 Nov 2023 21:27:53 +0800 Subject: [PATCH 015/228] bpo-45759: Better error messages for non-matching 'elif'/'else' statements (#29513) --- Grammar/python.gram | 5 + Lib/test/test_syntax.py | 61 +- .../2021-11-10-10-40-05.bpo-45759.WJoB3D.rst | 1 + Parser/parser.c | 968 ++++++++++-------- 4 files changed, 598 insertions(+), 437 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-11-10-10-40-05.bpo-45759.WJoB3D.rst diff --git a/Grammar/python.gram b/Grammar/python.gram index 55930b0f357ca0..c9269b058bc7f6 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -124,6 +124,7 @@ simple_stmt[stmt_ty] (memo): | &'nonlocal' nonlocal_stmt compound_stmt[stmt_ty]: + | invalid_compound_stmt | &('def' | '@' | 'async') function_def | &'if' if_stmt | &('class' | '@') class_def @@ -1298,6 +1299,10 @@ invalid_import_from_targets: | import_from_as_names ',' NEWLINE { RAISE_SYNTAX_ERROR("trailing comma not allowed without surrounding parentheses") } +invalid_compound_stmt: + | a='elif' named_expression ':' { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "'elif' must match an if-statement here") } + | a='else' ':' { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "'else' must match a valid statement here") } + invalid_with_stmt: | ['async'] 'with' ','.(expression ['as' star_target])+ NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") } | ['async'] 'with' '(' ','.(expressions ['as' star_target])+ ','? ')' NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") } diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 7ebf9ca1707acd..f6fa6495508d2c 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -1752,6 +1752,28 @@ Traceback (most recent call last): SyntaxError: positional patterns follow keyword patterns +Non-matching 'elif'/'else' statements: + + >>> if a == b: + ... ... + ... elif a == c: + Traceback (most recent call last): + SyntaxError: 'elif' must match an if-statement here + + >>> if x == y: + ... ... + ... else: + Traceback (most recent call last): + SyntaxError: 'else' must match a valid statement here + + >>> elif m == n: + Traceback (most recent call last): + SyntaxError: 'elif' must match an if-statement here + + >>> else: + Traceback (most recent call last): + SyntaxError: 'else' must match a valid statement here + Uses of the star operator which should fail: A[:*b] @@ -2006,8 +2028,8 @@ def _check_error(self, code, errtext, lineno=None, offset=None, end_lineno=None, end_offset=None): """Check that compiling code raises SyntaxError with errtext. - errtest is a regular expression that must be present in the - test of the exception raised. If subclass is specified it + errtext is a regular expression that must be present in the + test of the exception raised. If subclass is specified, it is the expected subclass of SyntaxError (e.g. IndentationError). """ try: @@ -2031,6 +2053,22 @@ def _check_error(self, code, errtext, else: self.fail("compile() did not raise SyntaxError") + def _check_noerror(self, code, + errtext="compile() raised unexpected SyntaxError", + filename="", mode="exec", subclass=None): + """Check that compiling code does not raise a SyntaxError. + + errtext is the message passed to self.fail if there is + a SyntaxError. If the subclass parameter is specified, + it is the subclass of SyntaxError (e.g. IndentationError) + that the raised error is checked against. + """ + try: + compile(code, filename, mode) + except SyntaxError as err: + if (not subclass) or isinstance(err, subclass): + self.fail(errtext) + def test_expression_with_assignment(self): self._check_error( "print(end1 + end2 = ' ')", @@ -2372,6 +2410,25 @@ def test_syntax_error_on_deeply_nested_blocks(self): """ self._check_error(source, "too many statically nested blocks") + def test_syntax_error_non_matching_elif_else_statements(self): + # Check bpo-45759: 'elif' statements that doesn't match an + # if-statement or 'else' statements that doesn't match any + # valid else-able statement (e.g. 'while') + self._check_error( + "elif m == n:\n ...", + "'elif' must match an if-statement here") + self._check_error( + "else:\n ...", + "'else' must match a valid statement here") + self._check_noerror("if a == b:\n ...\nelif a == c:\n ...") + self._check_noerror("if x == y:\n ...\nelse:\n ...") + self._check_error( + "else = 123", + "invalid syntax") + self._check_error( + "elif 55 = 123", + "cannot assign to literal here") + @support.cpython_only def test_error_on_parser_stack_overflow(self): source = "-" * 100000 + "4" diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-11-10-10-40-05.bpo-45759.WJoB3D.rst b/Misc/NEWS.d/next/Core and Builtins/2021-11-10-10-40-05.bpo-45759.WJoB3D.rst new file mode 100644 index 00000000000000..6d7b0209fc04b1 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-11-10-10-40-05.bpo-45759.WJoB3D.rst @@ -0,0 +1 @@ +Improved error messages for ``elif``/``else`` statements not matching any valid statements. Patch by Jeremiah Vivian. diff --git a/Parser/parser.c b/Parser/parser.c index d27ddd3d3e3835..9006efb09a59fa 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -21,18 +21,18 @@ static KeywordToken *reserved_keywords[] = { (KeywordToken[]) {{NULL, -1}}, (KeywordToken[]) {{NULL, -1}}, (KeywordToken[]) { - {"if", 656}, - {"as", 654}, - {"in", 667}, + {"if", 658}, + {"as", 656}, + {"in", 669}, {"or", 581}, {"is", 589}, {NULL, -1}, }, (KeywordToken[]) { {"del", 613}, - {"def", 669}, - {"for", 666}, - {"try", 638}, + {"def", 671}, + {"for", 668}, + {"try", 640}, {"and", 582}, {"not", 588}, {NULL, -1}, @@ -40,9 +40,9 @@ static KeywordToken *reserved_keywords[] = { (KeywordToken[]) { {"from", 618}, {"pass", 504}, - {"with", 629}, - {"elif", 658}, - {"else", 659}, + {"with", 631}, + {"elif", 660}, + {"else", 661}, {"None", 611}, {"True", 610}, {NULL, -1}, @@ -51,9 +51,9 @@ static KeywordToken *reserved_keywords[] = { {"raise", 525}, {"yield", 580}, {"break", 508}, - {"async", 668}, - {"class", 671}, - {"while", 661}, + {"async", 670}, + {"class", 673}, + {"while", 663}, {"False", 612}, {"await", 590}, {NULL, -1}, @@ -63,12 +63,12 @@ static KeywordToken *reserved_keywords[] = { {"import", 617}, {"assert", 529}, {"global", 526}, - {"except", 651}, + {"except", 653}, {"lambda", 609}, {NULL, -1}, }, (KeywordToken[]) { - {"finally", 647}, + {"finally", 649}, {NULL, -1}, }, (KeywordToken[]) { @@ -304,311 +304,312 @@ static char *soft_keywords[] = { #define invalid_group_type 1217 #define invalid_import_type 1218 #define invalid_import_from_targets_type 1219 -#define invalid_with_stmt_type 1220 -#define invalid_with_stmt_indent_type 1221 -#define invalid_try_stmt_type 1222 -#define invalid_except_stmt_type 1223 -#define invalid_finally_stmt_type 1224 -#define invalid_except_stmt_indent_type 1225 -#define invalid_except_star_stmt_indent_type 1226 -#define invalid_match_stmt_type 1227 -#define invalid_case_block_type 1228 -#define invalid_as_pattern_type 1229 -#define invalid_class_pattern_type 1230 -#define invalid_class_argument_pattern_type 1231 -#define invalid_if_stmt_type 1232 -#define invalid_elif_stmt_type 1233 -#define invalid_else_stmt_type 1234 -#define invalid_while_stmt_type 1235 -#define invalid_for_stmt_type 1236 -#define invalid_def_raw_type 1237 -#define invalid_class_def_raw_type 1238 -#define invalid_double_starred_kvpairs_type 1239 -#define invalid_kvpair_type 1240 -#define invalid_starred_expression_type 1241 -#define invalid_replacement_field_type 1242 -#define invalid_conversion_character_type 1243 -#define _loop0_1_type 1244 -#define _loop0_2_type 1245 -#define _loop1_3_type 1246 -#define _loop0_5_type 1247 -#define _gather_4_type 1248 -#define _tmp_6_type 1249 -#define _tmp_7_type 1250 -#define _tmp_8_type 1251 -#define _tmp_9_type 1252 -#define _tmp_10_type 1253 -#define _tmp_11_type 1254 -#define _tmp_12_type 1255 -#define _tmp_13_type 1256 -#define _loop1_14_type 1257 -#define _tmp_15_type 1258 -#define _tmp_16_type 1259 -#define _tmp_17_type 1260 -#define _loop0_19_type 1261 -#define _gather_18_type 1262 -#define _loop0_21_type 1263 -#define _gather_20_type 1264 -#define _tmp_22_type 1265 -#define _tmp_23_type 1266 -#define _loop0_24_type 1267 -#define _loop1_25_type 1268 -#define _loop0_27_type 1269 -#define _gather_26_type 1270 -#define _tmp_28_type 1271 -#define _loop0_30_type 1272 -#define _gather_29_type 1273 -#define _tmp_31_type 1274 -#define _loop1_32_type 1275 -#define _tmp_33_type 1276 -#define _tmp_34_type 1277 -#define _tmp_35_type 1278 -#define _loop0_36_type 1279 -#define _loop0_37_type 1280 -#define _loop0_38_type 1281 -#define _loop1_39_type 1282 -#define _loop0_40_type 1283 -#define _loop1_41_type 1284 -#define _loop1_42_type 1285 -#define _loop1_43_type 1286 -#define _loop0_44_type 1287 -#define _loop1_45_type 1288 -#define _loop0_46_type 1289 -#define _loop1_47_type 1290 -#define _loop0_48_type 1291 -#define _loop0_49_type 1292 -#define _loop1_50_type 1293 -#define _loop0_52_type 1294 -#define _gather_51_type 1295 -#define _loop0_54_type 1296 -#define _gather_53_type 1297 -#define _loop0_56_type 1298 -#define _gather_55_type 1299 -#define _loop0_58_type 1300 -#define _gather_57_type 1301 -#define _tmp_59_type 1302 -#define _loop1_60_type 1303 -#define _loop1_61_type 1304 -#define _tmp_62_type 1305 -#define _tmp_63_type 1306 -#define _loop1_64_type 1307 -#define _loop0_66_type 1308 -#define _gather_65_type 1309 -#define _tmp_67_type 1310 -#define _tmp_68_type 1311 -#define _tmp_69_type 1312 -#define _tmp_70_type 1313 -#define _loop0_72_type 1314 -#define _gather_71_type 1315 -#define _loop0_74_type 1316 -#define _gather_73_type 1317 -#define _tmp_75_type 1318 -#define _loop0_77_type 1319 -#define _gather_76_type 1320 -#define _loop0_79_type 1321 -#define _gather_78_type 1322 -#define _loop0_81_type 1323 -#define _gather_80_type 1324 -#define _loop1_82_type 1325 -#define _loop1_83_type 1326 -#define _loop0_85_type 1327 -#define _gather_84_type 1328 -#define _loop1_86_type 1329 -#define _loop1_87_type 1330 -#define _loop1_88_type 1331 -#define _tmp_89_type 1332 -#define _loop0_91_type 1333 -#define _gather_90_type 1334 -#define _tmp_92_type 1335 -#define _tmp_93_type 1336 -#define _tmp_94_type 1337 -#define _tmp_95_type 1338 -#define _tmp_96_type 1339 -#define _tmp_97_type 1340 -#define _loop0_98_type 1341 -#define _loop0_99_type 1342 -#define _loop0_100_type 1343 -#define _loop1_101_type 1344 -#define _loop0_102_type 1345 -#define _loop1_103_type 1346 -#define _loop1_104_type 1347 -#define _loop1_105_type 1348 -#define _loop0_106_type 1349 -#define _loop1_107_type 1350 -#define _loop0_108_type 1351 -#define _loop1_109_type 1352 -#define _loop0_110_type 1353 -#define _loop1_111_type 1354 -#define _tmp_112_type 1355 -#define _loop0_113_type 1356 -#define _loop0_114_type 1357 -#define _loop1_115_type 1358 -#define _tmp_116_type 1359 -#define _loop0_118_type 1360 -#define _gather_117_type 1361 -#define _loop1_119_type 1362 -#define _loop0_120_type 1363 -#define _loop0_121_type 1364 -#define _tmp_122_type 1365 -#define _loop0_124_type 1366 -#define _gather_123_type 1367 -#define _tmp_125_type 1368 -#define _loop0_127_type 1369 -#define _gather_126_type 1370 -#define _loop0_129_type 1371 -#define _gather_128_type 1372 -#define _loop0_131_type 1373 -#define _gather_130_type 1374 -#define _loop0_133_type 1375 -#define _gather_132_type 1376 -#define _loop0_134_type 1377 -#define _loop0_136_type 1378 -#define _gather_135_type 1379 -#define _loop1_137_type 1380 -#define _tmp_138_type 1381 -#define _loop0_140_type 1382 -#define _gather_139_type 1383 -#define _loop0_142_type 1384 -#define _gather_141_type 1385 -#define _loop0_144_type 1386 -#define _gather_143_type 1387 -#define _loop0_146_type 1388 -#define _gather_145_type 1389 -#define _loop0_148_type 1390 -#define _gather_147_type 1391 -#define _tmp_149_type 1392 -#define _tmp_150_type 1393 -#define _tmp_151_type 1394 -#define _tmp_152_type 1395 -#define _tmp_153_type 1396 -#define _tmp_154_type 1397 -#define _tmp_155_type 1398 -#define _tmp_156_type 1399 -#define _tmp_157_type 1400 -#define _tmp_158_type 1401 -#define _tmp_159_type 1402 -#define _tmp_160_type 1403 -#define _loop0_161_type 1404 -#define _loop0_162_type 1405 -#define _loop0_163_type 1406 -#define _tmp_164_type 1407 -#define _tmp_165_type 1408 -#define _tmp_166_type 1409 -#define _tmp_167_type 1410 -#define _tmp_168_type 1411 -#define _loop0_169_type 1412 -#define _loop0_170_type 1413 -#define _loop0_171_type 1414 -#define _loop1_172_type 1415 -#define _tmp_173_type 1416 -#define _loop0_174_type 1417 -#define _tmp_175_type 1418 -#define _loop0_176_type 1419 -#define _loop1_177_type 1420 -#define _tmp_178_type 1421 -#define _tmp_179_type 1422 -#define _tmp_180_type 1423 -#define _loop0_181_type 1424 -#define _tmp_182_type 1425 -#define _tmp_183_type 1426 -#define _loop1_184_type 1427 -#define _tmp_185_type 1428 -#define _loop0_186_type 1429 -#define _loop0_187_type 1430 -#define _loop0_188_type 1431 -#define _loop0_190_type 1432 -#define _gather_189_type 1433 -#define _tmp_191_type 1434 -#define _loop0_192_type 1435 -#define _tmp_193_type 1436 -#define _loop0_194_type 1437 -#define _loop1_195_type 1438 -#define _loop1_196_type 1439 -#define _tmp_197_type 1440 -#define _tmp_198_type 1441 -#define _loop0_199_type 1442 -#define _tmp_200_type 1443 -#define _tmp_201_type 1444 -#define _tmp_202_type 1445 -#define _loop0_204_type 1446 -#define _gather_203_type 1447 -#define _loop0_206_type 1448 -#define _gather_205_type 1449 -#define _loop0_208_type 1450 -#define _gather_207_type 1451 -#define _loop0_210_type 1452 -#define _gather_209_type 1453 -#define _loop0_212_type 1454 -#define _gather_211_type 1455 -#define _tmp_213_type 1456 -#define _loop0_214_type 1457 -#define _loop1_215_type 1458 -#define _tmp_216_type 1459 -#define _loop0_217_type 1460 -#define _loop1_218_type 1461 -#define _tmp_219_type 1462 -#define _tmp_220_type 1463 -#define _tmp_221_type 1464 -#define _tmp_222_type 1465 -#define _tmp_223_type 1466 -#define _tmp_224_type 1467 -#define _tmp_225_type 1468 -#define _tmp_226_type 1469 -#define _tmp_227_type 1470 -#define _tmp_228_type 1471 -#define _loop0_230_type 1472 -#define _gather_229_type 1473 -#define _tmp_231_type 1474 -#define _tmp_232_type 1475 -#define _tmp_233_type 1476 -#define _tmp_234_type 1477 -#define _tmp_235_type 1478 -#define _tmp_236_type 1479 -#define _tmp_237_type 1480 -#define _tmp_238_type 1481 -#define _tmp_239_type 1482 -#define _tmp_240_type 1483 -#define _tmp_241_type 1484 -#define _tmp_242_type 1485 -#define _tmp_243_type 1486 -#define _loop0_244_type 1487 -#define _tmp_245_type 1488 -#define _tmp_246_type 1489 -#define _tmp_247_type 1490 -#define _tmp_248_type 1491 -#define _tmp_249_type 1492 -#define _tmp_250_type 1493 -#define _tmp_251_type 1494 -#define _tmp_252_type 1495 -#define _tmp_253_type 1496 -#define _tmp_254_type 1497 -#define _tmp_255_type 1498 -#define _tmp_256_type 1499 -#define _tmp_257_type 1500 -#define _tmp_258_type 1501 -#define _tmp_259_type 1502 -#define _tmp_260_type 1503 -#define _tmp_261_type 1504 -#define _tmp_262_type 1505 -#define _tmp_263_type 1506 -#define _tmp_264_type 1507 -#define _tmp_265_type 1508 -#define _tmp_266_type 1509 -#define _tmp_267_type 1510 -#define _tmp_268_type 1511 -#define _tmp_269_type 1512 -#define _tmp_270_type 1513 -#define _tmp_271_type 1514 -#define _tmp_272_type 1515 -#define _tmp_273_type 1516 -#define _loop0_275_type 1517 -#define _gather_274_type 1518 -#define _tmp_276_type 1519 -#define _tmp_277_type 1520 -#define _tmp_278_type 1521 -#define _tmp_279_type 1522 -#define _tmp_280_type 1523 -#define _tmp_281_type 1524 +#define invalid_compound_stmt_type 1220 +#define invalid_with_stmt_type 1221 +#define invalid_with_stmt_indent_type 1222 +#define invalid_try_stmt_type 1223 +#define invalid_except_stmt_type 1224 +#define invalid_finally_stmt_type 1225 +#define invalid_except_stmt_indent_type 1226 +#define invalid_except_star_stmt_indent_type 1227 +#define invalid_match_stmt_type 1228 +#define invalid_case_block_type 1229 +#define invalid_as_pattern_type 1230 +#define invalid_class_pattern_type 1231 +#define invalid_class_argument_pattern_type 1232 +#define invalid_if_stmt_type 1233 +#define invalid_elif_stmt_type 1234 +#define invalid_else_stmt_type 1235 +#define invalid_while_stmt_type 1236 +#define invalid_for_stmt_type 1237 +#define invalid_def_raw_type 1238 +#define invalid_class_def_raw_type 1239 +#define invalid_double_starred_kvpairs_type 1240 +#define invalid_kvpair_type 1241 +#define invalid_starred_expression_type 1242 +#define invalid_replacement_field_type 1243 +#define invalid_conversion_character_type 1244 +#define _loop0_1_type 1245 +#define _loop0_2_type 1246 +#define _loop1_3_type 1247 +#define _loop0_5_type 1248 +#define _gather_4_type 1249 +#define _tmp_6_type 1250 +#define _tmp_7_type 1251 +#define _tmp_8_type 1252 +#define _tmp_9_type 1253 +#define _tmp_10_type 1254 +#define _tmp_11_type 1255 +#define _tmp_12_type 1256 +#define _tmp_13_type 1257 +#define _loop1_14_type 1258 +#define _tmp_15_type 1259 +#define _tmp_16_type 1260 +#define _tmp_17_type 1261 +#define _loop0_19_type 1262 +#define _gather_18_type 1263 +#define _loop0_21_type 1264 +#define _gather_20_type 1265 +#define _tmp_22_type 1266 +#define _tmp_23_type 1267 +#define _loop0_24_type 1268 +#define _loop1_25_type 1269 +#define _loop0_27_type 1270 +#define _gather_26_type 1271 +#define _tmp_28_type 1272 +#define _loop0_30_type 1273 +#define _gather_29_type 1274 +#define _tmp_31_type 1275 +#define _loop1_32_type 1276 +#define _tmp_33_type 1277 +#define _tmp_34_type 1278 +#define _tmp_35_type 1279 +#define _loop0_36_type 1280 +#define _loop0_37_type 1281 +#define _loop0_38_type 1282 +#define _loop1_39_type 1283 +#define _loop0_40_type 1284 +#define _loop1_41_type 1285 +#define _loop1_42_type 1286 +#define _loop1_43_type 1287 +#define _loop0_44_type 1288 +#define _loop1_45_type 1289 +#define _loop0_46_type 1290 +#define _loop1_47_type 1291 +#define _loop0_48_type 1292 +#define _loop0_49_type 1293 +#define _loop1_50_type 1294 +#define _loop0_52_type 1295 +#define _gather_51_type 1296 +#define _loop0_54_type 1297 +#define _gather_53_type 1298 +#define _loop0_56_type 1299 +#define _gather_55_type 1300 +#define _loop0_58_type 1301 +#define _gather_57_type 1302 +#define _tmp_59_type 1303 +#define _loop1_60_type 1304 +#define _loop1_61_type 1305 +#define _tmp_62_type 1306 +#define _tmp_63_type 1307 +#define _loop1_64_type 1308 +#define _loop0_66_type 1309 +#define _gather_65_type 1310 +#define _tmp_67_type 1311 +#define _tmp_68_type 1312 +#define _tmp_69_type 1313 +#define _tmp_70_type 1314 +#define _loop0_72_type 1315 +#define _gather_71_type 1316 +#define _loop0_74_type 1317 +#define _gather_73_type 1318 +#define _tmp_75_type 1319 +#define _loop0_77_type 1320 +#define _gather_76_type 1321 +#define _loop0_79_type 1322 +#define _gather_78_type 1323 +#define _loop0_81_type 1324 +#define _gather_80_type 1325 +#define _loop1_82_type 1326 +#define _loop1_83_type 1327 +#define _loop0_85_type 1328 +#define _gather_84_type 1329 +#define _loop1_86_type 1330 +#define _loop1_87_type 1331 +#define _loop1_88_type 1332 +#define _tmp_89_type 1333 +#define _loop0_91_type 1334 +#define _gather_90_type 1335 +#define _tmp_92_type 1336 +#define _tmp_93_type 1337 +#define _tmp_94_type 1338 +#define _tmp_95_type 1339 +#define _tmp_96_type 1340 +#define _tmp_97_type 1341 +#define _loop0_98_type 1342 +#define _loop0_99_type 1343 +#define _loop0_100_type 1344 +#define _loop1_101_type 1345 +#define _loop0_102_type 1346 +#define _loop1_103_type 1347 +#define _loop1_104_type 1348 +#define _loop1_105_type 1349 +#define _loop0_106_type 1350 +#define _loop1_107_type 1351 +#define _loop0_108_type 1352 +#define _loop1_109_type 1353 +#define _loop0_110_type 1354 +#define _loop1_111_type 1355 +#define _tmp_112_type 1356 +#define _loop0_113_type 1357 +#define _loop0_114_type 1358 +#define _loop1_115_type 1359 +#define _tmp_116_type 1360 +#define _loop0_118_type 1361 +#define _gather_117_type 1362 +#define _loop1_119_type 1363 +#define _loop0_120_type 1364 +#define _loop0_121_type 1365 +#define _tmp_122_type 1366 +#define _loop0_124_type 1367 +#define _gather_123_type 1368 +#define _tmp_125_type 1369 +#define _loop0_127_type 1370 +#define _gather_126_type 1371 +#define _loop0_129_type 1372 +#define _gather_128_type 1373 +#define _loop0_131_type 1374 +#define _gather_130_type 1375 +#define _loop0_133_type 1376 +#define _gather_132_type 1377 +#define _loop0_134_type 1378 +#define _loop0_136_type 1379 +#define _gather_135_type 1380 +#define _loop1_137_type 1381 +#define _tmp_138_type 1382 +#define _loop0_140_type 1383 +#define _gather_139_type 1384 +#define _loop0_142_type 1385 +#define _gather_141_type 1386 +#define _loop0_144_type 1387 +#define _gather_143_type 1388 +#define _loop0_146_type 1389 +#define _gather_145_type 1390 +#define _loop0_148_type 1391 +#define _gather_147_type 1392 +#define _tmp_149_type 1393 +#define _tmp_150_type 1394 +#define _tmp_151_type 1395 +#define _tmp_152_type 1396 +#define _tmp_153_type 1397 +#define _tmp_154_type 1398 +#define _tmp_155_type 1399 +#define _tmp_156_type 1400 +#define _tmp_157_type 1401 +#define _tmp_158_type 1402 +#define _tmp_159_type 1403 +#define _tmp_160_type 1404 +#define _loop0_161_type 1405 +#define _loop0_162_type 1406 +#define _loop0_163_type 1407 +#define _tmp_164_type 1408 +#define _tmp_165_type 1409 +#define _tmp_166_type 1410 +#define _tmp_167_type 1411 +#define _tmp_168_type 1412 +#define _loop0_169_type 1413 +#define _loop0_170_type 1414 +#define _loop0_171_type 1415 +#define _loop1_172_type 1416 +#define _tmp_173_type 1417 +#define _loop0_174_type 1418 +#define _tmp_175_type 1419 +#define _loop0_176_type 1420 +#define _loop1_177_type 1421 +#define _tmp_178_type 1422 +#define _tmp_179_type 1423 +#define _tmp_180_type 1424 +#define _loop0_181_type 1425 +#define _tmp_182_type 1426 +#define _tmp_183_type 1427 +#define _loop1_184_type 1428 +#define _tmp_185_type 1429 +#define _loop0_186_type 1430 +#define _loop0_187_type 1431 +#define _loop0_188_type 1432 +#define _loop0_190_type 1433 +#define _gather_189_type 1434 +#define _tmp_191_type 1435 +#define _loop0_192_type 1436 +#define _tmp_193_type 1437 +#define _loop0_194_type 1438 +#define _loop1_195_type 1439 +#define _loop1_196_type 1440 +#define _tmp_197_type 1441 +#define _tmp_198_type 1442 +#define _loop0_199_type 1443 +#define _tmp_200_type 1444 +#define _tmp_201_type 1445 +#define _tmp_202_type 1446 +#define _loop0_204_type 1447 +#define _gather_203_type 1448 +#define _loop0_206_type 1449 +#define _gather_205_type 1450 +#define _loop0_208_type 1451 +#define _gather_207_type 1452 +#define _loop0_210_type 1453 +#define _gather_209_type 1454 +#define _loop0_212_type 1455 +#define _gather_211_type 1456 +#define _tmp_213_type 1457 +#define _loop0_214_type 1458 +#define _loop1_215_type 1459 +#define _tmp_216_type 1460 +#define _loop0_217_type 1461 +#define _loop1_218_type 1462 +#define _tmp_219_type 1463 +#define _tmp_220_type 1464 +#define _tmp_221_type 1465 +#define _tmp_222_type 1466 +#define _tmp_223_type 1467 +#define _tmp_224_type 1468 +#define _tmp_225_type 1469 +#define _tmp_226_type 1470 +#define _tmp_227_type 1471 +#define _tmp_228_type 1472 +#define _loop0_230_type 1473 +#define _gather_229_type 1474 +#define _tmp_231_type 1475 +#define _tmp_232_type 1476 +#define _tmp_233_type 1477 +#define _tmp_234_type 1478 +#define _tmp_235_type 1479 +#define _tmp_236_type 1480 +#define _tmp_237_type 1481 +#define _tmp_238_type 1482 +#define _tmp_239_type 1483 +#define _tmp_240_type 1484 +#define _tmp_241_type 1485 +#define _tmp_242_type 1486 +#define _tmp_243_type 1487 +#define _loop0_244_type 1488 +#define _tmp_245_type 1489 +#define _tmp_246_type 1490 +#define _tmp_247_type 1491 +#define _tmp_248_type 1492 +#define _tmp_249_type 1493 +#define _tmp_250_type 1494 +#define _tmp_251_type 1495 +#define _tmp_252_type 1496 +#define _tmp_253_type 1497 +#define _tmp_254_type 1498 +#define _tmp_255_type 1499 +#define _tmp_256_type 1500 +#define _tmp_257_type 1501 +#define _tmp_258_type 1502 +#define _tmp_259_type 1503 +#define _tmp_260_type 1504 +#define _tmp_261_type 1505 +#define _tmp_262_type 1506 +#define _tmp_263_type 1507 +#define _tmp_264_type 1508 +#define _tmp_265_type 1509 +#define _tmp_266_type 1510 +#define _tmp_267_type 1511 +#define _tmp_268_type 1512 +#define _tmp_269_type 1513 +#define _tmp_270_type 1514 +#define _tmp_271_type 1515 +#define _tmp_272_type 1516 +#define _tmp_273_type 1517 +#define _loop0_275_type 1518 +#define _gather_274_type 1519 +#define _tmp_276_type 1520 +#define _tmp_277_type 1521 +#define _tmp_278_type 1522 +#define _tmp_279_type 1523 +#define _tmp_280_type 1524 +#define _tmp_281_type 1525 static mod_ty file_rule(Parser *p); static mod_ty interactive_rule(Parser *p); @@ -830,6 +831,7 @@ static void *invalid_for_target_rule(Parser *p); static void *invalid_group_rule(Parser *p); static void *invalid_import_rule(Parser *p); static void *invalid_import_from_targets_rule(Parser *p); +static void *invalid_compound_stmt_rule(Parser *p); static void *invalid_with_stmt_rule(Parser *p); static void *invalid_with_stmt_indent_rule(Parser *p); static void *invalid_try_stmt_rule(Parser *p); @@ -2044,6 +2046,7 @@ simple_stmt_rule(Parser *p) } // compound_stmt: +// | invalid_compound_stmt // | &('def' | '@' | 'async') function_def // | &'if' if_stmt // | &('class' | '@') class_def @@ -2064,6 +2067,25 @@ compound_stmt_rule(Parser *p) } stmt_ty _res = NULL; int _mark = p->mark; + if (p->call_invalid_rules) { // invalid_compound_stmt + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_compound_stmt")); + void *invalid_compound_stmt_var; + if ( + (invalid_compound_stmt_var = invalid_compound_stmt_rule(p)) // invalid_compound_stmt + ) + { + D(fprintf(stderr, "%*c+ compound_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_compound_stmt")); + _res = invalid_compound_stmt_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s compound_stmt[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_compound_stmt")); + } { // &('def' | '@' | 'async') function_def if (p->error_indicator) { p->level--; @@ -2093,7 +2115,7 @@ compound_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'if' if_stmt")); stmt_ty if_stmt_var; if ( - _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 656) // token='if' + _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 658) // token='if' && (if_stmt_var = if_stmt_rule(p)) // if_stmt ) @@ -2177,7 +2199,7 @@ compound_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'try' try_stmt")); stmt_ty try_stmt_var; if ( - _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 638) // token='try' + _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 640) // token='try' && (try_stmt_var = try_stmt_rule(p)) // try_stmt ) @@ -2198,7 +2220,7 @@ compound_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'while' while_stmt")); stmt_ty while_stmt_var; if ( - _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 661) // token='while' + _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 663) // token='while' && (while_stmt_var = while_stmt_rule(p)) // while_stmt ) @@ -4338,7 +4360,7 @@ class_def_raw_rule(Parser *p) asdl_stmt_seq* c; void *t; if ( - (_keyword = _PyPegen_expect_token(p, 671)) // token='class' + (_keyword = _PyPegen_expect_token(p, 673)) // token='class' && (a = _PyPegen_name_token(p)) // NAME && @@ -4505,7 +4527,7 @@ function_def_raw_rule(Parser *p) void *t; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 669)) // token='def' + (_keyword = _PyPegen_expect_token(p, 671)) // token='def' && (n = _PyPegen_name_token(p)) // NAME && @@ -4566,9 +4588,9 @@ function_def_raw_rule(Parser *p) void *t; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 668)) // token='async' + (_keyword = _PyPegen_expect_token(p, 670)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 669)) // token='def' + (_keyword_1 = _PyPegen_expect_token(p, 671)) // token='def' && (n = _PyPegen_name_token(p)) // NAME && @@ -5906,7 +5928,7 @@ if_stmt_rule(Parser *p) asdl_stmt_seq* b; stmt_ty c; if ( - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (a = named_expression_rule(p)) // named_expression && @@ -5951,7 +5973,7 @@ if_stmt_rule(Parser *p) asdl_stmt_seq* b; void *c; if ( - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (a = named_expression_rule(p)) // named_expression && @@ -6046,7 +6068,7 @@ elif_stmt_rule(Parser *p) asdl_stmt_seq* b; stmt_ty c; if ( - (_keyword = _PyPegen_expect_token(p, 658)) // token='elif' + (_keyword = _PyPegen_expect_token(p, 660)) // token='elif' && (a = named_expression_rule(p)) // named_expression && @@ -6091,7 +6113,7 @@ elif_stmt_rule(Parser *p) asdl_stmt_seq* b; void *c; if ( - (_keyword = _PyPegen_expect_token(p, 658)) // token='elif' + (_keyword = _PyPegen_expect_token(p, 660)) // token='elif' && (a = named_expression_rule(p)) // named_expression && @@ -6172,7 +6194,7 @@ else_block_rule(Parser *p) Token * _literal; asdl_stmt_seq* b; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='else' + (_keyword = _PyPegen_expect_token(p, 661)) // token='else' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -6251,7 +6273,7 @@ while_stmt_rule(Parser *p) asdl_stmt_seq* b; void *c; if ( - (_keyword = _PyPegen_expect_token(p, 661)) // token='while' + (_keyword = _PyPegen_expect_token(p, 663)) // token='while' && (a = named_expression_rule(p)) // named_expression && @@ -6351,11 +6373,11 @@ for_stmt_rule(Parser *p) expr_ty t; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 666)) // token='for' + (_keyword = _PyPegen_expect_token(p, 668)) // token='for' && (t = star_targets_rule(p)) // star_targets && - (_keyword_1 = _PyPegen_expect_token(p, 667)) // token='in' + (_keyword_1 = _PyPegen_expect_token(p, 669)) // token='in' && (_cut_var = 1) && @@ -6413,13 +6435,13 @@ for_stmt_rule(Parser *p) expr_ty t; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 668)) // token='async' + (_keyword = _PyPegen_expect_token(p, 670)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 666)) // token='for' + (_keyword_1 = _PyPegen_expect_token(p, 668)) // token='for' && (t = star_targets_rule(p)) // star_targets && - (_keyword_2 = _PyPegen_expect_token(p, 667)) // token='in' + (_keyword_2 = _PyPegen_expect_token(p, 669)) // token='in' && (_cut_var = 1) && @@ -6548,7 +6570,7 @@ with_stmt_rule(Parser *p) asdl_stmt_seq* b; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 629)) // token='with' + (_keyword = _PyPegen_expect_token(p, 631)) // token='with' && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && @@ -6599,7 +6621,7 @@ with_stmt_rule(Parser *p) asdl_stmt_seq* b; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 629)) // token='with' + (_keyword = _PyPegen_expect_token(p, 631)) // token='with' && (a = (asdl_withitem_seq*)_gather_53_rule(p)) // ','.with_item+ && @@ -6648,9 +6670,9 @@ with_stmt_rule(Parser *p) asdl_withitem_seq* a; asdl_stmt_seq* b; if ( - (_keyword = _PyPegen_expect_token(p, 668)) // token='async' + (_keyword = _PyPegen_expect_token(p, 670)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 629)) // token='with' + (_keyword_1 = _PyPegen_expect_token(p, 631)) // token='with' && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && @@ -6700,9 +6722,9 @@ with_stmt_rule(Parser *p) asdl_stmt_seq* b; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 668)) // token='async' + (_keyword = _PyPegen_expect_token(p, 670)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 629)) // token='with' + (_keyword_1 = _PyPegen_expect_token(p, 631)) // token='with' && (a = (asdl_withitem_seq*)_gather_57_rule(p)) // ','.with_item+ && @@ -6788,7 +6810,7 @@ with_item_rule(Parser *p) if ( (e = expression_rule(p)) // expression && - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (t = star_target_rule(p)) // star_target && @@ -6913,7 +6935,7 @@ try_stmt_rule(Parser *p) asdl_stmt_seq* b; asdl_stmt_seq* f; if ( - (_keyword = _PyPegen_expect_token(p, 638)) // token='try' + (_keyword = _PyPegen_expect_token(p, 640)) // token='try' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -6957,7 +6979,7 @@ try_stmt_rule(Parser *p) asdl_excepthandler_seq* ex; void *f; if ( - (_keyword = _PyPegen_expect_token(p, 638)) // token='try' + (_keyword = _PyPegen_expect_token(p, 640)) // token='try' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -7005,7 +7027,7 @@ try_stmt_rule(Parser *p) asdl_excepthandler_seq* ex; void *f; if ( - (_keyword = _PyPegen_expect_token(p, 638)) // token='try' + (_keyword = _PyPegen_expect_token(p, 640)) // token='try' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -7103,7 +7125,7 @@ except_block_rule(Parser *p) expr_ty e; void *t; if ( - (_keyword = _PyPegen_expect_token(p, 651)) // token='except' + (_keyword = _PyPegen_expect_token(p, 653)) // token='except' && (e = expression_rule(p)) // expression && @@ -7146,7 +7168,7 @@ except_block_rule(Parser *p) Token * _literal; asdl_stmt_seq* b; if ( - (_keyword = _PyPegen_expect_token(p, 651)) // token='except' + (_keyword = _PyPegen_expect_token(p, 653)) // token='except' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -7257,7 +7279,7 @@ except_star_block_rule(Parser *p) expr_ty e; void *t; if ( - (_keyword = _PyPegen_expect_token(p, 651)) // token='except' + (_keyword = _PyPegen_expect_token(p, 653)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && @@ -7359,7 +7381,7 @@ finally_block_rule(Parser *p) Token * _literal; asdl_stmt_seq* a; if ( - (_keyword = _PyPegen_expect_token(p, 647)) // token='finally' + (_keyword = _PyPegen_expect_token(p, 649)) // token='finally' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -7667,7 +7689,7 @@ guard_rule(Parser *p) Token * _keyword; expr_ty guard; if ( - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (guard = named_expression_rule(p)) // named_expression ) @@ -7862,7 +7884,7 @@ as_pattern_rule(Parser *p) if ( (pattern = or_pattern_rule(p)) // or_pattern && - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (target = pattern_capture_target_rule(p)) // pattern_capture_target ) @@ -11085,11 +11107,11 @@ expression_rule(Parser *p) if ( (a = disjunction_rule(p)) // disjunction && - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (b = disjunction_rule(p)) // disjunction && - (_keyword_1 = _PyPegen_expect_token(p, 659)) // token='else' + (_keyword_1 = _PyPegen_expect_token(p, 661)) // token='else' && (c = expression_rule(p)) // expression ) @@ -12627,7 +12649,7 @@ notin_bitwise_or_rule(Parser *p) if ( (_keyword = _PyPegen_expect_token(p, 588)) // token='not' && - (_keyword_1 = _PyPegen_expect_token(p, 667)) // token='in' + (_keyword_1 = _PyPegen_expect_token(p, 669)) // token='in' && (a = bitwise_or_rule(p)) // bitwise_or ) @@ -12673,7 +12695,7 @@ in_bitwise_or_rule(Parser *p) Token * _keyword; expr_ty a; if ( - (_keyword = _PyPegen_expect_token(p, 667)) // token='in' + (_keyword = _PyPegen_expect_token(p, 669)) // token='in' && (a = bitwise_or_rule(p)) // bitwise_or ) @@ -16853,13 +16875,13 @@ for_if_clause_rule(Parser *p) expr_ty b; asdl_expr_seq* c; if ( - (_keyword = _PyPegen_expect_token(p, 668)) // token='async' + (_keyword = _PyPegen_expect_token(p, 670)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 666)) // token='for' + (_keyword_1 = _PyPegen_expect_token(p, 668)) // token='for' && (a = star_targets_rule(p)) // star_targets && - (_keyword_2 = _PyPegen_expect_token(p, 667)) // token='in' + (_keyword_2 = _PyPegen_expect_token(p, 669)) // token='in' && (_cut_var = 1) && @@ -16898,11 +16920,11 @@ for_if_clause_rule(Parser *p) expr_ty b; asdl_expr_seq* c; if ( - (_keyword = _PyPegen_expect_token(p, 666)) // token='for' + (_keyword = _PyPegen_expect_token(p, 668)) // token='for' && (a = star_targets_rule(p)) // star_targets && - (_keyword_1 = _PyPegen_expect_token(p, 667)) // token='in' + (_keyword_1 = _PyPegen_expect_token(p, 669)) // token='in' && (_cut_var = 1) && @@ -20162,11 +20184,11 @@ expression_without_invalid_rule(Parser *p) if ( (a = disjunction_rule(p)) // disjunction && - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (b = disjunction_rule(p)) // disjunction && - (_keyword_1 = _PyPegen_expect_token(p, 659)) // token='else' + (_keyword_1 = _PyPegen_expect_token(p, 661)) // token='else' && (c = expression_rule(p)) // expression ) @@ -20347,7 +20369,7 @@ invalid_expression_rule(Parser *p) if ( (a = disjunction_rule(p)) // disjunction && - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (b = disjunction_rule(p)) // disjunction && @@ -22285,7 +22307,7 @@ invalid_with_item_rule(Parser *p) if ( (expression_var = expression_rule(p)) // expression && - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (a = expression_rule(p)) // expression && @@ -22335,9 +22357,9 @@ invalid_for_target_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings expr_ty a; if ( - (_opt_var = _PyPegen_expect_token(p, 668), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 670), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 666)) // token='for' + (_keyword = _PyPegen_expect_token(p, 668)) // token='for' && (a = star_expressions_rule(p)) // star_expressions ) @@ -22544,6 +22566,82 @@ invalid_import_from_targets_rule(Parser *p) return _res; } +// invalid_compound_stmt: 'elif' named_expression ':' | 'else' ':' +static void * +invalid_compound_stmt_rule(Parser *p) +{ + if (p->level++ == MAXSTACK) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // 'elif' named_expression ':' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'elif' named_expression ':'")); + Token * _literal; + Token * a; + expr_ty named_expression_var; + if ( + (a = _PyPegen_expect_token(p, 660)) // token='elif' + && + (named_expression_var = named_expression_rule(p)) // named_expression + && + (_literal = _PyPegen_expect_token(p, 11)) // token=':' + ) + { + D(fprintf(stderr, "%*c+ invalid_compound_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'elif' named_expression ':'")); + _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( a , "'elif' must match an if-statement here" ); + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_compound_stmt[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'elif' named_expression ':'")); + } + { // 'else' ':' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'else' ':'")); + Token * _literal; + Token * a; + if ( + (a = _PyPegen_expect_token(p, 661)) // token='else' + && + (_literal = _PyPegen_expect_token(p, 11)) // token=':' + ) + { + D(fprintf(stderr, "%*c+ invalid_compound_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'else' ':'")); + _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( a , "'else' must match a valid statement here" ); + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_compound_stmt[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'else' ':'")); + } + _res = NULL; + done: + p->level--; + return _res; +} + // invalid_with_stmt: // | 'async'? 'with' ','.(expression ['as' star_target])+ NEWLINE // | 'async'? 'with' '(' ','.(expressions ['as' star_target])+ ','? ')' NEWLINE @@ -22571,9 +22669,9 @@ invalid_with_stmt_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 668), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 670), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 629)) // token='with' + (_keyword = _PyPegen_expect_token(p, 631)) // token='with' && (_gather_205_var = _gather_205_rule(p)) // ','.(expression ['as' star_target])+ && @@ -22609,9 +22707,9 @@ invalid_with_stmt_rule(Parser *p) UNUSED(_opt_var_1); // Silence compiler warnings Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 668), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 670), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 629)) // token='with' + (_keyword = _PyPegen_expect_token(p, 631)) // token='with' && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && @@ -22671,9 +22769,9 @@ invalid_with_stmt_indent_rule(Parser *p) Token * a; Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 668), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 670), !p->error_indicator) // 'async'? && - (a = _PyPegen_expect_token(p, 629)) // token='with' + (a = _PyPegen_expect_token(p, 631)) // token='with' && (_gather_209_var = _gather_209_rule(p)) // ','.(expression ['as' star_target])+ && @@ -22714,9 +22812,9 @@ invalid_with_stmt_indent_rule(Parser *p) Token * a; Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 668), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 670), !p->error_indicator) // 'async'? && - (a = _PyPegen_expect_token(p, 629)) // token='with' + (a = _PyPegen_expect_token(p, 631)) // token='with' && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && @@ -22779,7 +22877,7 @@ invalid_try_stmt_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 638)) // token='try' + (a = _PyPegen_expect_token(p, 640)) // token='try' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -22811,7 +22909,7 @@ invalid_try_stmt_rule(Parser *p) Token * _literal; asdl_stmt_seq* block_var; if ( - (_keyword = _PyPegen_expect_token(p, 638)) // token='try' + (_keyword = _PyPegen_expect_token(p, 640)) // token='try' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -22850,7 +22948,7 @@ invalid_try_stmt_rule(Parser *p) Token * b; expr_ty expression_var; if ( - (_keyword = _PyPegen_expect_token(p, 638)) // token='try' + (_keyword = _PyPegen_expect_token(p, 640)) // token='try' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -22858,7 +22956,7 @@ invalid_try_stmt_rule(Parser *p) && (_loop1_215_var = _loop1_215_rule(p)) // except_block+ && - (a = _PyPegen_expect_token(p, 651)) // token='except' + (a = _PyPegen_expect_token(p, 653)) // token='except' && (b = _PyPegen_expect_token(p, 16)) // token='*' && @@ -22897,7 +22995,7 @@ invalid_try_stmt_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings Token * a; if ( - (_keyword = _PyPegen_expect_token(p, 638)) // token='try' + (_keyword = _PyPegen_expect_token(p, 640)) // token='try' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -22905,7 +23003,7 @@ invalid_try_stmt_rule(Parser *p) && (_loop1_218_var = _loop1_218_rule(p)) // except_star_block+ && - (a = _PyPegen_expect_token(p, 651)) // token='except' + (a = _PyPegen_expect_token(p, 653)) // token='except' && (_opt_var = _tmp_219_rule(p), !p->error_indicator) // [expression ['as' NAME]] && @@ -22964,7 +23062,7 @@ invalid_except_stmt_rule(Parser *p) expr_ty a; expr_ty expressions_var; if ( - (_keyword = _PyPegen_expect_token(p, 651)) // token='except' + (_keyword = _PyPegen_expect_token(p, 653)) // token='except' && (_opt_var = _PyPegen_expect_token(p, 16), !p->error_indicator) // '*'? && @@ -23006,7 +23104,7 @@ invalid_except_stmt_rule(Parser *p) expr_ty expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 651)) // token='except' + (a = _PyPegen_expect_token(p, 653)) // token='except' && (_opt_var = _PyPegen_expect_token(p, 16), !p->error_indicator) // '*'? && @@ -23039,7 +23137,7 @@ invalid_except_stmt_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 651)) // token='except' + (a = _PyPegen_expect_token(p, 653)) // token='except' && (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) @@ -23067,7 +23165,7 @@ invalid_except_stmt_rule(Parser *p) void *_tmp_222_var; Token * a; if ( - (a = _PyPegen_expect_token(p, 651)) // token='except' + (a = _PyPegen_expect_token(p, 653)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && @@ -23116,7 +23214,7 @@ invalid_finally_stmt_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 647)) // token='finally' + (a = _PyPegen_expect_token(p, 649)) // token='finally' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -23172,7 +23270,7 @@ invalid_except_stmt_indent_rule(Parser *p) expr_ty expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 651)) // token='except' + (a = _PyPegen_expect_token(p, 653)) // token='except' && (expression_var = expression_rule(p)) // expression && @@ -23208,7 +23306,7 @@ invalid_except_stmt_indent_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 651)) // token='except' + (a = _PyPegen_expect_token(p, 653)) // token='except' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -23264,7 +23362,7 @@ invalid_except_star_stmt_indent_rule(Parser *p) expr_ty expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 651)) // token='except' + (a = _PyPegen_expect_token(p, 653)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && @@ -23503,7 +23601,7 @@ invalid_as_pattern_rule(Parser *p) if ( (or_pattern_var = or_pattern_rule(p)) // or_pattern && - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (a = _PyPegen_expect_soft_keyword(p, "_")) // soft_keyword='"_"' ) @@ -23533,7 +23631,7 @@ invalid_as_pattern_rule(Parser *p) if ( (or_pattern_var = or_pattern_rule(p)) // or_pattern && - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && _PyPegen_lookahead_with_name(0, _PyPegen_name_token, p) && @@ -23687,7 +23785,7 @@ invalid_if_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -23718,7 +23816,7 @@ invalid_if_stmt_rule(Parser *p) expr_ty a_1; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 656)) // token='if' + (a = _PyPegen_expect_token(p, 658)) // token='if' && (a_1 = named_expression_rule(p)) // named_expression && @@ -23773,7 +23871,7 @@ invalid_elif_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (_keyword = _PyPegen_expect_token(p, 658)) // token='elif' + (_keyword = _PyPegen_expect_token(p, 660)) // token='elif' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -23804,7 +23902,7 @@ invalid_elif_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 658)) // token='elif' + (a = _PyPegen_expect_token(p, 660)) // token='elif' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -23857,7 +23955,7 @@ invalid_else_stmt_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 659)) // token='else' + (a = _PyPegen_expect_token(p, 661)) // token='else' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -23910,7 +24008,7 @@ invalid_while_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (_keyword = _PyPegen_expect_token(p, 661)) // token='while' + (_keyword = _PyPegen_expect_token(p, 663)) // token='while' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -23941,7 +24039,7 @@ invalid_while_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 661)) // token='while' + (a = _PyPegen_expect_token(p, 663)) // token='while' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -24000,13 +24098,13 @@ invalid_for_stmt_rule(Parser *p) expr_ty star_expressions_var; expr_ty star_targets_var; if ( - (_opt_var = _PyPegen_expect_token(p, 668), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 670), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 666)) // token='for' + (_keyword = _PyPegen_expect_token(p, 668)) // token='for' && (star_targets_var = star_targets_rule(p)) // star_targets && - (_keyword_1 = _PyPegen_expect_token(p, 667)) // token='in' + (_keyword_1 = _PyPegen_expect_token(p, 669)) // token='in' && (star_expressions_var = star_expressions_rule(p)) // star_expressions && @@ -24041,13 +24139,13 @@ invalid_for_stmt_rule(Parser *p) expr_ty star_expressions_var; expr_ty star_targets_var; if ( - (_opt_var = _PyPegen_expect_token(p, 668), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 670), !p->error_indicator) // 'async'? && - (a = _PyPegen_expect_token(p, 666)) // token='for' + (a = _PyPegen_expect_token(p, 668)) // token='for' && (star_targets_var = star_targets_rule(p)) // star_targets && - (_keyword = _PyPegen_expect_token(p, 667)) // token='in' + (_keyword = _PyPegen_expect_token(p, 669)) // token='in' && (star_expressions_var = star_expressions_rule(p)) // star_expressions && @@ -24112,9 +24210,9 @@ invalid_def_raw_rule(Parser *p) expr_ty name_var; Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 668), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 670), !p->error_indicator) // 'async'? && - (a = _PyPegen_expect_token(p, 669)) // token='def' + (a = _PyPegen_expect_token(p, 671)) // token='def' && (name_var = _PyPegen_name_token(p)) // NAME && @@ -24183,7 +24281,7 @@ invalid_class_def_raw_rule(Parser *p) expr_ty name_var; Token * newline_var; if ( - (_keyword = _PyPegen_expect_token(p, 671)) // token='class' + (_keyword = _PyPegen_expect_token(p, 673)) // token='class' && (name_var = _PyPegen_name_token(p)) // NAME && @@ -24222,7 +24320,7 @@ invalid_class_def_raw_rule(Parser *p) expr_ty name_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 671)) // token='class' + (a = _PyPegen_expect_token(p, 673)) // token='class' && (name_var = _PyPegen_name_token(p)) // NAME && @@ -25370,7 +25468,7 @@ _tmp_7_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_7[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'def'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 669)) // token='def' + (_keyword = _PyPegen_expect_token(p, 671)) // token='def' ) { D(fprintf(stderr, "%*c+ _tmp_7[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'def'")); @@ -25408,7 +25506,7 @@ _tmp_7_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_7[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 668)) // token='async' + (_keyword = _PyPegen_expect_token(p, 670)) // token='async' ) { D(fprintf(stderr, "%*c+ _tmp_7[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'")); @@ -25446,7 +25544,7 @@ _tmp_8_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_8[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'class'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 671)) // token='class' + (_keyword = _PyPegen_expect_token(p, 673)) // token='class' ) { D(fprintf(stderr, "%*c+ _tmp_8[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'class'")); @@ -25503,7 +25601,7 @@ _tmp_9_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_9[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'with'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 629)) // token='with' + (_keyword = _PyPegen_expect_token(p, 631)) // token='with' ) { D(fprintf(stderr, "%*c+ _tmp_9[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'with'")); @@ -25522,7 +25620,7 @@ _tmp_9_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_9[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 668)) // token='async' + (_keyword = _PyPegen_expect_token(p, 670)) // token='async' ) { D(fprintf(stderr, "%*c+ _tmp_9[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'")); @@ -25560,7 +25658,7 @@ _tmp_10_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_10[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'for'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 666)) // token='for' + (_keyword = _PyPegen_expect_token(p, 668)) // token='for' ) { D(fprintf(stderr, "%*c+ _tmp_10[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'for'")); @@ -25579,7 +25677,7 @@ _tmp_10_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_10[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 668)) // token='async' + (_keyword = _PyPegen_expect_token(p, 670)) // token='async' ) { D(fprintf(stderr, "%*c+ _tmp_10[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'")); @@ -26603,7 +26701,7 @@ _tmp_28_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (z = _PyPegen_name_token(p)) // NAME ) @@ -26766,7 +26864,7 @@ _tmp_31_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (z = _PyPegen_name_token(p)) // NAME ) @@ -28753,7 +28851,7 @@ _tmp_62_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (z = _PyPegen_name_token(p)) // NAME ) @@ -28799,7 +28897,7 @@ _tmp_63_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (z = _PyPegen_name_token(p)) // NAME ) @@ -34581,7 +34679,7 @@ _tmp_157_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'else'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='else' + (_keyword = _PyPegen_expect_token(p, 661)) // token='else' ) { D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'else'")); @@ -38174,7 +38272,7 @@ _tmp_213_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_213[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'except'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 651)) // token='except' + (_keyword = _PyPegen_expect_token(p, 653)) // token='except' ) { D(fprintf(stderr, "%*c+ _tmp_213[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except'")); @@ -38193,7 +38291,7 @@ _tmp_213_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_213[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'finally'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 647)) // token='finally' + (_keyword = _PyPegen_expect_token(p, 649)) // token='finally' ) { D(fprintf(stderr, "%*c+ _tmp_213[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'finally'")); @@ -38371,7 +38469,7 @@ _tmp_216_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -38593,7 +38691,7 @@ _tmp_220_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -38634,7 +38732,7 @@ _tmp_221_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -38732,7 +38830,7 @@ _tmp_223_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -38773,7 +38871,7 @@ _tmp_224_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -40598,7 +40696,7 @@ _tmp_258_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (z = disjunction_rule(p)) // disjunction ) @@ -40644,7 +40742,7 @@ _tmp_259_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (z = disjunction_rule(p)) // disjunction ) @@ -41250,7 +41348,7 @@ _tmp_272_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -41468,7 +41566,7 @@ _tmp_276_rule(Parser *p) Token * _keyword; expr_ty star_target_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (star_target_var = star_target_rule(p)) // star_target ) @@ -41509,7 +41607,7 @@ _tmp_277_rule(Parser *p) Token * _keyword; expr_ty star_target_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (star_target_var = star_target_rule(p)) // star_target ) @@ -41550,7 +41648,7 @@ _tmp_278_rule(Parser *p) Token * _keyword; expr_ty star_target_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (star_target_var = star_target_rule(p)) // star_target ) @@ -41591,7 +41689,7 @@ _tmp_279_rule(Parser *p) Token * _keyword; expr_ty star_target_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (star_target_var = star_target_rule(p)) // star_target ) From 3b3ec0d77f0f836cbe5ff1ab97efcc8b7ed5d787 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 20 Nov 2023 15:52:00 +0200 Subject: [PATCH 016/228] gh-111863: Rename `Py_NOGIL` to `Py_GIL_DISABLED` (#111864) Rename Py_NOGIL to Py_GIL_DISABLED --- Include/cpython/pystate.h | 2 +- Include/internal/pycore_critical_section.h | 6 ++--- Include/internal/pycore_importdl.h | 2 +- Include/internal/pycore_lock.h | 6 ++--- Include/internal/pycore_object.h | 14 +++++----- Include/object.h | 26 +++++++++---------- Lib/sysconfig/__init__.py | 2 +- Lib/test/libregrtest/utils.py | 2 +- Lib/test/pythoninfo.py | 2 +- Lib/test/support/__init__.py | 2 +- Lib/test/test_cppext/__init__.py | 2 +- Lib/test/test_importlib/test_windows.py | 2 +- Lib/test/test_sys.py | 2 +- ...-11-08-20-28-03.gh-issue-111863.RPeFAX.rst | 1 + Modules/_ctypes/_ctypes_test.c | 4 +-- Modules/_multiprocessing/posixshmem.c | 4 +-- Modules/_scproxy.c | 4 +-- Modules/_stat.c | 4 +-- Modules/_sysconfig.c | 4 +-- Modules/_testcapi/heaptype_relative.c | 4 +-- Modules/_testcapi/vectorcall_limited.c | 4 +-- Modules/_testclinic_limited.c | 4 +-- Modules/_testimportmultiple.c | 4 +-- .../test_critical_sections.c | 4 +-- Modules/_uuidmodule.c | 4 +-- Modules/errnomodule.c | 4 +-- Modules/hashlib.h | 2 +- Modules/resource.c | 4 +-- Modules/xxlimited.c | 4 +-- Modules/xxlimited_35.c | 4 +-- Objects/object.c | 6 ++--- PC/winsound.c | 2 +- PCbuild/pyproject.props | 2 +- Python/ceval.c | 4 +-- Python/lock.c | 2 +- Python/pystate.c | 4 +-- configure | 2 +- configure.ac | 2 +- pyconfig.h.in | 6 ++--- 39 files changed, 82 insertions(+), 81 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2023-11-08-20-28-03.gh-issue-111863.RPeFAX.rst diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 34e23830f27e77..56172d231c44f4 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -151,7 +151,7 @@ struct _ts { /* Tagged pointer to top-most critical section, or zero if there is no * active critical section. Critical sections are only used in - * `--disable-gil` builds (i.e., when Py_NOGIL is defined to 1). In the + * `--disable-gil` builds (i.e., when Py_GIL_DISABLED is defined to 1). In the * default build, this field is always zero. */ uintptr_t critical_section; diff --git a/Include/internal/pycore_critical_section.h b/Include/internal/pycore_critical_section.h index 73c2e243f20bcc..bf2bbfffc38bd0 100644 --- a/Include/internal/pycore_critical_section.h +++ b/Include/internal/pycore_critical_section.h @@ -86,7 +86,7 @@ extern "C" { #define _Py_CRITICAL_SECTION_TWO_MUTEXES 0x2 #define _Py_CRITICAL_SECTION_MASK 0x3 -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED # define Py_BEGIN_CRITICAL_SECTION(op) \ { \ _PyCriticalSection _cs; \ @@ -104,13 +104,13 @@ extern "C" { # define Py_END_CRITICAL_SECTION2() \ _PyCriticalSection2_End(&_cs2); \ } -#else /* !Py_NOGIL */ +#else /* !Py_GIL_DISABLED */ // The critical section APIs are no-ops with the GIL. # define Py_BEGIN_CRITICAL_SECTION(op) # define Py_END_CRITICAL_SECTION() # define Py_BEGIN_CRITICAL_SECTION2(a, b) # define Py_END_CRITICAL_SECTION2() -#endif /* !Py_NOGIL */ +#endif /* !Py_GIL_DISABLED */ typedef struct { // Tagged pointer to an outer active critical section (or 0). diff --git a/Include/internal/pycore_importdl.h b/Include/internal/pycore_importdl.h index dee64241c763f3..c8583582b358ac 100644 --- a/Include/internal/pycore_importdl.h +++ b/Include/internal/pycore_importdl.h @@ -31,7 +31,7 @@ typedef FARPROC dl_funcptr; # define PYD_DEBUG_SUFFIX "" #endif -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED # define PYD_THREADING_TAG "t" #else # define PYD_THREADING_TAG "" diff --git a/Include/internal/pycore_lock.h b/Include/internal/pycore_lock.h index 25c3cf5377b778..f135cbbc3754fb 100644 --- a/Include/internal/pycore_lock.h +++ b/Include/internal/pycore_lock.h @@ -33,11 +33,11 @@ extern "C" { // ... // PyMutex_Unlock(&m); -// NOTE: In Py_NOGIL builds, `struct _PyMutex` is defined in Include/object.h. -// The Py_NOGIL builds need the definition in Include/object.h for the +// NOTE: In Py_GIL_DISABLED builds, `struct _PyMutex` is defined in Include/object.h. +// The Py_GIL_DISABLED builds need the definition in Include/object.h for the // `ob_mutex` field in PyObject. For the default (non-free-threaded) build, // we define the struct here to avoid exposing it in the public API. -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED struct _PyMutex { uint8_t v; }; #endif diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 206d8a5d4cc5e1..f413b8451e5ab4 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -54,7 +54,7 @@ PyAPI_FUNC(int) _PyObject_IsFreed(PyObject *); Furthermore, we can't use designated initializers in Extensions since these are not supported pre-C++20. Thus, keeping an internal copy here is the most backwards compatible solution */ -#if defined(Py_NOGIL) +#if defined(Py_GIL_DISABLED) #define _PyObject_HEAD_INIT(type) \ { \ .ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL, \ @@ -103,7 +103,7 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n) #ifdef Py_REF_DEBUG _Py_AddRefTotal(_PyInterpreterState_GET(), n); #endif -#if !defined(Py_NOGIL) +#if !defined(Py_GIL_DISABLED) op->ob_refcnt += n; #else if (_Py_IsOwnedByCurrentThread(op)) { @@ -128,7 +128,7 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n) static inline void _Py_SetImmortal(PyObject *op) { if (op) { -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED op->ob_tid = _Py_UNOWNED_TID; op->ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL; op->ob_ref_shared = 0; @@ -145,7 +145,7 @@ static inline void _Py_SetMortal(PyObject *op, Py_ssize_t refcnt) { if (op) { assert(_Py_IsImmortal(op)); -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED op->ob_tid = _Py_UNOWNED_TID; op->ob_ref_local = 0; op->ob_ref_shared = _Py_REF_SHARED(refcnt, _Py_REF_MERGED); @@ -169,7 +169,7 @@ static inline void _Py_ClearImmortal(PyObject *op) op = NULL; \ } while (0) -#if !defined(Py_NOGIL) +#if !defined(Py_GIL_DISABLED) static inline void _Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct) { @@ -210,7 +210,7 @@ _Py_DECREF_NO_DEALLOC(PyObject *op) } #else -// TODO: implement Py_DECREF specializations for Py_NOGIL build +// TODO: implement Py_DECREF specializations for Py_GIL_DISABLED build static inline void _Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct) { @@ -238,7 +238,7 @@ _Py_REF_IS_QUEUED(Py_ssize_t ob_ref_shared) // Merge the local and shared reference count fields and add `extra` to the // refcount when merging. Py_ssize_t _Py_ExplicitMergeRefcount(PyObject *op, Py_ssize_t extra); -#endif // !defined(Py_NOGIL) +#endif // !defined(Py_GIL_DISABLED) #ifdef Py_REF_DEBUG # undef _Py_DEC_REFTOTAL diff --git a/Include/object.h b/Include/object.h index 061b5093fb1a35..6b70a494844476 100644 --- a/Include/object.h +++ b/Include/object.h @@ -106,9 +106,9 @@ check by comparing the reference count field to the immortality reference count. #define _Py_IMMORTAL_REFCNT (UINT_MAX >> 2) #endif -// Py_NOGIL builds indicate immortal objects using `ob_ref_local`, which is +// Py_GIL_DISABLED builds indicate immortal objects using `ob_ref_local`, which is // always 32-bits. -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED #define _Py_IMMORTAL_REFCNT_LOCAL UINT32_MAX #endif @@ -117,7 +117,7 @@ check by comparing the reference count field to the immortality reference count. // Make all internal uses of PyObject_HEAD_INIT immortal while preserving the // C-API expectation that the refcnt will be set to 1. -#if defined(Py_NOGIL) +#if defined(Py_GIL_DISABLED) #define PyObject_HEAD_INIT(type) \ { \ 0, \ @@ -162,7 +162,7 @@ check by comparing the reference count field to the immortality reference count. * by hand. Similarly every pointer to a variable-size Python object can, * in addition, be cast to PyVarObject*. */ -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED struct _object { #if (defined(__GNUC__) || defined(__clang__)) \ && !(defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112L) @@ -238,7 +238,7 @@ typedef struct { PyAPI_FUNC(int) Py_Is(PyObject *x, PyObject *y); #define Py_Is(x, y) ((x) == (y)) -#if defined(Py_NOGIL) && !defined(Py_LIMITED_API) +#if defined(Py_GIL_DISABLED) && !defined(Py_LIMITED_API) static inline uintptr_t _Py_ThreadId(void) { @@ -275,7 +275,7 @@ _Py_IsOwnedByCurrentThread(PyObject *ob) #endif static inline Py_ssize_t Py_REFCNT(PyObject *ob) { -#if !defined(Py_NOGIL) +#if !defined(Py_GIL_DISABLED) return ob->ob_refcnt; #else uint32_t local = _Py_atomic_load_uint32_relaxed(&ob->ob_ref_local); @@ -316,7 +316,7 @@ static inline Py_ssize_t Py_SIZE(PyObject *ob) { static inline Py_ALWAYS_INLINE int _Py_IsImmortal(PyObject *op) { -#if defined(Py_NOGIL) +#if defined(Py_GIL_DISABLED) return op->ob_ref_local == _Py_IMMORTAL_REFCNT_LOCAL; #elif SIZEOF_VOID_P > 4 return _Py_CAST(PY_INT32_T, op->ob_refcnt) < 0; @@ -350,7 +350,7 @@ static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { if (_Py_IsImmortal(ob)) { return; } -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED ob->ob_refcnt = refcnt; #else if (_Py_IsOwnedByCurrentThread(ob)) { @@ -367,7 +367,7 @@ static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { ob->ob_ref_local = 0; ob->ob_ref_shared = _Py_REF_SHARED(refcnt, _Py_REF_MERGED); } -#endif // Py_NOGIL +#endif // Py_GIL_DISABLED #endif // Py_LIMITED_API+0 < 0x030d0000 } #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000 @@ -746,7 +746,7 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op) #else // Non-limited C API and limited C API for Python 3.9 and older access // directly PyObject.ob_refcnt. -#if defined(Py_NOGIL) +#if defined(Py_GIL_DISABLED) uint32_t local = _Py_atomic_load_uint32_relaxed(&op->ob_ref_local); uint32_t new_local = local + 1; if (new_local == 0) { @@ -784,7 +784,7 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op) #endif -#if !defined(Py_LIMITED_API) && defined(Py_NOGIL) +#if !defined(Py_LIMITED_API) && defined(Py_GIL_DISABLED) // Implements Py_DECREF on objects not owned by the current thread. PyAPI_FUNC(void) _Py_DecRefShared(PyObject *); PyAPI_FUNC(void) _Py_DecRefSharedDebug(PyObject *, const char *, int); @@ -810,7 +810,7 @@ static inline void Py_DECREF(PyObject *op) { } #define Py_DECREF(op) Py_DECREF(_PyObject_CAST(op)) -#elif defined(Py_NOGIL) && defined(Py_REF_DEBUG) +#elif defined(Py_GIL_DISABLED) && defined(Py_REF_DEBUG) static inline void Py_DECREF(const char *filename, int lineno, PyObject *op) { uint32_t local = _Py_atomic_load_uint32_relaxed(&op->ob_ref_local); @@ -835,7 +835,7 @@ static inline void Py_DECREF(const char *filename, int lineno, PyObject *op) } #define Py_DECREF(op) Py_DECREF(__FILE__, __LINE__, _PyObject_CAST(op)) -#elif defined(Py_NOGIL) +#elif defined(Py_GIL_DISABLED) static inline void Py_DECREF(PyObject *op) { uint32_t local = _Py_atomic_load_uint32_relaxed(&op->ob_ref_local); diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index 68d30c0f9e618f..2a7fa45be079de 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -348,7 +348,7 @@ def _init_non_posix(vars): vars['BINLIBDEST'] = get_path('platstdlib') vars['INCLUDEPY'] = get_path('include') - # Add EXT_SUFFIX, SOABI, and Py_NOGIL + # Add EXT_SUFFIX, SOABI, and Py_GIL_DISABLED vars.update(_sysconfig.config_vars()) vars['LIBDIR'] = _safe_realpath(os.path.join(get_config_var('installed_base'), 'libs')) diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index bd4dce3400f0a9..e4a28af381ee2d 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -290,7 +290,7 @@ def get_build_info(): build = [] # --disable-gil - if sysconfig.get_config_var('Py_NOGIL'): + if sysconfig.get_config_var('Py_GIL_DISABLED'): build.append("nogil") if hasattr(sys, 'gettotalrefcount'): diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index 3a91a7dbc65021..49e41ca6cdaf98 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -517,7 +517,7 @@ def collect_sysconfig(info_add): 'PY_STDMODULE_CFLAGS', 'Py_DEBUG', 'Py_ENABLE_SHARED', - 'Py_NOGIL', + 'Py_GIL_DISABLED', 'SHELL', 'SOABI', 'abs_builddir', diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index bb9f998db4622b..eec5498e633eb6 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -796,7 +796,7 @@ def check_cflags_pgo(): return any(option in cflags_nodist for option in pgo_options) -if sysconfig.get_config_var('Py_NOGIL'): +if sysconfig.get_config_var('Py_GIL_DISABLED'): _header = 'PHBBInP' else: _header = 'nP' diff --git a/Lib/test/test_cppext/__init__.py b/Lib/test/test_cppext/__init__.py index 4d9ee3cb2228ae..299a16ada2e32e 100644 --- a/Lib/test/test_cppext/__init__.py +++ b/Lib/test/test_cppext/__init__.py @@ -15,7 +15,7 @@ # gh-110119: pip does not currently support 't' in the ABI flag use by # --disable-gil builds. Once it does, we can remove this skip. -@unittest.skipIf(sysconfig.get_config_var('Py_NOGIL') == 1, +@unittest.skipIf(sysconfig.get_config_var('Py_GIL_DISABLED') == 1, 'test does not work with --disable-gil') @support.requires_subprocess() class TestCPPExt(unittest.TestCase): diff --git a/Lib/test/test_importlib/test_windows.py b/Lib/test/test_importlib/test_windows.py index a60a4c4cebcf1a..d25133240b1afd 100644 --- a/Lib/test/test_importlib/test_windows.py +++ b/Lib/test/test_importlib/test_windows.py @@ -112,7 +112,7 @@ def test_module_not_found(self): class WindowsExtensionSuffixTests: def test_tagged_suffix(self): suffixes = self.machinery.EXTENSION_SUFFIXES - abi_flags = "t" if sysconfig.get_config_var("Py_NOGIL") else "" + abi_flags = "t" if sysconfig.get_config_var("Py_GIL_DISABLED") else "" ver = sys.version_info platform = re.sub('[^a-zA-Z0-9]', '_', get_platform()) expected_tag = f".cp{ver.major}{ver.minor}{abi_flags}-{platform}.pyd" diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index b111962abdcdc3..ae73f5a7de7421 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1209,7 +1209,7 @@ def test_pystats(self): @unittest.skipUnless(hasattr(sys, 'abiflags'), 'need sys.abiflags') def test_disable_gil_abi(self): abi_threaded = 't' in sys.abiflags - py_nogil = (sysconfig.get_config_var('Py_NOGIL') == 1) + py_nogil = (sysconfig.get_config_var('Py_GIL_DISABLED') == 1) self.assertEqual(py_nogil, abi_threaded) diff --git a/Misc/NEWS.d/next/C_API/2023-11-08-20-28-03.gh-issue-111863.RPeFAX.rst b/Misc/NEWS.d/next/C_API/2023-11-08-20-28-03.gh-issue-111863.RPeFAX.rst new file mode 100644 index 00000000000000..9e9145ceecf567 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2023-11-08-20-28-03.gh-issue-111863.RPeFAX.rst @@ -0,0 +1 @@ +Rename ``Py_NOGIL`` to ``Py_GIL_DISABLED``. Patch by Hugo van Kemenade. diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c index 5869d8f952d184..d33e6fc7586d28 100644 --- a/Modules/_ctypes/_ctypes_test.c +++ b/Modules/_ctypes/_ctypes_test.c @@ -1,8 +1,8 @@ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED #define Py_LIMITED_API 0x030c0000 #endif diff --git a/Modules/_multiprocessing/posixshmem.c b/Modules/_multiprocessing/posixshmem.c index cd08a9fedc0578..425ce10075c156 100644 --- a/Modules/_multiprocessing/posixshmem.c +++ b/Modules/_multiprocessing/posixshmem.c @@ -2,9 +2,9 @@ posixshmem - A Python extension that provides shm_open() and shm_unlink() */ -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED #define Py_LIMITED_API 0x030c0000 #endif diff --git a/Modules/_scproxy.c b/Modules/_scproxy.c index 9f7a65c91e2d33..7920d2c2b8739d 100644 --- a/Modules/_scproxy.c +++ b/Modules/_scproxy.c @@ -4,10 +4,10 @@ */ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED #define Py_LIMITED_API 0x030c0000 #endif diff --git a/Modules/_stat.c b/Modules/_stat.c index 9e278d6dcd1589..1ef1e97f4b7dca 100644 --- a/Modules/_stat.c +++ b/Modules/_stat.c @@ -12,10 +12,10 @@ */ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.13 for PyModule_Add() on Windows #define Py_LIMITED_API 0x030d0000 #endif diff --git a/Modules/_sysconfig.c b/Modules/_sysconfig.c index 6f1cc16b58467d..2918913c18ed1e 100644 --- a/Modules/_sysconfig.c +++ b/Modules/_sysconfig.c @@ -57,12 +57,12 @@ _sysconfig_config_vars_impl(PyObject *module) } #endif -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED PyObject *py_nogil = _PyLong_GetOne(); #else PyObject *py_nogil = _PyLong_GetZero(); #endif - if (PyDict_SetItemString(config, "Py_NOGIL", py_nogil) < 0) { + if (PyDict_SetItemString(config, "Py_GIL_DISABLED", py_nogil) < 0) { Py_DECREF(config); return NULL; } diff --git a/Modules/_testcapi/heaptype_relative.c b/Modules/_testcapi/heaptype_relative.c index b58d26cddc723a..52286f05f7154c 100644 --- a/Modules/_testcapi/heaptype_relative.c +++ b/Modules/_testcapi/heaptype_relative.c @@ -1,8 +1,8 @@ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED #define Py_LIMITED_API 0x030c0000 // 3.12 #endif diff --git a/Modules/_testcapi/vectorcall_limited.c b/Modules/_testcapi/vectorcall_limited.c index 857cb30b3da1ca..0a650f1b351d2d 100644 --- a/Modules/_testcapi/vectorcall_limited.c +++ b/Modules/_testcapi/vectorcall_limited.c @@ -1,10 +1,10 @@ /* Test Vectorcall in the limited API */ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED #define Py_LIMITED_API 0x030c0000 // 3.12 #endif diff --git a/Modules/_testclinic_limited.c b/Modules/_testclinic_limited.c index c5d07e1370dac8..61bc84134458da 100644 --- a/Modules/_testclinic_limited.c +++ b/Modules/_testclinic_limited.c @@ -5,10 +5,10 @@ #undef Py_BUILD_CORE_BUILTIN #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // For now, only limited C API 3.13 is supported #define Py_LIMITED_API 0x030d0000 #endif diff --git a/Modules/_testimportmultiple.c b/Modules/_testimportmultiple.c index 99d48b6617f49c..245e81b2dce7f8 100644 --- a/Modules/_testimportmultiple.c +++ b/Modules/_testimportmultiple.c @@ -5,10 +5,10 @@ */ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED #define Py_LIMITED_API 0x03020000 #endif diff --git a/Modules/_testinternalcapi/test_critical_sections.c b/Modules/_testinternalcapi/test_critical_sections.c index 9392096a16d1bf..1f7e311558b27c 100644 --- a/Modules/_testinternalcapi/test_critical_sections.c +++ b/Modules/_testinternalcapi/test_critical_sections.c @@ -6,7 +6,7 @@ #include "pycore_critical_section.h" -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED #define assert_nogil assert #define assert_gil(x) #else @@ -25,7 +25,7 @@ test_critical_sections(PyObject *self, PyObject *Py_UNUSED(args)) assert(d2 != NULL); // Beginning a critical section should lock the associated object and - // push the critical section onto the thread's stack (in Py_NOGIL builds). + // push the critical section onto the thread's stack (in Py_GIL_DISABLED builds). Py_BEGIN_CRITICAL_SECTION(d1); assert_nogil(PyMutex_IsLocked(&d1->ob_mutex)); assert_nogil(_PyCriticalSection_IsActive(PyThreadState_GET()->critical_section)); diff --git a/Modules/_uuidmodule.c b/Modules/_uuidmodule.c index 1d716051df4ade..d8b211c632eef1 100644 --- a/Modules/_uuidmodule.c +++ b/Modules/_uuidmodule.c @@ -4,10 +4,10 @@ */ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED #define Py_LIMITED_API 0x030c0000 #endif diff --git a/Modules/errnomodule.c b/Modules/errnomodule.c index 1e2c55a1a26b4b..8287edbfb47f6c 100644 --- a/Modules/errnomodule.c +++ b/Modules/errnomodule.c @@ -1,10 +1,10 @@ /* Errno module */ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED #define Py_LIMITED_API 0x030c0000 #endif diff --git a/Modules/hashlib.h b/Modules/hashlib.h index 25b2aaadfb2868..7105e68af7b806 100644 --- a/Modules/hashlib.h +++ b/Modules/hashlib.h @@ -58,7 +58,7 @@ PyMutex_Unlock(&(obj)->mutex); \ } -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED #define HASHLIB_INIT_MUTEX(obj) \ do { \ (obj)->mutex = (PyMutex){0}; \ diff --git a/Modules/resource.c b/Modules/resource.c index 87c44607df1c4c..a4b8f648c329e3 100644 --- a/Modules/resource.c +++ b/Modules/resource.c @@ -1,8 +1,8 @@ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.13 for PySys_Audit() #define Py_LIMITED_API 0x030d0000 #endif diff --git a/Modules/xxlimited.c b/Modules/xxlimited.c index 21d7e9ba67ad78..19f61216255cfa 100644 --- a/Modules/xxlimited.c +++ b/Modules/xxlimited.c @@ -63,10 +63,10 @@ */ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED #define Py_LIMITED_API 0x030c0000 #endif diff --git a/Modules/xxlimited_35.c b/Modules/xxlimited_35.c index 04673ea68cfee5..867820a6cb93fa 100644 --- a/Modules/xxlimited_35.c +++ b/Modules/xxlimited_35.c @@ -6,10 +6,10 @@ */ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED #define Py_LIMITED_API 0x03050000 #endif diff --git a/Objects/object.c b/Objects/object.c index 1f5b2b4f79e5fa..d145674cb3ba34 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -296,7 +296,7 @@ _Py_DecRef(PyObject *o) Py_DECREF(o); } -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED # ifdef Py_REF_DEBUG static inline int is_shared_refcnt_dead(Py_ssize_t shared) @@ -414,7 +414,7 @@ _Py_ExplicitMergeRefcount(PyObject *op, Py_ssize_t extra) _Py_atomic_store_uintptr_relaxed(&op->ob_tid, 0); return refcnt; } -#endif /* Py_NOGIL */ +#endif /* Py_GIL_DISABLED */ /**************************************/ @@ -2359,7 +2359,7 @@ new_reference(PyObject *op) _PyTraceMalloc_NewReference(op); } // Skip the immortal object check in Py_SET_REFCNT; always set refcnt to 1 -#if !defined(Py_NOGIL) +#if !defined(Py_GIL_DISABLED) op->ob_refcnt = 1; #else op->ob_tid = _Py_ThreadId(); diff --git a/PC/winsound.c b/PC/winsound.c index d1ff991c934bae..b0e416cfec4699 100644 --- a/PC/winsound.c +++ b/PC/winsound.c @@ -35,7 +35,7 @@ winsound.PlaySound(None, 0) */ -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED #define Py_LIMITED_API 0x030c0000 #endif diff --git a/PCbuild/pyproject.props b/PCbuild/pyproject.props index bb3555bd123089..0acc7045c39a26 100644 --- a/PCbuild/pyproject.props +++ b/PCbuild/pyproject.props @@ -40,7 +40,7 @@ $(PySourcePath)Include;$(PySourcePath)Include\internal;$(PySourcePath)Include\internal\mimalloc;$(PySourcePath)PC;$(IntDir);%(AdditionalIncludeDirectories) WIN32;$(_Py3NamePreprocessorDefinition);$(_PlatformPreprocessorDefinition)$(_DebugPreprocessorDefinition)$(_PydPreprocessorDefinition)%(PreprocessorDefinitions) - Py_NOGIL=1;%(PreprocessorDefinitions) + Py_GIL_DISABLED=1;%(PreprocessorDefinitions) _Py_USING_PGO=1;%(PreprocessorDefinitions) MaxSpeed diff --git a/Python/ceval.c b/Python/ceval.c index ae515780440ecb..ba234dc5774472 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -46,13 +46,13 @@ # error "ceval.c must be build with Py_BUILD_CORE define for best performance" #endif -#if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_NOGIL) +#if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_GIL_DISABLED) // GH-89279: The MSVC compiler does not inline these static inline functions // in PGO build in _PyEval_EvalFrameDefault(), because this function is over // the limit of PGO, and that limit cannot be configured. // Define them as macros to make sure that they are always inlined by the // preprocessor. -// TODO: implement Py_DECREF macro for Py_NOGIL +// TODO: implement Py_DECREF macro for Py_GIL_DISABLED #undef Py_DECREF #define Py_DECREF(arg) \ diff --git a/Python/lock.c b/Python/lock.c index bc43b1ab192fea..e9279f0b92a5e7 100644 --- a/Python/lock.c +++ b/Python/lock.c @@ -21,7 +21,7 @@ static const _PyTime_t TIME_TO_BE_FAIR_NS = 1000*1000; // Spin for a bit before parking the thread. This is only enabled for // `--disable-gil` builds because it is unlikely to be helpful if the GIL is // enabled. -#if Py_NOGIL +#if Py_GIL_DISABLED static const int MAX_SPIN_COUNT = 40; #else static const int MAX_SPIN_COUNT = 0; diff --git a/Python/pystate.c b/Python/pystate.c index 89e9bddc6de060..6196b15da0117a 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1857,7 +1857,7 @@ tstate_deactivate(PyThreadState *tstate) static int tstate_try_attach(PyThreadState *tstate) { -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED int expected = _Py_THREAD_DETACHED; if (_Py_atomic_compare_exchange_int( &tstate->state, @@ -1877,7 +1877,7 @@ static void tstate_set_detached(PyThreadState *tstate) { assert(tstate->state == _Py_THREAD_ATTACHED); -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED _Py_atomic_store_int(&tstate->state, _Py_THREAD_DETACHED); #else tstate->state = _Py_THREAD_DETACHED; diff --git a/configure b/configure index 5e86e8255c03d0..319009537f461c 100755 --- a/configure +++ b/configure @@ -7883,7 +7883,7 @@ printf "%s\n" "$disable_gil" >&6; } if test "$disable_gil" = "yes" then -printf "%s\n" "#define Py_NOGIL 1" >>confdefs.h +printf "%s\n" "#define Py_GIL_DISABLED 1" >>confdefs.h # Add "t" for "threaded" ABIFLAGS="${ABIFLAGS}t" diff --git a/configure.ac b/configure.ac index f46e19ba4be3f8..b78472e04846b7 100644 --- a/configure.ac +++ b/configure.ac @@ -1505,7 +1505,7 @@ AC_MSG_RESULT([$disable_gil]) if test "$disable_gil" = "yes" then - AC_DEFINE([Py_NOGIL], [1], + AC_DEFINE([Py_GIL_DISABLED], [1], [Define if you want to disable the GIL]) # Add "t" for "threaded" ABIFLAGS="${ABIFLAGS}t" diff --git a/pyconfig.h.in b/pyconfig.h.in index 2bbc1bdd52dc0e..bf708926e22c43 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1611,13 +1611,13 @@ /* Defined if Python is built as a shared library. */ #undef Py_ENABLE_SHARED +/* Define if you want to disable the GIL */ +#undef Py_GIL_DISABLED + /* Define hash algorithm for str, bytes and memoryview. SipHash24: 1, FNV: 2, SipHash13: 3, externally defined: 0 */ #undef Py_HASH_ALGORITHM -/* Define if you want to disable the GIL */ -#undef Py_NOGIL - /* Define if you want to enable internal statistics gathering. */ #undef Py_STATS From d59feb5dbe5395615d06c30a95e6a6a9b7681d4d Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Mon, 20 Nov 2023 15:18:24 +0000 Subject: [PATCH 017/228] gh-112243: Don't include comments in f-string debug expressions (#112284) --- Lib/test/test_fstring.py | 3 + ...-11-20-14-13-02.gh-issue-112243.FKdQnr.rst | 1 + Parser/lexer/lexer.c | 55 +++++++++++++++++-- 3 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-20-14-13-02.gh-issue-112243.FKdQnr.rst diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index dd8c2dd628ee13..da0160d2382cc6 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -1627,6 +1627,9 @@ def __repr__(self): self.assertEqual(f'X{x = }Y', 'Xx = '+repr(x)+'Y') self.assertEqual(f"sadsd {1 + 1 = :{1 + 1:1d}f}", "sadsd 1 + 1 = 2.000000") + self.assertEqual(f"{1+2 = # my comment + }", '1+2 = \n 3') + # These next lines contains tabs. Backslash escapes don't # work in f-strings. # patchcheck doesn't like these tabs. So the only way to test diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-20-14-13-02.gh-issue-112243.FKdQnr.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-20-14-13-02.gh-issue-112243.FKdQnr.rst new file mode 100644 index 00000000000000..d69f29f5c63490 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-20-14-13-02.gh-issue-112243.FKdQnr.rst @@ -0,0 +1 @@ +Don't include comments in f-string debug expressions. Patch by Pablo Galindo diff --git a/Parser/lexer/lexer.c b/Parser/lexer/lexer.c index 2ba24a2c2405f2..ea4bdf7ce4a24c 100644 --- a/Parser/lexer/lexer.c +++ b/Parser/lexer/lexer.c @@ -116,13 +116,56 @@ set_fstring_expr(struct tok_state* tok, struct token *token, char c) { if (!tok_mode->f_string_debug || token->metadata) { return 0; } + PyObject *res = NULL; - PyObject *res = PyUnicode_DecodeUTF8( - tok_mode->last_expr_buffer, - tok_mode->last_expr_size - tok_mode->last_expr_end, - NULL - ); - if (!res) { + // Check if there is a # character in the expression + int hash_detected = 0; + for (Py_ssize_t i = 0; i < tok_mode->last_expr_size - tok_mode->last_expr_end; i++) { + if (tok_mode->last_expr_buffer[i] == '#') { + hash_detected = 1; + break; + } + } + + if (hash_detected) { + Py_ssize_t input_length = tok_mode->last_expr_size - tok_mode->last_expr_end; + char *result = (char *)PyObject_Malloc((input_length + 1) * sizeof(char)); + if (!result) { + return -1; + } + + Py_ssize_t i = 0; + Py_ssize_t j = 0; + + for (i = 0, j = 0; i < input_length; i++) { + if (tok_mode->last_expr_buffer[i] == '#') { + // Skip characters until newline or end of string + while (tok_mode->last_expr_buffer[i] != '\0' && i < input_length) { + if (tok_mode->last_expr_buffer[i] == '\n') { + result[j++] = tok_mode->last_expr_buffer[i]; + break; + } + i++; + } + } else { + result[j++] = tok_mode->last_expr_buffer[i]; + } + } + + result[j] = '\0'; // Null-terminate the result string + res = PyUnicode_DecodeUTF8(result, j, NULL); + PyObject_Free(result); + } else { + res = PyUnicode_DecodeUTF8( + tok_mode->last_expr_buffer, + tok_mode->last_expr_size - tok_mode->last_expr_end, + NULL + ); + + } + + + if (!res) { return -1; } token->metadata = res; From 1995955173737bcb009dbacaeff7821b4d744148 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 20 Nov 2023 10:08:53 -0800 Subject: [PATCH 018/228] gh-106529: Make FOR_ITER a viable uop (#112134) This uses the new mechanism whereby certain uops are replaced by others during translation, using the `_PyUop_Replacements` table. We further special-case the `_FOR_ITER_TIER_TWO` uop to update the deoptimization target to point just past the corresponding `END_FOR` opcode. Two tiny code cleanups are also part of this PR. --- Include/internal/pycore_opcode_metadata.h | 86 ++++++++++--------- Lib/test/test_capi/test_misc.py | 30 +++++++ ...-11-15-16-14-10.gh-issue-106529.Y48ax9.rst | 1 + Python/abstract_interp_cases.c.h | 6 ++ Python/bytecodes.c | 23 ++++- Python/ceval.c | 4 +- Python/executor_cases.c.h | 25 ++++++ Python/optimizer.c | 6 ++ 8 files changed, 138 insertions(+), 43 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-15-16-14-10.gh-issue-106529.Y48ax9.rst diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 4d98b23df5d927..4e45725d393479 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -81,45 +81,46 @@ #define _IS_NONE 353 #define _SPECIALIZE_FOR_ITER 354 #define _FOR_ITER 355 -#define _ITER_CHECK_LIST 356 -#define _ITER_JUMP_LIST 357 -#define _GUARD_NOT_EXHAUSTED_LIST 358 -#define _ITER_NEXT_LIST 359 -#define _ITER_CHECK_TUPLE 360 -#define _ITER_JUMP_TUPLE 361 -#define _GUARD_NOT_EXHAUSTED_TUPLE 362 -#define _ITER_NEXT_TUPLE 363 -#define _ITER_CHECK_RANGE 364 -#define _ITER_JUMP_RANGE 365 -#define _GUARD_NOT_EXHAUSTED_RANGE 366 -#define _ITER_NEXT_RANGE 367 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 368 -#define _GUARD_KEYS_VERSION 369 -#define _LOAD_ATTR_METHOD_WITH_VALUES 370 -#define _LOAD_ATTR_METHOD_NO_DICT 371 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 372 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 373 -#define _CHECK_ATTR_METHOD_LAZY_DICT 374 -#define _LOAD_ATTR_METHOD_LAZY_DICT 375 -#define _SPECIALIZE_CALL 376 -#define _CALL 377 -#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 378 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 379 -#define _CHECK_PEP_523 380 -#define _CHECK_FUNCTION_EXACT_ARGS 381 -#define _CHECK_STACK_SPACE 382 -#define _INIT_CALL_PY_EXACT_ARGS 383 -#define _PUSH_FRAME 384 -#define _SPECIALIZE_BINARY_OP 385 -#define _BINARY_OP 386 -#define _GUARD_IS_TRUE_POP 387 -#define _GUARD_IS_FALSE_POP 388 -#define _GUARD_IS_NONE_POP 389 -#define _GUARD_IS_NOT_NONE_POP 390 -#define _JUMP_TO_TOP 391 -#define _SAVE_RETURN_OFFSET 392 -#define _INSERT 393 -#define _CHECK_VALIDITY 394 +#define _FOR_ITER_TIER_TWO 356 +#define _ITER_CHECK_LIST 357 +#define _ITER_JUMP_LIST 358 +#define _GUARD_NOT_EXHAUSTED_LIST 359 +#define _ITER_NEXT_LIST 360 +#define _ITER_CHECK_TUPLE 361 +#define _ITER_JUMP_TUPLE 362 +#define _GUARD_NOT_EXHAUSTED_TUPLE 363 +#define _ITER_NEXT_TUPLE 364 +#define _ITER_CHECK_RANGE 365 +#define _ITER_JUMP_RANGE 366 +#define _GUARD_NOT_EXHAUSTED_RANGE 367 +#define _ITER_NEXT_RANGE 368 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 369 +#define _GUARD_KEYS_VERSION 370 +#define _LOAD_ATTR_METHOD_WITH_VALUES 371 +#define _LOAD_ATTR_METHOD_NO_DICT 372 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 373 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 374 +#define _CHECK_ATTR_METHOD_LAZY_DICT 375 +#define _LOAD_ATTR_METHOD_LAZY_DICT 376 +#define _SPECIALIZE_CALL 377 +#define _CALL 378 +#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 379 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 380 +#define _CHECK_PEP_523 381 +#define _CHECK_FUNCTION_EXACT_ARGS 382 +#define _CHECK_STACK_SPACE 383 +#define _INIT_CALL_PY_EXACT_ARGS 384 +#define _PUSH_FRAME 385 +#define _SPECIALIZE_BINARY_OP 386 +#define _BINARY_OP 387 +#define _GUARD_IS_TRUE_POP 388 +#define _GUARD_IS_FALSE_POP 389 +#define _GUARD_IS_NONE_POP 390 +#define _GUARD_IS_NOT_NONE_POP 391 +#define _JUMP_TO_TOP 392 +#define _SAVE_RETURN_OFFSET 393 +#define _INSERT 394 +#define _CHECK_VALIDITY 395 extern int _PyOpcode_num_popped(int opcode, int oparg, bool jump); #ifdef NEED_OPCODE_METADATA @@ -543,6 +544,8 @@ int _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 1; case _FOR_ITER: return 1; + case _FOR_ITER_TIER_TWO: + return 1; case FOR_ITER: return 1; case INSTRUMENTED_FOR_ITER: @@ -1181,6 +1184,8 @@ int _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return 1; case _FOR_ITER: return 2; + case _FOR_ITER_TIER_TWO: + return 2; case FOR_ITER: return 2; case INSTRUMENTED_FOR_ITER: @@ -1676,6 +1681,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE] = { [GET_YIELD_FROM_ITER] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [_SPECIALIZE_FOR_ITER] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_ESCAPES_FLAG }, [_FOR_ITER] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, + [_FOR_ITER_TIER_TWO] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [FOR_ITER] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [INSTRUMENTED_FOR_ITER] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [_ITER_CHECK_LIST] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG }, @@ -1906,6 +1912,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN [MATCH_KEYS] = { .nuops = 1, .uops = { { MATCH_KEYS, 0, 0 } } }, [GET_ITER] = { .nuops = 1, .uops = { { GET_ITER, 0, 0 } } }, [GET_YIELD_FROM_ITER] = { .nuops = 1, .uops = { { GET_YIELD_FROM_ITER, 0, 0 } } }, + [FOR_ITER] = { .nuops = 1, .uops = { { _FOR_ITER, 0, 0 } } }, [FOR_ITER_LIST] = { .nuops = 3, .uops = { { _ITER_CHECK_LIST, 0, 0 }, { _ITER_JUMP_LIST, 0, 0 }, { _ITER_NEXT_LIST, 0, 0 } } }, [FOR_ITER_TUPLE] = { .nuops = 3, .uops = { { _ITER_CHECK_TUPLE, 0, 0 }, { _ITER_JUMP_TUPLE, 0, 0 }, { _ITER_NEXT_TUPLE, 0, 0 } } }, [FOR_ITER_RANGE] = { .nuops = 3, .uops = { { _ITER_CHECK_RANGE, 0, 0 }, { _ITER_JUMP_RANGE, 0, 0 }, { _ITER_NEXT_RANGE, 0, 0 } } }, @@ -2005,6 +2012,7 @@ const char * const _PyOpcode_uop_name[OPCODE_UOP_NAME_SIZE] = { [_IS_NONE] = "_IS_NONE", [_SPECIALIZE_FOR_ITER] = "_SPECIALIZE_FOR_ITER", [_FOR_ITER] = "_FOR_ITER", + [_FOR_ITER_TIER_TWO] = "_FOR_ITER_TIER_TWO", [_ITER_CHECK_LIST] = "_ITER_CHECK_LIST", [_ITER_JUMP_LIST] = "_ITER_JUMP_LIST", [_GUARD_NOT_EXHAUSTED_LIST] = "_GUARD_NOT_EXHAUSTED_LIST", diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index fe5c36c0c0dec9..21a5cd3326d707 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2808,6 +2808,36 @@ def testfunc(n): uops = {opname for opname, _, _ in ex} self.assertIn("_GUARD_IS_FALSE_POP", uops) + def test_for_iter_tier_two(self): + class MyIter: + def __init__(self, n): + self.n = n + def __iter__(self): + return self + def __next__(self): + self.n -= 1 + if self.n < 0: + raise StopIteration + return self.n + + def testfunc(n, m): + x = 0 + for i in range(m): + for j in MyIter(n): + x += 1000*i + j + return x + + opt = _testinternalcapi.get_uop_optimizer() + with temporary_optimizer(opt): + x = testfunc(10, 10) + + self.assertEqual(x, sum(range(10)) * 10010) + + ex = get_first_executor(testfunc) + self.assertIsNotNone(ex) + uops = {opname for opname, _, _ in ex} + self.assertIn("_FOR_ITER_TIER_TWO", uops) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-15-16-14-10.gh-issue-106529.Y48ax9.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-15-16-14-10.gh-issue-106529.Y48ax9.rst new file mode 100644 index 00000000000000..b2a34ac735cdeb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-15-16-14-10.gh-issue-106529.Y48ax9.rst @@ -0,0 +1 @@ +Enable translating unspecialized ``FOR_ITER`` to Tier 2. diff --git a/Python/abstract_interp_cases.c.h b/Python/abstract_interp_cases.c.h index a2f6aa8def8f69..0d7fbe8a39a5d4 100644 --- a/Python/abstract_interp_cases.c.h +++ b/Python/abstract_interp_cases.c.h @@ -624,6 +624,12 @@ break; } + case _FOR_ITER_TIER_TWO: { + STACK_GROW(1); + PARTITIONNODE_OVERWRITE((_Py_PARTITIONNODE_t *)PARTITIONNODE_NULLROOT, PEEK(-(-1)), true); + break; + } + case _ITER_CHECK_LIST: { break; } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index da98630f53943a..a1ca66e457e47d 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2369,7 +2369,7 @@ dummy_func( goto enter_tier_one; } - replaced op(_POP_JUMP_IF_FALSE, (unused/1, cond -- )) { + replaced op(_POP_JUMP_IF_FALSE, (unused/1, cond -- )) { assert(PyBool_Check(cond)); int flag = Py_IsFalse(cond); #if ENABLE_SPECIALIZATION @@ -2513,7 +2513,7 @@ dummy_func( #endif /* ENABLE_SPECIALIZATION */ } - op(_FOR_ITER, (iter -- iter, next)) { + replaced op(_FOR_ITER, (iter -- iter, next)) { /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */ next = (*Py_TYPE(iter)->tp_iternext)(iter); if (next == NULL) { @@ -2536,6 +2536,25 @@ dummy_func( // Common case: no jump, leave it to the code generator } + op(_FOR_ITER_TIER_TWO, (iter -- iter, next)) { + /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */ + next = (*Py_TYPE(iter)->tp_iternext)(iter); + if (next == NULL) { + if (_PyErr_Occurred(tstate)) { + if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { + GOTO_ERROR(error); + } + _PyErr_Clear(tstate); + } + /* iterator ended normally */ + Py_DECREF(iter); + STACK_SHRINK(1); + /* The translator sets the deopt target just past END_FOR */ + DEOPT_IF(true); + } + // Common case: no jump, leave it to the code generator + } + macro(FOR_ITER) = _SPECIALIZE_FOR_ITER + _FOR_ITER; inst(INSTRUMENTED_FOR_ITER, (unused/1 -- )) { diff --git a/Python/ceval.c b/Python/ceval.c index ba234dc5774472..b5e85930c81c9e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1074,7 +1074,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int UOP_STAT_INC(opcode, miss); frame->return_offset = 0; // Dispatch to frame->instr_ptr _PyFrame_SetStackPointer(frame, stack_pointer); - frame->instr_ptr = next_uop[-1].target + _PyCode_CODE((PyCodeObject *)frame->f_executable); + frame->instr_ptr = next_uop[-1].target + _PyCode_CODE(_PyFrame_GetCode(frame)); Py_DECREF(current_executor); // Fall through // Jump here from ENTER_EXECUTOR @@ -1085,7 +1085,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int // Jump here from _EXIT_TRACE exit_trace: _PyFrame_SetStackPointer(frame, stack_pointer); - frame->instr_ptr = next_uop[-1].target + _PyCode_CODE((PyCodeObject *)frame->f_executable); + frame->instr_ptr = next_uop[-1].target + _PyCode_CODE(_PyFrame_GetCode(frame)); Py_DECREF(current_executor); OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); goto enter_tier_one; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 4e29fb9f0fa93d..ae662b20e4403f 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -2101,6 +2101,31 @@ break; } + case _FOR_ITER_TIER_TWO: { + PyObject *iter; + PyObject *next; + iter = stack_pointer[-1]; + /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */ + next = (*Py_TYPE(iter)->tp_iternext)(iter); + if (next == NULL) { + if (_PyErr_Occurred(tstate)) { + if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { + GOTO_ERROR(error); + } + _PyErr_Clear(tstate); + } + /* iterator ended normally */ + Py_DECREF(iter); + STACK_SHRINK(1); + /* The translator sets the deopt target just past END_FOR */ + DEOPT_IF(true, _FOR_ITER_TIER_TWO); + } + // Common case: no jump, leave it to the code generator + STACK_GROW(1); + stack_pointer[-1] = next; + break; + } + case _ITER_CHECK_LIST: { PyObject *iter; iter = stack_pointer[-1]; diff --git a/Python/optimizer.c b/Python/optimizer.c index 64a15e0dd6696e..261a5ffd1e22e0 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -392,6 +392,7 @@ _PyUop_Replacements[OPCODE_METADATA_SIZE] = { [_ITER_JUMP_RANGE] = _GUARD_NOT_EXHAUSTED_RANGE, [_ITER_JUMP_LIST] = _GUARD_NOT_EXHAUSTED_LIST, [_ITER_JUMP_TUPLE] = _GUARD_NOT_EXHAUSTED_TUPLE, + [_FOR_ITER] = _FOR_ITER_TIER_TWO, }; static const uint16_t @@ -620,6 +621,11 @@ translate_bytecode_to_trace( } if (_PyUop_Replacements[uop]) { uop = _PyUop_Replacements[uop]; + if (uop == _FOR_ITER_TIER_TWO) { + target += 1 + INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1; + assert(_PyCode_CODE(code)[target-1].op.code == END_FOR || + _PyCode_CODE(code)[target-1].op.code == INSTRUMENTED_END_FOR); + } } break; case OPARG_CACHE_1: From c4c63211e83aa50927f3f1e57eacfaf4952ed228 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 20 Nov 2023 10:45:42 -0800 Subject: [PATCH 019/228] gh-111848: Clean up RESERVE() macro (#112274) Also avoid compiler warnings about unused 'reserved' variable. --- Python/optimizer.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index 261a5ffd1e22e0..ec59fea26fbc70 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -426,7 +426,6 @@ translate_bytecode_to_trace( _Py_CODEUNIT *initial_instr = instr; int trace_length = 0; int max_length = buffer_size; - int reserved = 0; struct { PyCodeObject *code; _Py_CODEUNIT *instr; @@ -456,8 +455,6 @@ translate_bytecode_to_trace( (OPARG), \ (uint64_t)(OPERAND)); \ assert(trace_length < max_length); \ - assert(reserved > 0); \ - reserved--; \ trace[trace_length].opcode = (OPCODE); \ trace[trace_length].oparg = (OPARG); \ trace[trace_length].operand = (OPERAND); \ @@ -474,11 +471,10 @@ translate_bytecode_to_trace( (opname), (n), max_length - trace_length); \ OPT_STAT_INC(trace_too_long); \ goto done; \ - } \ - reserved = (n); // Keep ADD_TO_TRACE honest + } -// Reserve space for main+stub uops, plus 3 for _SET_IP, _CHECK_VALIDITY and _EXIT_TRACE -#define RESERVE(main, stub) RESERVE_RAW((main) + (stub) + 3, _PyUopName(opcode)) +// Reserve space for N uops, plus 3 for _SET_IP, _CHECK_VALIDITY and _EXIT_TRACE +#define RESERVE(needed) RESERVE_RAW((needed) + 3, _PyUopName(opcode)) // Trace stack operations (used by _PUSH_FRAME, _POP_FRAME) #define TRACE_STACK_PUSH() \ @@ -543,7 +539,7 @@ translate_bytecode_to_trace( case POP_JUMP_IF_FALSE: case POP_JUMP_IF_TRUE: { - RESERVE(1, 0); + RESERVE(1); int counter = instr[1].cache; int bitcount = _Py_popcount32(counter); int jump_likely = bitcount > 8; @@ -566,7 +562,7 @@ translate_bytecode_to_trace( case JUMP_BACKWARD: { if (instr + 2 - oparg == initial_instr && code == initial_code) { - RESERVE(1, 0); + RESERVE(1); ADD_TO_TRACE(_JUMP_TO_TOP, 0, 0, 0); } else { @@ -578,7 +574,7 @@ translate_bytecode_to_trace( case JUMP_FORWARD: { - RESERVE(0, 0); + RESERVE(0); // This will emit two _SET_IP instructions; leave it to the optimizer instr += oparg; break; @@ -590,7 +586,7 @@ translate_bytecode_to_trace( if (expansion->nuops > 0) { // Reserve space for nuops (+ _SET_IP + _EXIT_TRACE) int nuops = expansion->nuops; - RESERVE(nuops, 0); + RESERVE(nuops); if (expansion->uops[nuops-1].uop == _POP_FRAME) { // Check for trace stack underflow now: // We can't bail e.g. in the middle of @@ -737,13 +733,12 @@ translate_bytecode_to_trace( if (trace_length > 4) { ADD_TO_TRACE(_EXIT_TRACE, 0, 0, target); DPRINTF(1, - "Created a trace for %s (%s:%d) at byte offset %d -- length %d+%d\n", + "Created a trace for %s (%s:%d) at byte offset %d -- length %d\n", PyUnicode_AsUTF8(code->co_qualname), PyUnicode_AsUTF8(code->co_filename), code->co_firstlineno, 2 * INSTR_IP(initial_instr, code), - trace_length, - buffer_size - max_length); + trace_length); OPT_HIST(trace_length + buffer_size - max_length, trace_length_hist); return 1; } From 8deb8bc2e5af0e229df87002ee8e0b0c1383f572 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 20 Nov 2023 11:25:32 -0800 Subject: [PATCH 020/228] gh-112287: Speed up Tier 2 (uop) interpreter a little (#112286) This makes the Tier 2 interpreter a little faster. I calculated by about 3%, though I hesitate to claim an exact number. This starts by doubling the trace size limit (to 512), making it more likely that loops fit in a trace. The rest of the approach is to only load `oparg` and `operand` in cases that use them. The code generator know when these are used. For `oparg`, it will conditionally emit ``` oparg = CURRENT_OPARG(); ``` at the top of the case block. (The `oparg` variable may be referenced multiple times by the instructions code block, so it must be in a variable.) For `operand`, it will use `CURRENT_OPERAND()` directly instead of referencing the `operand` variable, which no longer exists. (There is only one place where this will be used.) --- Include/internal/pycore_uops.h | 2 +- ...-11-20-10-40-40.gh-issue-112287.15gWAK.rst | 3 + Python/ceval.c | 16 +-- Python/ceval_macros.h | 4 + Python/executor_cases.c.h | 135 +++++++++++++++--- Tools/cases_generator/generate_cases.py | 2 + Tools/cases_generator/instructions.py | 2 +- 7 files changed, 131 insertions(+), 33 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-20-10-40-40.gh-issue-112287.15gWAK.rst diff --git a/Include/internal/pycore_uops.h b/Include/internal/pycore_uops.h index 8ab9aaf4108079..e2b94894681f44 100644 --- a/Include/internal/pycore_uops.h +++ b/Include/internal/pycore_uops.h @@ -10,7 +10,7 @@ extern "C" { #include "pycore_frame.h" // _PyInterpreterFrame -#define _Py_UOP_MAX_TRACE_LENGTH 256 +#define _Py_UOP_MAX_TRACE_LENGTH 512 typedef struct { uint16_t opcode; diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-20-10-40-40.gh-issue-112287.15gWAK.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-20-10-40-40.gh-issue-112287.15gWAK.rst new file mode 100644 index 00000000000000..3f31a0f55ca44e --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-20-10-40-40.gh-issue-112287.15gWAK.rst @@ -0,0 +1,3 @@ +Slightly optimize the Tier 2 (uop) interpreter by only loading ``oparg`` and +``operand`` when needed. Also double the trace size limit again, to 512 this +time. diff --git a/Python/ceval.c b/Python/ceval.c index b5e85930c81c9e..b19b746834278a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -994,21 +994,18 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int OPT_STAT_INC(traces_executed); _PyUOpInstruction *next_uop = current_executor->trace; - uint64_t operand; #ifdef Py_STATS uint64_t trace_uop_execution_counter = 0; #endif for (;;) { opcode = next_uop->opcode; - oparg = next_uop->oparg; - operand = next_uop->operand; DPRINTF(3, "%4d: uop %s, oparg %d, operand %" PRIu64 ", target %d, stack_level %d\n", (int)(next_uop - current_executor->trace), _PyUopName(opcode), - oparg, - operand, + next_uop->oparg, + next_uop->operand, next_uop->target, (int)(stack_pointer - _PyFrame_Stackbase(frame))); next_uop++; @@ -1025,8 +1022,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int default: #ifdef Py_DEBUG { - fprintf(stderr, "Unknown uop %d, oparg %d, operand %" PRIu64 "\n", - opcode, oparg, operand); + fprintf(stderr, "Unknown uop %d, oparg %d, operand %" PRIu64 " @ %d\n", + opcode, next_uop[-1].oparg, next_uop[-1].operand, + (int)(next_uop - current_executor->trace - 1)); Py_FatalError("Unknown uop"); } #else @@ -1055,7 +1053,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int STACK_SHRINK(1); error_tier_two: DPRINTF(2, "Error: [Uop %d (%s), oparg %d, operand %" PRIu64 ", target %d @ %d]\n", - opcode, _PyUopName(opcode), oparg, operand, next_uop[-1].target, + opcode, _PyUopName(opcode), next_uop[-1].oparg, next_uop[-1].operand, next_uop[-1].target, (int)(next_uop - current_executor->trace - 1)); OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); frame->return_offset = 0; // Don't leave this random @@ -1068,7 +1066,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int // On DEOPT_IF we just repeat the last instruction. // This presumes nothing was popped from the stack (nor pushed). DPRINTF(2, "DEOPT: [Uop %d (%s), oparg %d, operand %" PRIu64 ", target %d @ %d]\n", - opcode, _PyUopName(opcode), oparg, operand, next_uop[-1].target, + opcode, _PyUopName(opcode), next_uop[-1].oparg, next_uop[-1].operand, next_uop[-1].target, (int)(next_uop - current_executor->trace - 1)); OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); UOP_STAT_INC(opcode, miss); diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 546adbe5f438d1..b0cb7c8926338c 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -397,3 +397,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); #define GOTO_TIER_TWO() goto enter_tier_two; #define GOTO_TIER_ONE() goto exit_trace; + +#define CURRENT_OPARG() (next_uop[-1].oparg) + +#define CURRENT_OPERAND() (next_uop[-1].operand) diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index ae662b20e4403f..547be6f13237dd 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -25,6 +25,7 @@ } case LOAD_FAST_CHECK: { + oparg = CURRENT_OPARG(); PyObject *value; value = GETLOCAL(oparg); if (value == NULL) goto unbound_local_error_tier_two; @@ -35,6 +36,7 @@ } case LOAD_FAST: { + oparg = CURRENT_OPARG(); PyObject *value; value = GETLOCAL(oparg); assert(value != NULL); @@ -45,6 +47,7 @@ } case LOAD_FAST_AND_CLEAR: { + oparg = CURRENT_OPARG(); PyObject *value; value = GETLOCAL(oparg); // do not use SETLOCAL here, it decrefs the old value @@ -55,6 +58,7 @@ } case LOAD_CONST: { + oparg = CURRENT_OPARG(); PyObject *value; value = GETITEM(FRAME_CO_CONSTS, oparg); Py_INCREF(value); @@ -64,6 +68,7 @@ } case STORE_FAST: { + oparg = CURRENT_OPARG(); PyObject *value; value = stack_pointer[-1]; SETLOCAL(oparg, value); @@ -204,7 +209,7 @@ PyObject *value; PyObject *res; value = stack_pointer[-1]; - uint32_t version = (uint32_t)operand; + uint32_t version = (uint32_t)CURRENT_OPERAND(); // This one is a bit weird, because we expect *some* failures: assert(version); DEOPT_IF(Py_TYPE(value)->tp_version_tag != version, TO_BOOL); @@ -525,6 +530,7 @@ } case LIST_APPEND: { + oparg = CURRENT_OPARG(); PyObject *v; PyObject *list; v = stack_pointer[-1]; @@ -535,6 +541,7 @@ } case SET_ADD: { + oparg = CURRENT_OPARG(); PyObject *v; PyObject *set; v = stack_pointer[-1]; @@ -621,6 +628,7 @@ } case CALL_INTRINSIC_1: { + oparg = CURRENT_OPARG(); PyObject *value; PyObject *res; value = stack_pointer[-1]; @@ -633,6 +641,7 @@ } case CALL_INTRINSIC_2: { + oparg = CURRENT_OPARG(); PyObject *value1; PyObject *value2; PyObject *res; @@ -764,6 +773,7 @@ } case GET_AWAITABLE: { + oparg = CURRENT_OPARG(); PyObject *iterable; PyObject *iter; iterable = stack_pointer[-1]; @@ -825,6 +835,7 @@ } case STORE_NAME: { + oparg = CURRENT_OPARG(); PyObject *v; v = stack_pointer[-1]; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); @@ -847,6 +858,7 @@ } case DELETE_NAME: { + oparg = CURRENT_OPARG(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); PyObject *ns = LOCALS(); int err; @@ -867,6 +879,7 @@ } case _UNPACK_SEQUENCE: { + oparg = CURRENT_OPARG(); PyObject *seq; seq = stack_pointer[-1]; PyObject **top = stack_pointer + oparg - 1; @@ -879,6 +892,7 @@ } case UNPACK_SEQUENCE_TWO_TUPLE: { + oparg = CURRENT_OPARG(); PyObject *seq; PyObject **values; seq = stack_pointer[-1]; @@ -896,6 +910,7 @@ } case UNPACK_SEQUENCE_TUPLE: { + oparg = CURRENT_OPARG(); PyObject *seq; PyObject **values; seq = stack_pointer[-1]; @@ -914,6 +929,7 @@ } case UNPACK_SEQUENCE_LIST: { + oparg = CURRENT_OPARG(); PyObject *seq; PyObject **values; seq = stack_pointer[-1]; @@ -932,6 +948,7 @@ } case UNPACK_EX: { + oparg = CURRENT_OPARG(); PyObject *seq; seq = stack_pointer[-1]; int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8); @@ -944,6 +961,7 @@ } case _STORE_ATTR: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *v; owner = stack_pointer[-1]; @@ -958,6 +976,7 @@ } case DELETE_ATTR: { + oparg = CURRENT_OPARG(); PyObject *owner; owner = stack_pointer[-1]; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); @@ -969,6 +988,7 @@ } case STORE_GLOBAL: { + oparg = CURRENT_OPARG(); PyObject *v; v = stack_pointer[-1]; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); @@ -980,6 +1000,7 @@ } case DELETE_GLOBAL: { + oparg = CURRENT_OPARG(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); int err; err = PyDict_DelItem(GLOBALS(), name); @@ -1009,6 +1030,7 @@ } case LOAD_FROM_DICT_OR_GLOBALS: { + oparg = CURRENT_OPARG(); PyObject *mod_or_class_dict; PyObject *v; mod_or_class_dict = stack_pointer[-1]; @@ -1038,6 +1060,7 @@ } case LOAD_NAME: { + oparg = CURRENT_OPARG(); PyObject *v; PyObject *mod_or_class_dict = LOCALS(); if (mod_or_class_dict == NULL) { @@ -1071,6 +1094,7 @@ } case _LOAD_GLOBAL: { + oparg = CURRENT_OPARG(); PyObject *res; PyObject *null = NULL; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); @@ -1115,7 +1139,7 @@ } case _GUARD_GLOBALS_VERSION: { - uint16_t version = (uint16_t)operand; + uint16_t version = (uint16_t)CURRENT_OPERAND(); PyDictObject *dict = (PyDictObject *)GLOBALS(); DEOPT_IF(!PyDict_CheckExact(dict), _GUARD_GLOBALS_VERSION); DEOPT_IF(dict->ma_keys->dk_version != version, _GUARD_GLOBALS_VERSION); @@ -1124,7 +1148,7 @@ } case _GUARD_BUILTINS_VERSION: { - uint16_t version = (uint16_t)operand; + uint16_t version = (uint16_t)CURRENT_OPERAND(); PyDictObject *dict = (PyDictObject *)BUILTINS(); DEOPT_IF(!PyDict_CheckExact(dict), _GUARD_BUILTINS_VERSION); DEOPT_IF(dict->ma_keys->dk_version != version, _GUARD_BUILTINS_VERSION); @@ -1133,9 +1157,10 @@ } case _LOAD_GLOBAL_MODULE: { + oparg = CURRENT_OPARG(); PyObject *res; PyObject *null = NULL; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)CURRENT_OPERAND(); PyDictObject *dict = (PyDictObject *)GLOBALS(); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys); res = entries[index].me_value; @@ -1151,9 +1176,10 @@ } case _LOAD_GLOBAL_BUILTINS: { + oparg = CURRENT_OPARG(); PyObject *res; PyObject *null = NULL; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)CURRENT_OPERAND(); PyDictObject *bdict = (PyDictObject *)BUILTINS(); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(bdict->ma_keys); res = entries[index].me_value; @@ -1169,6 +1195,7 @@ } case DELETE_FAST: { + oparg = CURRENT_OPARG(); PyObject *v = GETLOCAL(oparg); if (v == NULL) goto unbound_local_error_tier_two; SETLOCAL(oparg, NULL); @@ -1176,6 +1203,7 @@ } case MAKE_CELL: { + oparg = CURRENT_OPARG(); // "initial" is probably NULL but not if it's an arg (or set // via PyFrame_LocalsToFast() before MAKE_CELL has run). PyObject *initial = GETLOCAL(oparg); @@ -1188,6 +1216,7 @@ } case DELETE_DEREF: { + oparg = CURRENT_OPARG(); PyObject *cell = GETLOCAL(oparg); PyObject *oldobj = PyCell_GET(cell); // Can't use ERROR_IF here. @@ -1202,6 +1231,7 @@ } case LOAD_FROM_DICT_OR_DEREF: { + oparg = CURRENT_OPARG(); PyObject *class_dict; PyObject *value; class_dict = stack_pointer[-1]; @@ -1227,6 +1257,7 @@ } case LOAD_DEREF: { + oparg = CURRENT_OPARG(); PyObject *value; PyObject *cell = GETLOCAL(oparg); value = PyCell_GET(cell); @@ -1241,6 +1272,7 @@ } case STORE_DEREF: { + oparg = CURRENT_OPARG(); PyObject *v; v = stack_pointer[-1]; PyObject *cell = GETLOCAL(oparg); @@ -1252,6 +1284,7 @@ } case COPY_FREE_VARS: { + oparg = CURRENT_OPARG(); /* Copy closure variables to free variables */ PyCodeObject *co = _PyFrame_GetCode(frame); assert(PyFunction_Check(frame->f_funcobj)); @@ -1266,6 +1299,7 @@ } case BUILD_STRING: { + oparg = CURRENT_OPARG(); PyObject **pieces; PyObject *str; pieces = stack_pointer - oparg; @@ -1281,6 +1315,7 @@ } case BUILD_TUPLE: { + oparg = CURRENT_OPARG(); PyObject **values; PyObject *tup; values = stack_pointer - oparg; @@ -1293,6 +1328,7 @@ } case BUILD_LIST: { + oparg = CURRENT_OPARG(); PyObject **values; PyObject *list; values = stack_pointer - oparg; @@ -1305,6 +1341,7 @@ } case LIST_EXTEND: { + oparg = CURRENT_OPARG(); PyObject *iterable; PyObject *list; iterable = stack_pointer[-1]; @@ -1329,6 +1366,7 @@ } case SET_UPDATE: { + oparg = CURRENT_OPARG(); PyObject *iterable; PyObject *set; iterable = stack_pointer[-1]; @@ -1341,6 +1379,7 @@ } case BUILD_SET: { + oparg = CURRENT_OPARG(); PyObject **values; PyObject *set; values = stack_pointer - oparg; @@ -1365,6 +1404,7 @@ } case BUILD_MAP: { + oparg = CURRENT_OPARG(); PyObject **values; PyObject *map; values = stack_pointer - oparg*2; @@ -1407,6 +1447,7 @@ } case BUILD_CONST_KEY_MAP: { + oparg = CURRENT_OPARG(); PyObject *keys; PyObject **values; PyObject *map; @@ -1432,6 +1473,7 @@ } case DICT_UPDATE: { + oparg = CURRENT_OPARG(); PyObject *update; PyObject *dict; update = stack_pointer[-1]; @@ -1451,6 +1493,7 @@ } case DICT_MERGE: { + oparg = CURRENT_OPARG(); PyObject *update; PyObject *dict; PyObject *callable; @@ -1468,6 +1511,7 @@ } case MAP_ADD: { + oparg = CURRENT_OPARG(); PyObject *value; PyObject *key; PyObject *dict; @@ -1483,6 +1527,7 @@ } case LOAD_SUPER_ATTR_ATTR: { + oparg = CURRENT_OPARG(); PyObject *self; PyObject *class; PyObject *global_super; @@ -1506,6 +1551,7 @@ } case LOAD_SUPER_ATTR_METHOD: { + oparg = CURRENT_OPARG(); PyObject *self; PyObject *class; PyObject *global_super; @@ -1542,6 +1588,7 @@ } case _LOAD_ATTR: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *self_or_null = NULL; @@ -1585,7 +1632,7 @@ case _GUARD_TYPE_VERSION: { PyObject *owner; owner = stack_pointer[-1]; - uint32_t type_version = (uint32_t)operand; + uint32_t type_version = (uint32_t)CURRENT_OPERAND(); PyTypeObject *tp = Py_TYPE(owner); assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, _GUARD_TYPE_VERSION); @@ -1603,11 +1650,12 @@ } case _LOAD_ATTR_INSTANCE_VALUE: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *null = NULL; owner = stack_pointer[-1]; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)CURRENT_OPERAND(); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); attr = _PyDictOrValues_GetValues(dorv)->values[index]; DEOPT_IF(attr == NULL, _LOAD_ATTR_INSTANCE_VALUE); @@ -1624,7 +1672,7 @@ case _CHECK_ATTR_MODULE: { PyObject *owner; owner = stack_pointer[-1]; - uint32_t type_version = (uint32_t)operand; + uint32_t type_version = (uint32_t)CURRENT_OPERAND(); DEOPT_IF(!PyModule_CheckExact(owner), _CHECK_ATTR_MODULE); PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; assert(dict != NULL); @@ -1633,11 +1681,12 @@ } case _LOAD_ATTR_MODULE: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *null = NULL; owner = stack_pointer[-1]; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)CURRENT_OPERAND(); PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); assert(index < dict->ma_keys->dk_nentries); @@ -1667,11 +1716,12 @@ } case _LOAD_ATTR_WITH_HINT: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *null = NULL; owner = stack_pointer[-1]; - uint16_t hint = (uint16_t)operand; + uint16_t hint = (uint16_t)CURRENT_OPERAND(); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, _LOAD_ATTR_WITH_HINT); @@ -1698,11 +1748,12 @@ } case _LOAD_ATTR_SLOT: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *null = NULL; owner = stack_pointer[-1]; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)CURRENT_OPERAND(); char *addr = (char *)owner + index; attr = *(PyObject **)addr; DEOPT_IF(attr == NULL, _LOAD_ATTR_SLOT); @@ -1719,7 +1770,7 @@ case _CHECK_ATTR_CLASS: { PyObject *owner; owner = stack_pointer[-1]; - uint32_t type_version = (uint32_t)operand; + uint32_t type_version = (uint32_t)CURRENT_OPERAND(); DEOPT_IF(!PyType_Check(owner), _CHECK_ATTR_CLASS); assert(type_version != 0); DEOPT_IF(((PyTypeObject *)owner)->tp_version_tag != type_version, _CHECK_ATTR_CLASS); @@ -1727,11 +1778,12 @@ } case _LOAD_ATTR_CLASS: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *null = NULL; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)CURRENT_OPERAND(); STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); attr = Py_NewRef(descr); @@ -1757,7 +1809,7 @@ PyObject *value; owner = stack_pointer[-1]; value = stack_pointer[-2]; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)CURRENT_OPERAND(); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); STAT_INC(STORE_ATTR, hit); PyDictValues *values = _PyDictOrValues_GetValues(dorv); @@ -1779,7 +1831,7 @@ PyObject *value; owner = stack_pointer[-1]; value = stack_pointer[-2]; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)CURRENT_OPERAND(); char *addr = (char *)owner + index; STAT_INC(STORE_ATTR, hit); PyObject *old_value = *(PyObject **)addr; @@ -1791,6 +1843,7 @@ } case _COMPARE_OP: { + oparg = CURRENT_OPARG(); PyObject *right; PyObject *left; PyObject *res; @@ -1813,6 +1866,7 @@ } case COMPARE_OP_FLOAT: { + oparg = CURRENT_OPARG(); PyObject *right; PyObject *left; PyObject *res; @@ -1835,6 +1889,7 @@ } case COMPARE_OP_INT: { + oparg = CURRENT_OPARG(); PyObject *right; PyObject *left; PyObject *res; @@ -1861,6 +1916,7 @@ } case COMPARE_OP_STR: { + oparg = CURRENT_OPARG(); PyObject *right; PyObject *left; PyObject *res; @@ -1884,6 +1940,7 @@ } case IS_OP: { + oparg = CURRENT_OPARG(); PyObject *right; PyObject *left; PyObject *b; @@ -1899,6 +1956,7 @@ } case CONTAINS_OP: { + oparg = CURRENT_OPARG(); PyObject *right; PyObject *left; PyObject *b; @@ -1995,6 +2053,7 @@ } case MATCH_CLASS: { + oparg = CURRENT_OPARG(); PyObject *names; PyObject *type; PyObject *subject; @@ -2378,7 +2437,7 @@ case _GUARD_KEYS_VERSION: { PyObject *owner; owner = stack_pointer[-1]; - uint32_t keys_version = (uint32_t)operand; + uint32_t keys_version = (uint32_t)CURRENT_OPERAND(); PyTypeObject *owner_cls = Py_TYPE(owner); PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version, _GUARD_KEYS_VERSION); @@ -2386,11 +2445,12 @@ } case _LOAD_ATTR_METHOD_WITH_VALUES: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *self; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)CURRENT_OPERAND(); assert(oparg & 1); /* Cached method object */ STAT_INC(LOAD_ATTR, hit); @@ -2405,11 +2465,12 @@ } case _LOAD_ATTR_METHOD_NO_DICT: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *self; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)CURRENT_OPERAND(); assert(oparg & 1); assert(Py_TYPE(owner)->tp_dictoffset == 0); STAT_INC(LOAD_ATTR, hit); @@ -2424,10 +2485,11 @@ } case _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)CURRENT_OPERAND(); assert((oparg & 1) == 0); STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); @@ -2438,10 +2500,11 @@ } case _LOAD_ATTR_NONDESCRIPTOR_NO_DICT: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)CURRENT_OPERAND(); assert((oparg & 1) == 0); assert(Py_TYPE(owner)->tp_dictoffset == 0); STAT_INC(LOAD_ATTR, hit); @@ -2464,11 +2527,12 @@ } case _LOAD_ATTR_METHOD_LAZY_DICT: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *self; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)CURRENT_OPERAND(); assert(oparg & 1); STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); @@ -2482,6 +2546,7 @@ } case _CHECK_CALL_BOUND_METHOD_EXACT_ARGS: { + oparg = CURRENT_OPARG(); PyObject *null; PyObject *callable; null = stack_pointer[-1 - oparg]; @@ -2492,6 +2557,7 @@ } case _INIT_CALL_BOUND_METHOD_EXACT_ARGS: { + oparg = CURRENT_OPARG(); PyObject *callable; PyObject *func; PyObject *self; @@ -2513,11 +2579,12 @@ } case _CHECK_FUNCTION_EXACT_ARGS: { + oparg = CURRENT_OPARG(); PyObject *self_or_null; PyObject *callable; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; - uint32_t func_version = (uint32_t)operand; + uint32_t func_version = (uint32_t)CURRENT_OPERAND(); DEOPT_IF(!PyFunction_Check(callable), _CHECK_FUNCTION_EXACT_ARGS); PyFunctionObject *func = (PyFunctionObject *)callable; DEOPT_IF(func->func_version != func_version, _CHECK_FUNCTION_EXACT_ARGS); @@ -2527,6 +2594,7 @@ } case _CHECK_STACK_SPACE: { + oparg = CURRENT_OPARG(); PyObject *callable; callable = stack_pointer[-2 - oparg]; PyFunctionObject *func = (PyFunctionObject *)callable; @@ -2537,6 +2605,7 @@ } case _INIT_CALL_PY_EXACT_ARGS: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2585,6 +2654,7 @@ } case CALL_TYPE_1: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *null; PyObject *callable; @@ -2607,6 +2677,7 @@ } case CALL_STR_1: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *null; PyObject *callable; @@ -2631,6 +2702,7 @@ } case CALL_TUPLE_1: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *null; PyObject *callable; @@ -2669,6 +2741,7 @@ } case CALL_BUILTIN_CLASS: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2700,6 +2773,7 @@ } case CALL_BUILTIN_O: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2739,6 +2813,7 @@ } case CALL_BUILTIN_FAST: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2782,6 +2857,7 @@ } case CALL_BUILTIN_FAST_WITH_KEYWORDS: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2819,6 +2895,7 @@ } case CALL_LEN: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2854,6 +2931,7 @@ } case CALL_ISINSTANCE: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2891,6 +2969,7 @@ } case CALL_METHOD_DESCRIPTOR_O: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2933,6 +3012,7 @@ } case CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2973,6 +3053,7 @@ } case CALL_METHOD_DESCRIPTOR_NOARGS: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -3014,6 +3095,7 @@ } case CALL_METHOD_DESCRIPTOR_FAST: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -3073,6 +3155,7 @@ } case SET_FUNCTION_ATTRIBUTE: { + oparg = CURRENT_OPARG(); PyObject *func; PyObject *attr; func = stack_pointer[-1]; @@ -3107,6 +3190,7 @@ } case BUILD_SLICE: { + oparg = CURRENT_OPARG(); PyObject *step = NULL; PyObject *stop; PyObject *start; @@ -3126,6 +3210,7 @@ } case CONVERT_VALUE: { + oparg = CURRENT_OPARG(); PyObject *value; PyObject *result; value = stack_pointer[-1]; @@ -3173,6 +3258,7 @@ } case COPY: { + oparg = CURRENT_OPARG(); PyObject *bottom; PyObject *top; bottom = stack_pointer[-1 - (oparg-1)]; @@ -3184,6 +3270,7 @@ } case _BINARY_OP: { + oparg = CURRENT_OPARG(); PyObject *rhs; PyObject *lhs; PyObject *res; @@ -3200,6 +3287,7 @@ } case SWAP: { + oparg = CURRENT_OPARG(); PyObject *top; PyObject *bottom; top = stack_pointer[-1]; @@ -3252,6 +3340,7 @@ } case _SET_IP: { + oparg = CURRENT_OPARG(); TIER_TWO_ONLY // TODO: Put the code pointer in `operand` to avoid indirection via `frame` frame->instr_ptr = _PyCode_CODE(_PyFrame_GetCode(frame)) + oparg; @@ -3259,6 +3348,7 @@ } case _SAVE_RETURN_OFFSET: { + oparg = CURRENT_OPARG(); #if TIER_ONE frame->return_offset = (uint16_t)(next_instr - this_instr); #endif @@ -3275,6 +3365,7 @@ } case _INSERT: { + oparg = CURRENT_OPARG(); PyObject *top; top = stack_pointer[-1]; // Inserts TOS at position specified by oparg; diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 149558e1640364..851bd2f53879e5 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -810,6 +810,8 @@ def write_executor_instructions( n_uops += 1 self.out.emit("") with self.out.block(f"case {instr.name}:"): + if instr.instr_flags.HAS_ARG_FLAG: + self.out.emit("oparg = CURRENT_OPARG();") stacking.write_single_instr(instr, self.out, tier=TIER_TWO) if instr.check_eval_breaker: self.out.emit("CHECK_EVAL_BREAKER();") diff --git a/Tools/cases_generator/instructions.py b/Tools/cases_generator/instructions.py index 9039ac5c6f127e..149a08810e4ae5 100644 --- a/Tools/cases_generator/instructions.py +++ b/Tools/cases_generator/instructions.py @@ -166,7 +166,7 @@ def write_body( f"{func}(&this_instr[{active.offset + 1}].cache);" ) else: - out.emit(f"{typ}{ceffect.name} = ({typ.strip()})operand;") + out.emit(f"{typ}{ceffect.name} = ({typ.strip()})CURRENT_OPERAND();") # Write the body, substituting a goto for ERROR_IF() and other stuff assert dedent <= 0 From 6a00a58f60a1ce18d32503f11b95d6fcf00c7406 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Mon, 20 Nov 2023 18:13:44 -0500 Subject: [PATCH 021/228] gh-111786: Use separate opcode vars for Tier 1 and Tier 2 (#112289) This makes Windows about 3% faster on pyperformance benchmarks. --- Python/ceval.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index b19b746834278a..76ab5df42f63db 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -689,7 +689,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #ifdef Py_STATS int lastopcode = 0; #endif - int opcode; /* Current opcode */ + uint8_t opcode; /* Current opcode */ int oparg; /* Current opcode argument, if any */ #ifdef LLTRACE int lltrace = 0; @@ -776,9 +776,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int /* Start instructions */ #if !USE_COMPUTED_GOTOS dispatch_opcode: - // Cast to an 8-bit value to improve the code generated by MSVC - // (in combination with the EXTRA_CASES macro). - switch ((uint8_t)opcode) + switch (opcode) #endif { @@ -822,7 +820,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #if USE_COMPUTED_GOTOS _unknown_opcode: #else - EXTRA_CASES // From pycore_opcode.h, a 'case' for each unused opcode + EXTRA_CASES // From pycore_opcode_metadata.h, a 'case' for each unused opcode #endif /* Tell C compilers not to hold the opcode variable in the loop. next_instr points the current instruction without TARGET(). */ @@ -994,28 +992,29 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int OPT_STAT_INC(traces_executed); _PyUOpInstruction *next_uop = current_executor->trace; + uint16_t uopcode; #ifdef Py_STATS uint64_t trace_uop_execution_counter = 0; #endif for (;;) { - opcode = next_uop->opcode; + uopcode = next_uop->opcode; DPRINTF(3, "%4d: uop %s, oparg %d, operand %" PRIu64 ", target %d, stack_level %d\n", (int)(next_uop - current_executor->trace), - _PyUopName(opcode), + _PyUopName(uopcode), next_uop->oparg, next_uop->operand, next_uop->target, (int)(stack_pointer - _PyFrame_Stackbase(frame))); next_uop++; OPT_STAT_INC(uops_executed); - UOP_STAT_INC(opcode, execution_count); + UOP_STAT_INC(uopcode, execution_count); #ifdef Py_STATS trace_uop_execution_counter++; #endif - switch (opcode) { + switch (uopcode) { #include "executor_cases.c.h" @@ -1053,7 +1052,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int STACK_SHRINK(1); error_tier_two: DPRINTF(2, "Error: [Uop %d (%s), oparg %d, operand %" PRIu64 ", target %d @ %d]\n", - opcode, _PyUopName(opcode), next_uop[-1].oparg, next_uop[-1].operand, next_uop[-1].target, + uopcode, _PyUopName(uopcode), next_uop[-1].oparg, next_uop[-1].operand, next_uop[-1].target, (int)(next_uop - current_executor->trace - 1)); OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); frame->return_offset = 0; // Don't leave this random @@ -1066,10 +1065,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int // On DEOPT_IF we just repeat the last instruction. // This presumes nothing was popped from the stack (nor pushed). DPRINTF(2, "DEOPT: [Uop %d (%s), oparg %d, operand %" PRIu64 ", target %d @ %d]\n", - opcode, _PyUopName(opcode), next_uop[-1].oparg, next_uop[-1].operand, next_uop[-1].target, + uopcode, _PyUopName(uopcode), next_uop[-1].oparg, next_uop[-1].operand, next_uop[-1].target, (int)(next_uop - current_executor->trace - 1)); OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); - UOP_STAT_INC(opcode, miss); + UOP_STAT_INC(uopcode, miss); frame->return_offset = 0; // Dispatch to frame->instr_ptr _PyFrame_SetStackPointer(frame, stack_pointer); frame->instr_ptr = next_uop[-1].target + _PyCode_CODE(_PyFrame_GetCode(frame)); From de2715f086bc799b20e420a82b76180338352565 Mon Sep 17 00:00:00 2001 From: AN Long Date: Tue, 21 Nov 2023 13:15:25 +0800 Subject: [PATCH 022/228] gh-59703: use the system dladdr function in getpath.c for macOS framework builds (GH-111546) Co-authored-by: Ned Deily --- ...023-10-31-22-13-05.gh-issue-59703.SML6Ag.rst | 4 ++++ Modules/getpath.c | 17 ++++++----------- 2 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/macOS/2023-10-31-22-13-05.gh-issue-59703.SML6Ag.rst diff --git a/Misc/NEWS.d/next/macOS/2023-10-31-22-13-05.gh-issue-59703.SML6Ag.rst b/Misc/NEWS.d/next/macOS/2023-10-31-22-13-05.gh-issue-59703.SML6Ag.rst new file mode 100644 index 00000000000000..eeb014dd92ba36 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-10-31-22-13-05.gh-issue-59703.SML6Ag.rst @@ -0,0 +1,4 @@ +For macOS framework builds, in ``getpath.c`` use the system ``dladdr`` +function to find the path to the shared library rather than depending +on deprecated macOS APIs. + diff --git a/Modules/getpath.c b/Modules/getpath.c index 50ba090ee548ca..be1a9cf9cc4782 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -17,7 +17,7 @@ #endif #ifdef __APPLE__ -# include +# include #endif /* Reference the precompiled getpath.py */ @@ -768,16 +768,11 @@ library_to_dict(PyObject *dict, const char *key) which is in the framework, not relative to the executable, which may be outside of the framework. Except when we're in the build directory... */ - NSSymbol symbol = NSLookupAndBindSymbol("_Py_Initialize"); - if (symbol != NULL) { - NSModule pythonModule = NSModuleForSymbol(symbol); - if (pythonModule != NULL) { - /* Use dylib functions to find out where the framework was loaded from */ - const char *path = NSLibraryNameForModule(pythonModule); - if (path) { - strncpy(modPath, path, MAXPATHLEN); - modPathInitialized = 1; - } + Dl_info pythonInfo; + if (dladdr(&Py_Initialize, &pythonInfo)) { + if (pythonInfo.dli_fname) { + strncpy(modPath, pythonInfo.dli_fname, MAXPATHLEN); + modPathInitialized = 1; } } } From d67f947c72af8a215db2fd285e5de9b1e671fde1 Mon Sep 17 00:00:00 2001 From: Christopher Chavez Date: Tue, 21 Nov 2023 02:12:19 -0600 Subject: [PATCH 023/228] gh-110950: add upstream Tk fixes to macOS installer. (GH-111041) Add upstream Tk patches for three problems affecting tkinter users: - Update macOS installer to include a fix accepted by upstream Tcl/Tk for a crash encountered after the first :meth:`tkinter.Tk` instance is destroyed. (gh-92603) - Update macOS installer to include an upstream Tcl/Tk fix for the ``ttk::ThemeChanged`` error encountered in Tkinter. (gh-71383) - Update macOS installer to include an upstream Tcl/Tk fix for the ``Secure coding is not enabled for restorable state!`` warning encountered in Tkinter on macOS 14 Sonoma. (gh-110950) Co-authored-by: Ned Deily --- Mac/BuildScript/backport_gh110950_fix.patch | 25 ++++++ Mac/BuildScript/backport_gh71383_fix.patch | 89 +++++++++++++++++++ Mac/BuildScript/backport_gh92603_fix.patch | 82 +++++++++++++++++ Mac/BuildScript/build-installer.py | 4 +- ...3-08-30-16-33-57.gh-issue-92603.ATkKVO.rst | 3 + ...3-09-02-08-49-57.gh-issue-71383.Ttkchg.rst | 2 + ...-10-18-17-26-36.gh-issue-110950.sonoma.rst | 3 + 7 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 Mac/BuildScript/backport_gh110950_fix.patch create mode 100644 Mac/BuildScript/backport_gh71383_fix.patch create mode 100644 Mac/BuildScript/backport_gh92603_fix.patch create mode 100644 Misc/NEWS.d/next/macOS/2023-08-30-16-33-57.gh-issue-92603.ATkKVO.rst create mode 100644 Misc/NEWS.d/next/macOS/2023-09-02-08-49-57.gh-issue-71383.Ttkchg.rst create mode 100644 Misc/NEWS.d/next/macOS/2023-10-18-17-26-36.gh-issue-110950.sonoma.rst diff --git a/Mac/BuildScript/backport_gh110950_fix.patch b/Mac/BuildScript/backport_gh110950_fix.patch new file mode 100644 index 00000000000000..793f5a78d8c6ab --- /dev/null +++ b/Mac/BuildScript/backport_gh110950_fix.patch @@ -0,0 +1,25 @@ +From https://core.tcl-lang.org/tk/info/ed7cfbac8db11aa0 + +Note: the diff here is hand-tweaked so that it applies cleanly to both Tk 8.6.8 and 8.6.13. + +diff --git a/macosx/tkMacOSXInit.c b/macosx/tkMacOSXInit.c +index 71d7c3385..e6a68356c 100644 +--- a/macosx/tkMacOSXInit.c ++++ b/macosx/tkMacOSXInit.c +@@ -128,6 +128,16 @@ static int TkMacOSXGetAppPathCmd(ClientData cd, Tcl_Interp *ip, + observe(NSApplicationDidChangeScreenParametersNotification, displayChanged:); + observe(NSTextInputContextKeyboardSelectionDidChangeNotification, keyboardChanged:); + #undef observe ++} ++ ++ ++/* ++ * Fix for 10b38a7a7c. ++ */ ++ ++- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app ++{ ++ return YES; + } + + -(void)applicationWillFinishLaunching:(NSNotification *)aNotification diff --git a/Mac/BuildScript/backport_gh71383_fix.patch b/Mac/BuildScript/backport_gh71383_fix.patch new file mode 100644 index 00000000000000..d77b1045e8c165 --- /dev/null +++ b/Mac/BuildScript/backport_gh71383_fix.patch @@ -0,0 +1,89 @@ +Adapted from https://core.tcl-lang.org/tk/info/b1876b9ebc4b + +Index: generic/tkInt.h +================================================================== +--- a/generic/tkInt.h.orig ++++ b/generic/tkInt.h +@@ -1094,10 +1094,11 @@ + /* + * Themed widget set init function: + */ + + MODULE_SCOPE int Ttk_Init(Tcl_Interp *interp); ++MODULE_SCOPE void Ttk_TkDestroyedHandler(Tcl_Interp *interp); + + /* + * Internal functions shared among Tk modules but not exported to the outside + * world: + */ + +Index: generic/tkWindow.c +================================================================== +--- a/generic/tkWindow.c.orig ++++ b/generic/tkWindow.c +@@ -1619,10 +1619,11 @@ + TkBindFree(winPtr->mainPtr); + TkDeleteAllImages(winPtr->mainPtr); + TkFontPkgFree(winPtr->mainPtr); + TkFocusFree(winPtr->mainPtr); + TkStylePkgFree(winPtr->mainPtr); ++ Ttk_TkDestroyedHandler(winPtr->mainPtr->interp); + + /* + * When embedding Tk into other applications, make sure that all + * destroy events reach the server. Otherwise the embedding + * application may also attempt to destroy the windows, resulting + +Index: generic/ttk/ttkTheme.c +================================================================== +--- a/generic/ttk/ttkTheme.c.orig ++++ b/generic/ttk/ttkTheme.c +@@ -415,17 +415,10 @@ + StylePackageData *pkgPtr = (StylePackageData *)clientData; + Tcl_HashSearch search; + Tcl_HashEntry *entryPtr; + Cleanup *cleanup; + +- /* +- * Cancel any pending ThemeChanged calls: +- */ +- if (pkgPtr->themeChangePending) { +- Tcl_CancelIdleCall(ThemeChangedProc, pkgPtr); +- } +- + /* + * Free themes. + */ + entryPtr = Tcl_FirstHashEntry(&pkgPtr->themeTable, &search); + while (entryPtr != NULL) { +@@ -528,10 +521,29 @@ + if (!pkgPtr->themeChangePending) { + Tcl_DoWhenIdle(ThemeChangedProc, pkgPtr); + pkgPtr->themeChangePending = 1; + } + } ++ ++/* Ttk_TkDestroyedHandler -- ++ * See bug [310c74ecf440]: idle calls to ThemeChangedProc() ++ * need to be canceled when Tk is destroyed, since the interp ++ * may still be active afterward; canceling them from ++ * Ttk_StylePkgFree() would be too late. ++ */ ++void Ttk_TkDestroyedHandler( ++ Tcl_Interp* interp) ++{ ++ StylePackageData* pkgPtr = GetStylePackageData(interp); ++ ++ /* ++ * Cancel any pending ThemeChanged calls: ++ */ ++ if (pkgPtr->themeChangePending) { ++ Tcl_CancelIdleCall(ThemeChangedProc, pkgPtr); ++ } ++} + + /* + * Ttk_CreateTheme -- + * Create a new theme and register it in the global theme table. + * + diff --git a/Mac/BuildScript/backport_gh92603_fix.patch b/Mac/BuildScript/backport_gh92603_fix.patch new file mode 100644 index 00000000000000..9a37b029650340 --- /dev/null +++ b/Mac/BuildScript/backport_gh92603_fix.patch @@ -0,0 +1,82 @@ +Accepted upstream for release in Tk 8.6.14: +https://core.tcl-lang.org/tk/info/cf3830280b + +--- tk8.6.13/macosx/tkMacOSXWindowEvent.c.orig ++++ tk8.6.13-patched/macosx/tkMacOSXWindowEvent.c +@@ -239,8 +239,8 @@ extern NSString *NSWindowDidOrderOffScreenNotification; + if (winPtr) { + TKContentView *view = [window contentView]; + +-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 +- if (@available(macOS 10.15, *)) { ++#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 ++ if (@available(macOS 10.14, *)) { + [view viewDidChangeEffectiveAppearance]; + } + #endif +@@ -1237,29 +1237,8 @@ static const char *const accentNames[] = { + } else if (effectiveAppearanceName == NSAppearanceNameDarkAqua) { + TkSendVirtualEvent(tkwin, "DarkAqua", NULL); + } +- if ([NSApp macOSVersion] < 101500) { +- +- /* +- * Mojave cannot handle the KVO shenanigans that we need for the +- * highlight and accent color notifications. +- */ +- +- return; +- } + if (!defaultColor) { + defaultColor = [NSApp macOSVersion] < 110000 ? "Blue" : "Multicolor"; +- preferences = [[NSUserDefaults standardUserDefaults] retain]; +- +- /* +- * AppKit calls this method when the user changes the Accent Color +- * but not when the user changes the Highlight Color. So we register +- * to receive KVO notifications for Highlight Color as well. +- */ +- +- [preferences addObserver:self +- forKeyPath:@"AppleHighlightColor" +- options:NSKeyValueObservingOptionNew +- context:NULL]; + } + NSString *accent = [preferences stringForKey:@"AppleAccentColor"]; + NSArray *words = [[preferences stringForKey:@"AppleHighlightColor"] +--- tk8.6.13/macosx/tkMacOSXWm.c.orig ++++ tk8.6.13-patched/macosx/tkMacOSXWm.c +@@ -1289,6 +1289,11 @@ TkWmDeadWindow( + [NSApp _setMainWindow:nil]; + } + [deadNSWindow close]; ++#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 ++ NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; ++ [preferences removeObserver:deadNSWindow.contentView ++ forKeyPath:@"AppleHighlightColor"]; ++#endif + [deadNSWindow release]; + + #if DEBUG_ZOMBIES > 1 +@@ -6763,6 +6768,21 @@ TkMacOSXMakeRealWindowExist( + } + TKContentView *contentView = [[TKContentView alloc] + initWithFrame:NSZeroRect]; ++#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 ++ NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; ++ ++ /* ++ * AppKit calls the viewDidChangeEffectiveAppearance method when the ++ * user changes the Accent Color but not when the user changes the ++ * Highlight Color. So we register to receive KVO notifications for ++ * Highlight Color as well. ++ */ ++ ++ [preferences addObserver:contentView ++ forKeyPath:@"AppleHighlightColor" ++ options:NSKeyValueObservingOptionNew ++ context:NULL]; ++#endif + [window setContentView:contentView]; + [contentView release]; + [window setDelegate:NSApp]; diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index a07d38aedcb5d6..84e1422a628361 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -261,14 +261,14 @@ def library_recipes(): tcl_checksum='81656d3367af032e0ae6157eff134f89' tk_checksum='5e0faecba458ee1386078fb228d008ba' - tk_patches = ['tk868_on_10_8_10_9.patch'] + tk_patches = ['backport_gh71383_fix.patch', 'tk868_on_10_8_10_9.patch', 'backport_gh110950_fix.patch'] else: tcl_tk_ver='8.6.13' tcl_checksum='43a1fae7412f61ff11de2cfd05d28cfc3a73762f354a417c62370a54e2caf066' tk_checksum='2e65fa069a23365440a3c56c556b8673b5e32a283800d8d9b257e3f584ce0675' - tk_patches = [ ] + tk_patches = ['backport_gh92603_fix.patch', 'backport_gh71383_fix.patch', 'backport_gh110950_fix.patch'] base_url = "https://prdownloads.sourceforge.net/tcl/{what}{version}-src.tar.gz" diff --git a/Misc/NEWS.d/next/macOS/2023-08-30-16-33-57.gh-issue-92603.ATkKVO.rst b/Misc/NEWS.d/next/macOS/2023-08-30-16-33-57.gh-issue-92603.ATkKVO.rst new file mode 100644 index 00000000000000..477463c0c21264 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-08-30-16-33-57.gh-issue-92603.ATkKVO.rst @@ -0,0 +1,3 @@ +Update macOS installer to include a fix accepted by upstream Tcl/Tk +for a crash encountered after the first :meth:`tkinter.Tk` instance +is destroyed. diff --git a/Misc/NEWS.d/next/macOS/2023-09-02-08-49-57.gh-issue-71383.Ttkchg.rst b/Misc/NEWS.d/next/macOS/2023-09-02-08-49-57.gh-issue-71383.Ttkchg.rst new file mode 100644 index 00000000000000..d8f3e429aab815 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-09-02-08-49-57.gh-issue-71383.Ttkchg.rst @@ -0,0 +1,2 @@ +Update macOS installer to include an upstream Tcl/Tk fix +for the ``ttk::ThemeChanged`` error encountered in Tkinter. diff --git a/Misc/NEWS.d/next/macOS/2023-10-18-17-26-36.gh-issue-110950.sonoma.rst b/Misc/NEWS.d/next/macOS/2023-10-18-17-26-36.gh-issue-110950.sonoma.rst new file mode 100644 index 00000000000000..c678c09f6aa55b --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-10-18-17-26-36.gh-issue-110950.sonoma.rst @@ -0,0 +1,3 @@ +Update macOS installer to include an upstream Tcl/Tk fix for the +``Secure coding is not enabled for restorable state!`` warning +encountered in Tkinter on macOS 14 Sonoma. From 44aa603d591388316d4e671656272d2a5bb9b334 Mon Sep 17 00:00:00 2001 From: "Liu, An-Chi" Date: Tue, 21 Nov 2023 22:32:09 +0900 Subject: [PATCH 024/228] gh-57879: Increase test coverage for pstats.py (gh-111447) --- Lib/test/test_pstats.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Lib/test/test_pstats.py b/Lib/test/test_pstats.py index acc2fa5385d923..d5a5a9738c2498 100644 --- a/Lib/test/test_pstats.py +++ b/Lib/test/test_pstats.py @@ -5,7 +5,9 @@ from pstats import SortKey from enum import StrEnum, _test_simple_enum +import os import pstats +import tempfile import cProfile class AddCallersTestCase(unittest.TestCase): @@ -36,6 +38,33 @@ def test_add(self): stats = pstats.Stats(stream=stream) stats.add(self.stats, self.stats) + def test_dump_and_load_works_correctly(self): + temp_storage_new = tempfile.NamedTemporaryFile(delete=False) + try: + self.stats.dump_stats(filename=temp_storage_new.name) + tmp_stats = pstats.Stats(temp_storage_new.name) + self.assertEqual(self.stats.stats, tmp_stats.stats) + finally: + temp_storage_new.close() + os.remove(temp_storage_new.name) + + def test_load_equivalent_to_init(self): + stats = pstats.Stats() + self.temp_storage = tempfile.NamedTemporaryFile(delete=False) + try: + cProfile.run('import os', filename=self.temp_storage.name) + stats.load_stats(self.temp_storage.name) + created = pstats.Stats(self.temp_storage.name) + self.assertEqual(stats.stats, created.stats) + finally: + self.temp_storage.close() + os.remove(self.temp_storage.name) + + def test_loading_wrong_types(self): + stats = pstats.Stats() + with self.assertRaises(TypeError): + stats.load_stats(42) + def test_sort_stats_int(self): valid_args = {-1: 'stdname', 0: 'calls', From e1540ae74d1fce62f53e25838ba21746ba5d8444 Mon Sep 17 00:00:00 2001 From: James Turk Date: Tue, 21 Nov 2023 09:18:53 -0600 Subject: [PATCH 025/228] gh-112252: Fix error on unset $OSNAME in venv/activate (GH-112253) --- Lib/venv/scripts/common/activate | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/venv/scripts/common/activate b/Lib/venv/scripts/common/activate index 458740a35b0d20..8398981ce53b9c 100644 --- a/Lib/venv/scripts/common/activate +++ b/Lib/venv/scripts/common/activate @@ -39,7 +39,7 @@ deactivate () { deactivate nondestructive # on Windows, a path can contain colons and backslashes and has to be converted: -if [ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ] ; then +if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then # transform D:\path\to\venv to /d/path/to/venv on MSYS # and to /cygdrive/d/path/to/venv on Cygwin export VIRTUAL_ENV=$(cygpath "__VENV_DIR__") From 4c483e011379aee9258db2e8abe479448109fd7b Mon Sep 17 00:00:00 2001 From: "T. Wouters" Date: Tue, 21 Nov 2023 18:37:03 +0100 Subject: [PATCH 026/228] gh-111863: Rename blurb snippet placed in the wrong directory by accident. (#112300) Rename blurb snippet placed in the wrong directory by accident. --- .../2023-11-08-20-28-03.gh-issue-111863.RPeFAX.rst | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Misc/NEWS.d/next/{C_API => C API}/2023-11-08-20-28-03.gh-issue-111863.RPeFAX.rst (100%) diff --git a/Misc/NEWS.d/next/C_API/2023-11-08-20-28-03.gh-issue-111863.RPeFAX.rst b/Misc/NEWS.d/next/C API/2023-11-08-20-28-03.gh-issue-111863.RPeFAX.rst similarity index 100% rename from Misc/NEWS.d/next/C_API/2023-11-08-20-28-03.gh-issue-111863.RPeFAX.rst rename to Misc/NEWS.d/next/C API/2023-11-08-20-28-03.gh-issue-111863.RPeFAX.rst From d857d5331a3326c77f867d837014d774841017a9 Mon Sep 17 00:00:00 2001 From: Lincoln <71312724+Lincoln-developer@users.noreply.github.com> Date: Tue, 21 Nov 2023 21:46:26 +0300 Subject: [PATCH 027/228] gh-111361: Added an update for unicodedata in what's new in Python 3.13 (#112031) Co-authored-by: Hugo van Kemenade --- Doc/whatsnew/3.13.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 4d05bce34ef847..fc5ae13abe7961 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -307,6 +307,12 @@ typing check whether a class is a :class:`typing.Protocol`. (Contributed by Jelle Zijlstra in :gh:`104873`.) +unicodedata +----------- + +* The Unicode database has been updated to version 15.1.0. (Contributed by + James Gerity in :gh:`109559`.) + venv ---- From 9d70831cb7127855a8bf83b585525f13cffb9f59 Mon Sep 17 00:00:00 2001 From: Junya Okabe <86868255+Okabe-Junya@users.noreply.github.com> Date: Wed, 22 Nov 2023 07:32:38 +0900 Subject: [PATCH 028/228] gh-110745: add a newline argument to pathlib.Path.read_text (#110880) Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Barney Gale --- Doc/library/pathlib.rst | 4 +++- Lib/pathlib.py | 4 ++-- Lib/test/test_pathlib.py | 15 +++++++++++++++ ...2023-10-15-08-08-26.gh-issue-110745.mxEkh0.rst | 2 ++ 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-15-08-08-26.gh-issue-110745.mxEkh0.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 8ee89a003a339a..7ecfd120db8d15 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -1314,7 +1314,7 @@ call fails (for example because the path doesn't exist). .. versionadded:: 3.5 -.. method:: Path.read_text(encoding=None, errors=None) +.. method:: Path.read_text(encoding=None, errors=None, newline=None) Return the decoded contents of the pointed-to file as a string:: @@ -1329,6 +1329,8 @@ call fails (for example because the path doesn't exist). .. versionadded:: 3.5 + .. versionchanged:: 3.13 + The *newline* parameter was added. .. method:: Path.readlink() diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 73f87b9acbf9d5..9bce5320ef68e9 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -950,12 +950,12 @@ def read_bytes(self): with self.open(mode='rb') as f: return f.read() - def read_text(self, encoding=None, errors=None): + def read_text(self, encoding=None, errors=None, newline=None): """ Open the file in text mode, read it, and close the file. """ encoding = io.text_encoding(encoding) - with self.open(mode='r', encoding=encoding, errors=errors) as f: + with self.open(mode='r', encoding=encoding, errors=errors, newline=newline) as f: return f.read() def write_bytes(self, data): diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 7083e9ebba6690..e1121a9d76c040 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1878,6 +1878,21 @@ def test_read_write_text(self): self.assertRaises(TypeError, (p / 'fileA').write_text, b'somebytes') self.assertEqual((p / 'fileA').read_text(encoding='latin-1'), 'äbcdefg') + def test_read_text_with_newlines(self): + p = self.cls(BASE) + # Check that `\n` character change nothing + (p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq') + self.assertEqual((p / 'fileA').read_text(newline='\n'), + 'abcde\r\nfghlk\n\rmnopq') + # Check that `\r` character replaces `\n` + (p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq') + self.assertEqual((p / 'fileA').read_text(newline='\r'), + 'abcde\r\nfghlk\n\rmnopq') + # Check that `\r\n` character replaces `\n` + (p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq') + self.assertEqual((p / 'fileA').read_text(newline='\r\n'), + 'abcde\r\nfghlk\n\rmnopq') + def test_write_text_with_newlines(self): p = self.cls(BASE) # Check that `\n` character change nothing diff --git a/Misc/NEWS.d/next/Library/2023-10-15-08-08-26.gh-issue-110745.mxEkh0.rst b/Misc/NEWS.d/next/Library/2023-10-15-08-08-26.gh-issue-110745.mxEkh0.rst new file mode 100644 index 00000000000000..b99f968dc20ae9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-15-08-08-26.gh-issue-110745.mxEkh0.rst @@ -0,0 +1,2 @@ +Added *newline* parameter to :meth:`pathlib.Path.read_text`. +Patch by Junya Okabe. From 46500c42f09a8342efde48ad74327d5225158ff3 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Tue, 21 Nov 2023 14:58:30 -0800 Subject: [PATCH 029/228] GH-111963: Clarify sys.monitoring.free_tool_id's limitations (GH-112291) --- Doc/library/sys.monitoring.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Doc/library/sys.monitoring.rst b/Doc/library/sys.monitoring.rst index f2fe3d76248c2c..762581b7eda7f1 100644 --- a/Doc/library/sys.monitoring.rst +++ b/Doc/library/sys.monitoring.rst @@ -54,6 +54,13 @@ Registering and using tools Should be called once a tool no longer requires *tool_id*. +.. note:: + + :func:`free_tool_id` will not disable global or local events associated + with *tool_id*, nor will it unregister any callback functions. This + function is only intended to be used to notify the VM that the + particular *tool_id* is no longer in use. + .. function:: get_tool(tool_id: int, /) -> str | None Returns the name of the tool if *tool_id* is in use, From 4fa376b0059948d4b3d994166b0dc210a4c4f8ec Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Wed, 22 Nov 2023 01:32:43 +0000 Subject: [PATCH 030/228] gh-111863: Rename term Py_NOGIL to Py_GIL_DISABLED in sysconfig (gh-112307) --- Lib/test/test_sys.py | 4 ++-- Modules/_sysconfig.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index ae73f5a7de7421..7f49fb004272bb 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1209,8 +1209,8 @@ def test_pystats(self): @unittest.skipUnless(hasattr(sys, 'abiflags'), 'need sys.abiflags') def test_disable_gil_abi(self): abi_threaded = 't' in sys.abiflags - py_nogil = (sysconfig.get_config_var('Py_GIL_DISABLED') == 1) - self.assertEqual(py_nogil, abi_threaded) + py_gil_disabled = (sysconfig.get_config_var('Py_GIL_DISABLED') == 1) + self.assertEqual(py_gil_disabled, abi_threaded) @test.support.cpython_only diff --git a/Modules/_sysconfig.c b/Modules/_sysconfig.c index 2918913c18ed1e..c76b9e6b3ebafa 100644 --- a/Modules/_sysconfig.c +++ b/Modules/_sysconfig.c @@ -58,11 +58,11 @@ _sysconfig_config_vars_impl(PyObject *module) #endif #ifdef Py_GIL_DISABLED - PyObject *py_nogil = _PyLong_GetOne(); + PyObject *py_gil_disabled = _PyLong_GetOne(); #else - PyObject *py_nogil = _PyLong_GetZero(); + PyObject *py_gil_disabled = _PyLong_GetZero(); #endif - if (PyDict_SetItemString(config, "Py_GIL_DISABLED", py_nogil) < 0) { + if (PyDict_SetItemString(config, "Py_GIL_DISABLED", py_gil_disabled) < 0) { Py_DECREF(config); return NULL; } From 6c47eaccfa2550c140a24bc6e520d968731d9689 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Wed, 22 Nov 2023 06:35:36 +0100 Subject: [PATCH 031/228] Fix docstring and var name of itertools recipe (#112113) `prepend()` works with arbitrary iterables, not only iterators. In fact, the example given uses a `list`, which is iterable, but not an iterator. --- Doc/library/itertools.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index f97e7f720ae4e4..ebb4ebcfa7618a 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -798,10 +798,10 @@ which incur interpreter overhead. "Return first n items of the iterable as a list" return list(islice(iterable, n)) - def prepend(value, iterator): - "Prepend a single value in front of an iterator" + def prepend(value, iterable): + "Prepend a single value in front of an iterable" # prepend(1, [2, 3, 4]) --> 1 2 3 4 - return chain([value], iterator) + return chain([value], iterable) def tabulate(function, start=0): "Return function(0), function(1), ..." From fde8fe50e495b66c397c6ce147ca3af2e7d9b289 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Wed, 22 Nov 2023 01:20:57 -0500 Subject: [PATCH 032/228] gh-59703: restore include of mach-o/dyld.h (gh-112309) On older versions of macOS, _NSGetExecutablePath appears to only be available via macho-o/dyld so macho-o/dyld.h is still needed. --- Modules/getpath.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/getpath.c b/Modules/getpath.c index be1a9cf9cc4782..6c1078b8914522 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -18,6 +18,7 @@ #ifdef __APPLE__ # include +# include #endif /* Reference the precompiled getpath.py */ From ad0e2a9332626dac4588f18626a20c48f4a58a9c Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Wed, 22 Nov 2023 03:29:28 -0500 Subject: [PATCH 033/228] Do not build the macOS installer with mimalloc enabled (#112312) Do not build the macOS installer with mimalloc enabled pending resolution of weak linking crashes during interpreter startup on macOS 10.9, 10.10, and 10.11 when built on macOS 11 and later. --- Mac/BuildScript/build-installer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 84e1422a628361..b35789a7e3132b 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -1147,7 +1147,9 @@ def buildPython(): # will find them during its extension import sanity checks. print("Running configure...") + print(" NOTE: --with-mimalloc=no pending resolution of weak linking issues") runCommand("%s -C --enable-framework --enable-universalsdk=/ " + "--with-mimalloc=no " "--with-universal-archs=%s " "%s " "%s " From 9c4347ef8b60f54dd357fd6b2f5ca9edc5105e2a Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Wed, 22 Nov 2023 11:43:55 +0100 Subject: [PATCH 034/228] Python 3.13.0a2 --- Include/patchlevel.h | 4 +- Lib/pydoc_data/topics.py | 44 +- Misc/NEWS.d/3.13.0a2.rst | 1622 +++++++++++++++++ ...3-10-17-01-56-11.gh-issue-85283.V156T2.rst | 5 - ...-10-17-03-10-40.gh-issue-110828.31vQ9B.rst | 1 - ...-10-20-15-29-31.gh-issue-111046.2DxQl8.rst | 1 - ...-11-15-13-40-29.gh-issue-112088.UJQxxh.rst | 5 - ...3-11-15-16-56-20.gh-issue-96954.6FYvKn.rst | 1 - ...3-06-08-21-12-44.gh-issue-67565.UkK3x-.rst | 1 - ...-07-12-12-14-52.gh-issue-106672.fkRjmi.rst | 5 - ...3-08-28-17-34-10.gh-issue-85283.f1zXcc.rst | 3 - ...3-08-28-17-40-51.gh-issue-85283.raFNiD.rst | 2 - ...-10-02-23-08-53.gh-issue-109587.UqqnDY.rst | 2 - ...-10-13-14-18-06.gh-issue-110815.tEFLVl.rst | 1 - ...-10-17-10-21-59.gh-issue-110964.OxqEjd.rst | 2 - ...-10-19-22-39-24.gh-issue-108082.uJytvc.rst | 1 - ...-10-30-18-13-01.gh-issue-111506.EUdO22.rst | 2 - ...-10-31-14-58-17.gh-issue-111569._V8iu4.rst | 3 - ...-10-31-18-22-03.gh-issue-108765._beYv8.rst | 3 - ...-11-08-18-37-19.gh-issue-111138.3Ypq8h.rst | 2 - ...-11-08-20-28-03.gh-issue-111863.RPeFAX.rst | 1 - ...-11-10-10-21-38.gh-issue-111262.2utB5m.rst | 4 - ...-11-10-10-24-28.gh-issue-111956.ImE6Cx.rst | 2 - ...-11-13-17-57-11.gh-issue-112026.WJLJcI.rst | 3 - ...-11-15-16-07-57.gh-issue-112026.bnr8dd.rst | 51 - ...-11-15-17-10-09.gh-issue-112026.ts9yyn.rst | 2 - ...-11-15-18-36-21.gh-issue-112026._Yybr5.rst | 3 - .../2021-11-10-10-40-05.bpo-45759.WJoB3D.rst | 1 - ...-12-27-02-51-45.gh-issue-100445.C8f6ph.rst | 1 - ...-07-20-11-41-16.gh-issue-106905.AyZpuB.rst | 1 - ...-08-31-11-42-16.gh-issue-106718._-57DA.rst | 2 - .../2023-09-06-12-36-11.bpo-46657.xea1T_.rst | 1 - ...-09-15-23-39-43.gh-issue-103615.WZavly.rst | 1 - ...3-09-30-17-30-11.gh-issue-89519.hz2pZf.rst | 6 - ...-10-09-19-54-33.gh-issue-110543.1wrxO8.rst | 3 - ...-10-12-12-09-01.gh-issue-110481.3Er3it.rst | 1 - ...-10-12-17-15-23.gh-issue-110722.sjMwQe.rst | 3 - ...-10-13-09-21-29.gh-issue-110805.vhU7A7.rst | 2 - ...-10-13-16-55-55.gh-issue-109094.ziL4cJ.rst | 3 - ...-10-14-12-19-34.gh-issue-110864.-baPDE.rst | 2 - ...-10-15-20-45-35.gh-issue-110892.oA6eVY.rst | 1 - ...-10-15-22-18-45.gh-issue-109894.UAmo06.rst | 1 - ...-10-16-12-12-48.gh-issue-110912.uEJGi_.rst | 2 - ...-10-16-15-51-37.gh-issue-109214.-RGTFH.rst | 1 - ...-10-17-11-03-45.gh-issue-110938.X3sbMb.rst | 2 - ...-10-20-23-14-06.gh-issue-111123.jjVc3M.rst | 2 - ...3-10-23-15-44-47.gh-issue-67224.S4D6CR.rst | 2 - ...3-10-23-22-11-09.gh-issue-94438.y2pITu.rst | 1 - ...-10-26-18-45-20.gh-issue-111354.GrT-Wf.rst | 3 - ...-10-27-11-22-09.gh-issue-111374.e9lrPZ.rst | 3 - ...-10-27-11-51-40.gh-issue-111380.vgSbir.rst | 2 - ...-10-27-12-17-49.gh-issue-111366._TSknV.rst | 3 - ...-10-27-19-38-33.gh-issue-102388.vd5YUZ.rst | 1 - ...-10-29-11-35-21.gh-issue-111435.ageUWQ.rst | 2 - ...-10-29-12-33-33.gh-issue-111438.bHTLLl.rst | 2 - ...-10-29-20-11-21.gh-issue-111420.IUT-GK.rst | 1 - ...-10-31-14-25-21.gh-issue-109181.11h6Mc.rst | 2 - ...-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst | 9 - ...-11-02-14-49-19.gh-issue-111354.gIS3f-.rst | 5 - ...-11-02-15-00-57.gh-issue-111623.BZxYc8.rst | 2 - ...-11-03-01-04-55.gh-issue-111654.scUhDO.rst | 2 - ...-11-03-01-23-48.gh-issue-111666.l8Q8G5.rst | 3 - ...-11-03-19-25-38.gh-issue-111772.aRQvOn.rst | 1 - ...-11-03-22-48-29.gh-issue-109369.ELYaxJ.rst | 1 - ...-11-04-13-36-51.gh-issue-110829.Pa0CJI.rst | 1 - ...-11-05-06-40-35.gh-issue-111843.c045cB.rst | 2 - ...3-11-05-20-59-10.gh-issue-81925.wKHLSS.rst | 1 - ...3-11-06-16-44-09.gh-issue-79932.2qv7uD.rst | 1 - ...3-11-07-12-59-02.gh-issue-81137.qFpJCY.rst | 2 - ...-11-14-22-12-11.gh-issue-111916.ZGCayL.rst | 1 - ...-11-15-16-14-10.gh-issue-106529.Y48ax9.rst | 1 - ...-11-15-20-20-51.gh-issue-111798.cs-3t3.rst | 4 - ...-11-17-16-49-32.gh-issue-111807.QvjP9_.rst | 1 - ...-11-19-15-57-23.gh-issue-112266.BSJMbR.rst | 2 - ...-11-20-10-40-40.gh-issue-112287.15gWAK.rst | 3 - ...-11-20-14-13-02.gh-issue-112243.FKdQnr.rst | 1 - .../2019-01-07-06-18-25.bpo-35668.JimxP5.rst | 4 - ...2-05-06-15-49-57.gh-issue-86826.rf006W.rst | 4 - ...2-05-28-20-55-07.gh-issue-73561.YRmAvy.rst | 1 - ...2-10-05-15-01-36.gh-issue-96954.ezwkrU.rst | 5 - ...2-10-14-21-11-10.gh-issue-97928.Pdxh1G.rst | 6 - ...-03-22-02-01-30.gh-issue-102895.HiEqaZ.rst | 1 - ...-04-15-14-45-21.gh-issue-102956.Z6qeUy.rst | 1 - ...3-04-26-16-37-00.gh-issue-90890.fIag4w.rst | 7 - ...3-05-30-02-01-14.gh-issue-90026.FyCXw8.rst | 1 - ...3-07-13-00-24-52.gh-issue-72904.Yn5-j0.rst | 2 - ...-07-29-19-00-39.gh-issue-107431.1GzJ2p.rst | 2 - ...-08-30-19-10-35.gh-issue-105931.Lpwve8.rst | 8 - ...-09-02-16-07-23.gh-issue-108791.fBcAqh.rst | 1 - ...3-09-08-12-10-10.gh-issue-85098.DfQbeJ.rst | 2 - ...-09-15-12-30-21.gh-issue-109466.6ah-aw.rst | 1 - ...-09-25-20-05-41.gh-issue-109747._cRJH8.rst | 3 - ...-10-02-05-23-27.gh-issue-110196.djwt0z.rst | 1 - ...-10-08-14-17-06.gh-issue-110395._tdCsV.rst | 2 - ...3-10-08-18-38-09.gh-issue-88434.2Q_IkG.rst | 3 - ...3-10-09-23-59-04.gh-issue-59013.qPbS-G.rst | 1 - ...-10-10-17-56-41.gh-issue-110392.6g6CnP.rst | 4 - ...-10-12-15-16-44.gh-issue-110774.AdCb5A.rst | 1 - ...-10-13-06-47-20.gh-issue-110771.opwdlc.rst | 1 - ...3-10-14-20-15-53.gh-issue-80675._M-cQC.rst | 1 - ...3-10-14-21-33-57.gh-issue-84583.-Cmn4_.rst | 1 - ...-10-15-08-08-26.gh-issue-110745.mxEkh0.rst | 2 - ...-10-16-18-41-51.gh-issue-110944.CmUKXo.rst | 1 - ...-10-19-22-46-34.gh-issue-111092.hgut12.rst | 1 - ...-10-20-15-29-10.gh-issue-110910.u2oPwX.rst | 3 - ...-10-21-13-57-06.gh-issue-111159.GoHp7s.rst | 1 - ...-10-22-21-28-05.gh-issue-111187._W11Ab.rst | 1 - ...-10-23-13-53-58.gh-issue-111174.Oohmzd.rst | 2 - ...-10-23-22-40-47.gh-issue-111230.k3Jm84.rst | 1 - ...-10-23-23-14-54.gh-issue-111233.sCdCC0.rst | 1 - ...-10-24-12-09-46.gh-issue-111251.urFYtn.rst | 1 - ...-10-24-12-20-46.gh-issue-111253.HFywSK.rst | 1 - ...-10-25-08-42-05.gh-issue-111295.H2K4lf.rst | 1 - ...-10-25-11-13-35.gh-issue-111259.z7ndeA.rst | 1 - ...3-10-25-11-54-00.gh-issue-79033.5ePgFl.rst | 6 - ...-10-27-09-56-20.gh-issue-111388.SlmDbC.rst | 2 - ...3-10-27-12-46-56.gh-issue-68166.0EbWW4.rst | 4 - ...-10-28-04-21-17.gh-issue-111342.m8Ln1k.rst | 1 - ...-10-28-22-11-11.gh-issue-111429.mJGxuQ.rst | 2 - ...3-10-29-03-46-27.gh-issue-66425.FWTdDo.rst | 3 - ...-10-30-08-50-46.gh-issue-111356.Bc8LvA.rst | 1 - ...-10-30-14-47-23.gh-issue-111246.QJ_ehs.rst | 2 - ...-10-31-07-46-56.gh-issue-111531.6zUV_G.rst | 2 - ...-11-01-14-03-24.gh-issue-110894.7-wZxC.rst | 1 - ...-11-02-12-15-46.gh-issue-111482.FWqZIU.rst | 3 - ...-11-04-01-20-23.gh-issue-111719.fUiKBD.rst | 1 - ...-11-04-10-24-25.gh-issue-111541.x0RBI1.rst | 1 - ...3-11-04-21-12-27.gh-issue-80731.Wq51xg.rst | 1 - ...-11-08-07-42-53.gh-issue-111768.g-WpnV.rst | 1 - ...-11-08-11-50-49.gh-issue-111841.iSqdQf.rst | 2 - ...-11-08-15-58-57.gh-issue-111804.uAXTOL.rst | 2 - ...-11-08-23-32-03.gh-issue-111835.ufFiuW.rst | 4 - ...-11-09-10-45-56.gh-issue-103791.sdfkja.rst | 3 - ...-11-09-12-57-43.gh-issue-111460.TQaz9I.rst | 3 - ...-11-10-22-08-28.gh-issue-111942.MDFm6v.rst | 2 - ...-11-11-16-42-48.gh-issue-109538.cMG5ux.rst | 1 - ...-11-14-16-31-59.gh-issue-111995.OoX8JJ.rst | 2 - ...-11-14-18-43-55.gh-issue-111942.x1pnrj.rst | 2 - ...-09-15-15-00-14.gh-issue-108747.ql0owS.rst | 2 - ...-10-16-13-47-24.gh-issue-110918.aFgZK3.rst | 4 - ...-10-17-17-54-36.gh-issue-110995.Fx8KRD.rst | 2 - ...-10-21-00-10-36.gh-issue-110932.jktjJU.rst | 2 - ...-10-21-19-27-36.gh-issue-111165.FU6mUk.rst | 2 - ...-10-31-22-09-25.gh-issue-110367.UhQi44.rst | 3 - ...-11-03-18-59-13.gh-issue-110722.jvT1pb.rst | 2 - ...-11-17-15-20-41.gh-issue-111808.jtIayt.rst | 4 - ...-11-09-13-04-29.gh-issue-111903.7Prryr.rst | 4 - ...-10-19-21-46-18.gh-issue-110913.CWlPfg.rst | 1 - ...-10-25-05-01-28.gh-issue-111293.FSsLT6.rst | 1 - ...-11-13-22-35-27.gh-issue-111856.vEtA5z.rst | 2 - ...3-08-30-16-33-57.gh-issue-92603.ATkKVO.rst | 3 - ...3-09-02-08-49-57.gh-issue-71383.Ttkchg.rst | 2 - ...-10-18-01-40-36.gh-issue-111015.NaLI2L.rst | 1 - ...-10-18-17-26-36.gh-issue-110950.sonoma.rst | 3 - ...3-10-31-22-13-05.gh-issue-59703.SML6Ag.rst | 4 - README.rst | 2 +- 156 files changed, 1651 insertions(+), 403 deletions(-) create mode 100644 Misc/NEWS.d/3.13.0a2.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-10-17-01-56-11.gh-issue-85283.V156T2.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-10-17-03-10-40.gh-issue-110828.31vQ9B.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-10-20-15-29-31.gh-issue-111046.2DxQl8.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-11-15-13-40-29.gh-issue-112088.UJQxxh.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-11-15-16-56-20.gh-issue-96954.6FYvKn.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-06-08-21-12-44.gh-issue-67565.UkK3x-.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-07-12-12-14-52.gh-issue-106672.fkRjmi.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-08-28-17-34-10.gh-issue-85283.f1zXcc.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-08-28-17-40-51.gh-issue-85283.raFNiD.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-10-02-23-08-53.gh-issue-109587.UqqnDY.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-10-13-14-18-06.gh-issue-110815.tEFLVl.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-10-17-10-21-59.gh-issue-110964.OxqEjd.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-10-19-22-39-24.gh-issue-108082.uJytvc.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-10-30-18-13-01.gh-issue-111506.EUdO22.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-10-31-14-58-17.gh-issue-111569._V8iu4.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-10-31-18-22-03.gh-issue-108765._beYv8.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-11-08-18-37-19.gh-issue-111138.3Ypq8h.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-11-08-20-28-03.gh-issue-111863.RPeFAX.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-11-10-10-21-38.gh-issue-111262.2utB5m.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-11-10-10-24-28.gh-issue-111956.ImE6Cx.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-11-13-17-57-11.gh-issue-112026.WJLJcI.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-11-15-16-07-57.gh-issue-112026.bnr8dd.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-11-15-17-10-09.gh-issue-112026.ts9yyn.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-11-15-18-36-21.gh-issue-112026._Yybr5.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-11-10-10-40-05.bpo-45759.WJoB3D.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-12-27-02-51-45.gh-issue-100445.C8f6ph.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-07-20-11-41-16.gh-issue-106905.AyZpuB.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-08-31-11-42-16.gh-issue-106718._-57DA.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-09-06-12-36-11.bpo-46657.xea1T_.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-09-15-23-39-43.gh-issue-103615.WZavly.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-09-30-17-30-11.gh-issue-89519.hz2pZf.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-09-19-54-33.gh-issue-110543.1wrxO8.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-12-12-09-01.gh-issue-110481.3Er3it.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-12-17-15-23.gh-issue-110722.sjMwQe.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-13-09-21-29.gh-issue-110805.vhU7A7.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-13-16-55-55.gh-issue-109094.ziL4cJ.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-14-12-19-34.gh-issue-110864.-baPDE.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-15-20-45-35.gh-issue-110892.oA6eVY.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-15-22-18-45.gh-issue-109894.UAmo06.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-16-12-12-48.gh-issue-110912.uEJGi_.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-16-15-51-37.gh-issue-109214.-RGTFH.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-17-11-03-45.gh-issue-110938.X3sbMb.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-20-23-14-06.gh-issue-111123.jjVc3M.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-23-15-44-47.gh-issue-67224.S4D6CR.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-23-22-11-09.gh-issue-94438.y2pITu.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-26-18-45-20.gh-issue-111354.GrT-Wf.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-27-11-22-09.gh-issue-111374.e9lrPZ.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-27-11-51-40.gh-issue-111380.vgSbir.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-27-12-17-49.gh-issue-111366._TSknV.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-27-19-38-33.gh-issue-102388.vd5YUZ.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-29-11-35-21.gh-issue-111435.ageUWQ.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-29-12-33-33.gh-issue-111438.bHTLLl.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-29-20-11-21.gh-issue-111420.IUT-GK.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-31-14-25-21.gh-issue-109181.11h6Mc.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-02-14-49-19.gh-issue-111354.gIS3f-.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-02-15-00-57.gh-issue-111623.BZxYc8.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-03-01-04-55.gh-issue-111654.scUhDO.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-03-01-23-48.gh-issue-111666.l8Q8G5.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-03-19-25-38.gh-issue-111772.aRQvOn.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-03-22-48-29.gh-issue-109369.ELYaxJ.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-04-13-36-51.gh-issue-110829.Pa0CJI.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-05-06-40-35.gh-issue-111843.c045cB.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-05-20-59-10.gh-issue-81925.wKHLSS.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-06-16-44-09.gh-issue-79932.2qv7uD.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-07-12-59-02.gh-issue-81137.qFpJCY.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-14-22-12-11.gh-issue-111916.ZGCayL.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-15-16-14-10.gh-issue-106529.Y48ax9.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-15-20-20-51.gh-issue-111798.cs-3t3.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-17-16-49-32.gh-issue-111807.QvjP9_.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-19-15-57-23.gh-issue-112266.BSJMbR.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-20-10-40-40.gh-issue-112287.15gWAK.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-20-14-13-02.gh-issue-112243.FKdQnr.rst delete mode 100644 Misc/NEWS.d/next/IDLE/2019-01-07-06-18-25.bpo-35668.JimxP5.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-05-06-15-49-57.gh-issue-86826.rf006W.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-05-28-20-55-07.gh-issue-73561.YRmAvy.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-10-05-15-01-36.gh-issue-96954.ezwkrU.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-10-14-21-11-10.gh-issue-97928.Pdxh1G.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-03-22-02-01-30.gh-issue-102895.HiEqaZ.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-04-15-14-45-21.gh-issue-102956.Z6qeUy.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-04-26-16-37-00.gh-issue-90890.fIag4w.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-05-30-02-01-14.gh-issue-90026.FyCXw8.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-13-00-24-52.gh-issue-72904.Yn5-j0.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-29-19-00-39.gh-issue-107431.1GzJ2p.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-08-30-19-10-35.gh-issue-105931.Lpwve8.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-09-02-16-07-23.gh-issue-108791.fBcAqh.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-09-08-12-10-10.gh-issue-85098.DfQbeJ.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-09-15-12-30-21.gh-issue-109466.6ah-aw.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-09-25-20-05-41.gh-issue-109747._cRJH8.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-02-05-23-27.gh-issue-110196.djwt0z.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-08-14-17-06.gh-issue-110395._tdCsV.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-08-18-38-09.gh-issue-88434.2Q_IkG.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-09-23-59-04.gh-issue-59013.qPbS-G.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-10-17-56-41.gh-issue-110392.6g6CnP.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-12-15-16-44.gh-issue-110774.AdCb5A.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-13-06-47-20.gh-issue-110771.opwdlc.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-14-20-15-53.gh-issue-80675._M-cQC.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-14-21-33-57.gh-issue-84583.-Cmn4_.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-15-08-08-26.gh-issue-110745.mxEkh0.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-16-18-41-51.gh-issue-110944.CmUKXo.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-19-22-46-34.gh-issue-111092.hgut12.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-20-15-29-10.gh-issue-110910.u2oPwX.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-21-13-57-06.gh-issue-111159.GoHp7s.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-22-21-28-05.gh-issue-111187._W11Ab.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-23-13-53-58.gh-issue-111174.Oohmzd.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-23-22-40-47.gh-issue-111230.k3Jm84.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-23-23-14-54.gh-issue-111233.sCdCC0.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-24-12-09-46.gh-issue-111251.urFYtn.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-24-12-20-46.gh-issue-111253.HFywSK.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-25-08-42-05.gh-issue-111295.H2K4lf.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-25-11-13-35.gh-issue-111259.z7ndeA.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-25-11-54-00.gh-issue-79033.5ePgFl.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-27-09-56-20.gh-issue-111388.SlmDbC.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-27-12-46-56.gh-issue-68166.0EbWW4.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-28-04-21-17.gh-issue-111342.m8Ln1k.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-28-22-11-11.gh-issue-111429.mJGxuQ.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-29-03-46-27.gh-issue-66425.FWTdDo.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-30-08-50-46.gh-issue-111356.Bc8LvA.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-30-14-47-23.gh-issue-111246.QJ_ehs.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-10-31-07-46-56.gh-issue-111531.6zUV_G.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-11-01-14-03-24.gh-issue-110894.7-wZxC.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-11-02-12-15-46.gh-issue-111482.FWqZIU.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-11-04-01-20-23.gh-issue-111719.fUiKBD.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-11-04-10-24-25.gh-issue-111541.x0RBI1.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-11-04-21-12-27.gh-issue-80731.Wq51xg.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-11-08-07-42-53.gh-issue-111768.g-WpnV.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-11-08-11-50-49.gh-issue-111841.iSqdQf.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-11-08-15-58-57.gh-issue-111804.uAXTOL.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-11-08-23-32-03.gh-issue-111835.ufFiuW.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-11-09-10-45-56.gh-issue-103791.sdfkja.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-11-09-12-57-43.gh-issue-111460.TQaz9I.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-11-10-22-08-28.gh-issue-111942.MDFm6v.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-11-11-16-42-48.gh-issue-109538.cMG5ux.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-11-14-16-31-59.gh-issue-111995.OoX8JJ.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-11-14-18-43-55.gh-issue-111942.x1pnrj.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-15-15-00-14.gh-issue-108747.ql0owS.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-10-16-13-47-24.gh-issue-110918.aFgZK3.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-10-17-17-54-36.gh-issue-110995.Fx8KRD.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-10-21-00-10-36.gh-issue-110932.jktjJU.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-10-21-19-27-36.gh-issue-111165.FU6mUk.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-10-31-22-09-25.gh-issue-110367.UhQi44.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-11-03-18-59-13.gh-issue-110722.jvT1pb.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-11-17-15-20-41.gh-issue-111808.jtIayt.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2023-11-09-13-04-29.gh-issue-111903.7Prryr.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-10-19-21-46-18.gh-issue-110913.CWlPfg.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-10-25-05-01-28.gh-issue-111293.FSsLT6.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-11-13-22-35-27.gh-issue-111856.vEtA5z.rst delete mode 100644 Misc/NEWS.d/next/macOS/2023-08-30-16-33-57.gh-issue-92603.ATkKVO.rst delete mode 100644 Misc/NEWS.d/next/macOS/2023-09-02-08-49-57.gh-issue-71383.Ttkchg.rst delete mode 100644 Misc/NEWS.d/next/macOS/2023-10-18-01-40-36.gh-issue-111015.NaLI2L.rst delete mode 100644 Misc/NEWS.d/next/macOS/2023-10-18-17-26-36.gh-issue-110950.sonoma.rst delete mode 100644 Misc/NEWS.d/next/macOS/2023-10-31-22-13-05.gh-issue-59703.SML6Ag.rst diff --git a/Include/patchlevel.h b/Include/patchlevel.h index c2ffa3d5a75c06..592491eed5ff84 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -20,10 +20,10 @@ #define PY_MINOR_VERSION 13 #define PY_MICRO_VERSION 0 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA -#define PY_RELEASE_SERIAL 1 +#define PY_RELEASE_SERIAL 2 /* Version as a string */ -#define PY_VERSION "3.13.0a1+" +#define PY_VERSION "3.13.0a2" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index 87b0a2d669e5a3..7c1bdc4dff2ec4 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Fri Oct 13 10:51:21 2023 +# Autogenerated by Sphinx on Wed Nov 22 11:44:32 2023 # as part of the release process. topics = {'assert': 'The "assert" statement\n' '**********************\n' @@ -5574,7 +5574,7 @@ ' Create an alias called *name* that executes *command*. The\n' ' *command* must *not* be enclosed in quotes. Replaceable ' 'parameters\n' - ' can be indicated by "%1", "%2", and so on, while "%*" is ' + ' can be indicated by "%1", "%2", … and "%9", while "%*" is ' 'replaced\n' ' by all the parameters. If *command* is omitted, the current ' 'alias\n' @@ -14650,10 +14650,16 @@ ' objects (for example when catching an exception and storing its\n' ' traceback for later use).\n' '\n' - ' "RuntimeError" is raised if the frame is currently executing.\n' + ' "RuntimeError" is raised if the frame is currently executing or\n' + ' suspended.\n' '\n' ' New in version 3.4.\n' '\n' + ' Changed in version 3.13: Attempting to clear a suspended frame\n' + ' raises "RuntimeError" (as has always been the case for ' + 'executing\n' + ' frames).\n' + '\n' '\n' 'Traceback objects\n' '-----------------\n' @@ -15216,21 +15222,23 @@ '\n' 'Keys views are set-like since their entries are unique and ' '*hashable*.\n' - 'If all values are hashable, so that "(key, value)" pairs are ' - 'unique\n' - 'and hashable, then the items view is also set-like. (Values ' - 'views are\n' - 'not treated as set-like since the entries are generally not ' - 'unique.)\n' - 'For set-like views, all of the operations defined for the ' - 'abstract\n' - 'base class "collections.abc.Set" are available (for example, ' - '"==",\n' - '"<", or "^"). While using set operators, set-like views ' - 'accept any\n' - 'iterable as the other operand, unlike sets which only accept ' - 'sets as\n' - 'the input.\n' + 'Items views also have set-like operations since the (key, ' + 'value) pairs\n' + 'are unique and the keys are hashable. If all values in an ' + 'items view\n' + 'are hashable as well, then the items view can interoperate ' + 'with other\n' + 'sets. (Values views are not treated as set-like since the ' + 'entries are\n' + 'generally not unique.) For set-like views, all of the ' + 'operations\n' + 'defined for the abstract base class "collections.abc.Set" ' + 'are\n' + 'available (for example, "==", "<", or "^"). While using ' + 'set\n' + 'operators, set-like views accept any iterable as the other ' + 'operand,\n' + 'unlike sets which only accept sets as the input.\n' '\n' 'An example of dictionary view usage:\n' '\n' diff --git a/Misc/NEWS.d/3.13.0a2.rst b/Misc/NEWS.d/3.13.0a2.rst new file mode 100644 index 00000000000000..c1b1be523325e8 --- /dev/null +++ b/Misc/NEWS.d/3.13.0a2.rst @@ -0,0 +1,1622 @@ +.. date: 2023-11-20-14-13-02 +.. gh-issue: 112243 +.. nonce: FKdQnr +.. release date: 2023-11-22 +.. section: Core and Builtins + +Don't include comments in f-string debug expressions. Patch by Pablo Galindo + +.. + +.. date: 2023-11-20-10-40-40 +.. gh-issue: 112287 +.. nonce: 15gWAK +.. section: Core and Builtins + +Slightly optimize the Tier 2 (uop) interpreter by only loading ``oparg`` and +``operand`` when needed. Also double the trace size limit again, to 512 this +time. + +.. + +.. date: 2023-11-19-15-57-23 +.. gh-issue: 112266 +.. nonce: BSJMbR +.. section: Core and Builtins + +Change docstrings of :attr:`~object.__dict__` and +:attr:`~object.__weakref__`. + +.. + +.. date: 2023-11-17-16-49-32 +.. gh-issue: 111807 +.. nonce: QvjP9_ +.. section: Core and Builtins + +Lower the max parser stack depth to 1000 under WASI debug builds. + +.. + +.. date: 2023-11-15-20-20-51 +.. gh-issue: 111798 +.. nonce: cs-3t3 +.. section: Core and Builtins + +When Python is built in debug mode, set the C recursion limit to 500 instead +of 1500. A debug build is likely built with low optimization level which +implies higher stack memory usage than a release build. Patch by Victor +Stinner. + +.. + +.. date: 2023-11-15-16-14-10 +.. gh-issue: 106529 +.. nonce: Y48ax9 +.. section: Core and Builtins + +Enable translating unspecialized ``FOR_ITER`` to Tier 2. + +.. + +.. date: 2023-11-14-22-12-11 +.. gh-issue: 111916 +.. nonce: ZGCayL +.. section: Core and Builtins + +Make hashlib related modules thread-safe without the GIL + +.. + +.. date: 2023-11-07-12-59-02 +.. gh-issue: 81137 +.. nonce: qFpJCY +.. section: Core and Builtins + +Deprecate assignment to a function's ``__code__`` field when the new code +object is of a mismatched type (e.g., from a generator to a plain function). + +.. + +.. date: 2023-11-06-16-44-09 +.. gh-issue: 79932 +.. nonce: 2qv7uD +.. section: Core and Builtins + +Raise exception if :meth:`frame.clear` is called on a suspended frame. + +.. + +.. date: 2023-11-05-20-59-10 +.. gh-issue: 81925 +.. nonce: wKHLSS +.. section: Core and Builtins + +Implement native thread ids for GNU KFreeBSD. + +.. + +.. date: 2023-11-05-06-40-35 +.. gh-issue: 111843 +.. nonce: c045cB +.. section: Core and Builtins + +Use exponential backoff to reduce the number of failed tier 2 optimization +attempts by over 99%. + +.. + +.. date: 2023-11-04-13-36-51 +.. gh-issue: 110829 +.. nonce: Pa0CJI +.. section: Core and Builtins + +Joining a thread now ensures the underlying OS thread has exited. This is +required for safer fork() in multi-threaded processes. + +.. + +.. date: 2023-11-03-22-48-29 +.. gh-issue: 109369 +.. nonce: ELYaxJ +.. section: Core and Builtins + +Make sure that tier 2 traces are de-optimized if the code is instrumented + +.. + +.. date: 2023-11-03-19-25-38 +.. gh-issue: 111772 +.. nonce: aRQvOn +.. section: Core and Builtins + +Specialize slot loads and stores for _Py_T_OBJECT as well as Py_T_OBJECT_EX + +.. + +.. date: 2023-11-03-01-23-48 +.. gh-issue: 111666 +.. nonce: l8Q8G5 +.. section: Core and Builtins + +Speed up :meth:`BaseExceptionGroup.derive`, +:meth:`BaseExceptionGroup.subgroup`, and :meth:`BaseExceptionGroup.split` by +changing how they parse passed arguments. + +.. + +.. date: 2023-11-03-01-04-55 +.. gh-issue: 111654 +.. nonce: scUhDO +.. section: Core and Builtins + +Fix runtime crash when some error happens in opcode +``LOAD_FROM_DICT_OR_DEREF``. + +.. + +.. date: 2023-11-02-15-00-57 +.. gh-issue: 111623 +.. nonce: BZxYc8 +.. section: Core and Builtins + +Add support for sharing tuples between interpreters using the +cross-interpreter API. Patch by Anthony Shaw. + +.. + +.. date: 2023-11-02-14-49-19 +.. gh-issue: 111354 +.. nonce: gIS3f- +.. section: Core and Builtins + +The oparg of :opcode:`YIELD_VALUE` is now ``1`` if the instruction is part +of a yield-from or await, and ``0`` otherwise. + +The SUSPENDED frame state is now split into ``SUSPENDED`` and +``SUSPENDED_YIELD_FROM``. This simplifies the code in ``_PyGen_yf``. + +.. + +.. date: 2023-10-31-21-33-35 +.. gh-issue: 111520 +.. nonce: vw-rxJ +.. section: Core and Builtins + +Merge the Tier 1 (bytecode) and Tier 2 (micro-ops) interpreters together, +moving the Tier 2 interpreter loop and switch into +``_PyEval_EvalFrameDefault()`` in ``Python/ceval.c``. The +``Python/executor.c`` file is gone. Also the ``TIER_ONE`` and ``TIER_TWO`` +macros are now handled by the code generator. + +**Beware!** This changes the environment variables to enable micro-ops and +their debugging to ``PYTHON_UOPS`` and ``PYTHON_LLTRACE``. + +.. + +.. date: 2023-10-31-14-25-21 +.. gh-issue: 109181 +.. nonce: 11h6Mc +.. section: Core and Builtins + +Speed up :obj:`Traceback` object creation by lazily compute the line number. +Patch by Pablo Galindo + +.. + +.. date: 2023-10-29-20-11-21 +.. gh-issue: 111420 +.. nonce: IUT-GK +.. section: Core and Builtins + +Allow type comments in parenthesized ``with`` statements + +.. + +.. date: 2023-10-29-12-33-33 +.. gh-issue: 111438 +.. nonce: bHTLLl +.. section: Core and Builtins + +Add support for sharing floats between interpreters using the +cross-interpreter API. Patch by Anthony Shaw. + +.. + +.. date: 2023-10-29-11-35-21 +.. gh-issue: 111435 +.. nonce: ageUWQ +.. section: Core and Builtins + +Add support for sharing of True and False between interpreters using the +cross-interpreter API. Patch by Anthony Shaw. + +.. + +.. date: 2023-10-27-19-38-33 +.. gh-issue: 102388 +.. nonce: vd5YUZ +.. section: Core and Builtins + +Fix a bug where ``iso2022_jp_3`` and ``iso2022_jp_2004`` codecs read out of +bounds + +.. + +.. date: 2023-10-27-12-17-49 +.. gh-issue: 111366 +.. nonce: _TSknV +.. section: Core and Builtins + +Fix an issue in the :mod:`codeop` that was causing :exc:`SyntaxError` +exceptions raised in the presence of invalid syntax to not contain precise +error messages. Patch by Pablo Galindo + +.. + +.. date: 2023-10-27-11-51-40 +.. gh-issue: 111380 +.. nonce: vgSbir +.. section: Core and Builtins + +Fix a bug that was causing :exc:`SyntaxWarning` to appear twice when parsing +if invalid syntax is encountered later. Patch by Pablo galindo + +.. + +.. date: 2023-10-27-11-22-09 +.. gh-issue: 111374 +.. nonce: e9lrPZ +.. section: Core and Builtins + +Added a new environment variable :envvar:`PYTHON_FROZEN_MODULES`. It +determines whether or not frozen modules are ignored by the import +machinery, equivalent of the :option:`-X frozen_modules <-X>` command-line +option. + +.. + +.. date: 2023-10-26-18-45-20 +.. gh-issue: 111354 +.. nonce: GrT-Wf +.. section: Core and Builtins + +Remove ``oparg`` from :opcode:`YIELD_VALUE`. Change ``oparg`` of +:opcode:`RESUME` to include information about the except-depth. These +changes make it possible to simplify the code in generator close. + +.. + +.. date: 2023-10-23-22-11-09 +.. gh-issue: 94438 +.. nonce: y2pITu +.. section: Core and Builtins + +Fix a regression that prevented jumping across ``is None`` and ``is not +None`` when debugging. Patch by Savannah Ostrowski. + +.. + +.. date: 2023-10-23-15-44-47 +.. gh-issue: 67224 +.. nonce: S4D6CR +.. section: Core and Builtins + +Show source lines in tracebacks when using the ``-c`` option when running +Python. Patch by Pablo Galindo + +.. + +.. date: 2023-10-20-23-14-06 +.. gh-issue: 111123 +.. nonce: jjVc3M +.. section: Core and Builtins + +Fix a bug where a :keyword:`global` declaration in an :keyword:`except` +block is rejected when the global is used in the :keyword:`else` block. + +.. + +.. date: 2023-10-17-11-03-45 +.. gh-issue: 110938 +.. nonce: X3sbMb +.. section: Core and Builtins + +Fix error messages for indented blocks with functions and classes with +generic type parameters. Patch by Pablo Galindo + +.. + +.. date: 2023-10-16-15-51-37 +.. gh-issue: 109214 +.. nonce: -RGTFH +.. section: Core and Builtins + +Remove unnecessary instruction pointer updates before returning from frames. + +.. + +.. date: 2023-10-16-12-12-48 +.. gh-issue: 110912 +.. nonce: uEJGi_ +.. section: Core and Builtins + +Correctly display the traceback for :exc:`MemoryError` exceptions using the +:mod:`traceback` module. Patch by Pablo Galindo + +.. + +.. date: 2023-10-15-22-18-45 +.. gh-issue: 109894 +.. nonce: UAmo06 +.. section: Core and Builtins + +Fixed crash due to improperly initialized static :exc:`MemoryError` in +subinterpreter. + +.. + +.. date: 2023-10-15-20-45-35 +.. gh-issue: 110892 +.. nonce: oA6eVY +.. section: Core and Builtins + +Return ``NULL`` for ``PyTrace_RETURN`` events caused by an exception + +.. + +.. date: 2023-10-14-12-19-34 +.. gh-issue: 110864 +.. nonce: -baPDE +.. section: Core and Builtins + +Fix argument parsing by ``_PyArg_UnpackKeywordsWithVararg`` for functions +defining pos-or-keyword, vararg, and kw-only parameters. + +.. + +.. date: 2023-10-13-16-55-55 +.. gh-issue: 109094 +.. nonce: ziL4cJ +.. section: Core and Builtins + +Replace ``prev_instr`` on the interpreter frame by ``instr_ptr`` which +points to the beginning of the instruction that is currently executing (or +will execute once the frame resumes). + +.. + +.. date: 2023-10-13-09-21-29 +.. gh-issue: 110805 +.. nonce: vhU7A7 +.. section: Core and Builtins + +Allow the repl to show source code and complete tracebacks. Patch by Pablo +Galindo + +.. + +.. date: 2023-10-12-17-15-23 +.. gh-issue: 110722 +.. nonce: sjMwQe +.. section: Core and Builtins + +Add :envvar:`PYTHON_PRESITE=package.module` to import a module early in the +interpreter lifecycle before ``site.py`` is executed. Python needs to be +:ref:`built in debug mode ` for this option to exist. + +.. + +.. date: 2023-10-12-12-09-01 +.. gh-issue: 110481 +.. nonce: 3Er3it +.. section: Core and Builtins + +Implement biased reference counting in ``--disable-gil`` builds. + +.. + +.. date: 2023-10-09-19-54-33 +.. gh-issue: 110543 +.. nonce: 1wrxO8 +.. section: Core and Builtins + +Fix regression in Python 3.12 where :meth:`types.CodeType.replace` would +produce a broken code object if called on a module or class code object that +contains a comprehension. Patch by Jelle Zijlstra. + +.. + +.. date: 2023-09-30-17-30-11 +.. gh-issue: 89519 +.. nonce: hz2pZf +.. section: Core and Builtins + +Removed chained :class:`classmethod` descriptors (introduced in +:issue:`19072`). This can no longer be used to wrap other descriptors such +as :class:`property`. The core design of this feature was flawed and caused +a number of downstream problems. To "pass-through" a :class:`classmethod`, +consider using the :attr:`!__wrapped__` attribute that was added in Python +3.10. + +.. + +.. date: 2023-09-15-23-39-43 +.. gh-issue: 103615 +.. nonce: WZavly +.. section: Core and Builtins + +Use local events for opcode tracing + +.. + +.. bpo: 46657 +.. date: 2023-09-06-12-36-11 +.. nonce: xea1T_ +.. section: Core and Builtins + +Add mimalloc memory allocator support. + +.. + +.. date: 2023-08-31-11-42-16 +.. gh-issue: 106718 +.. nonce: _-57DA +.. section: Core and Builtins + +When PyConfig.stdlib_dir is explicitly set, it's now respected and won't be +overridden by PyConfig.home. + +.. + +.. date: 2023-07-20-11-41-16 +.. gh-issue: 106905 +.. nonce: AyZpuB +.. section: Core and Builtins + +Fix incorrect SystemError about AST constructor recursion depth mismatch. + +.. + +.. date: 2022-12-27-02-51-45 +.. gh-issue: 100445 +.. nonce: C8f6ph +.. section: Core and Builtins + +Improve error message for unterminated strings with escapes. + +.. + +.. bpo: 45759 +.. date: 2021-11-10-10-40-05 +.. nonce: WJoB3D +.. section: Core and Builtins + +Improved error messages for ``elif``/``else`` statements not matching any +valid statements. Patch by Jeremiah Vivian. + +.. + +.. date: 2023-11-14-18-43-55 +.. gh-issue: 111942 +.. nonce: x1pnrj +.. section: Library + +Fix SystemError in the TextIOWrapper constructor with non-encodable "errors" +argument in non-debug mode. + +.. + +.. date: 2023-11-14-16-31-59 +.. gh-issue: 111995 +.. nonce: OoX8JJ +.. section: Library + +Added the ``NI_IDN`` constant to the :mod:`socket` module when present in C +at build time for use with :func:`socket.getnameinfo`. + +.. + +.. date: 2023-11-11-16-42-48 +.. gh-issue: 109538 +.. nonce: cMG5ux +.. section: Library + +Issue warning message instead of having :class:`RuntimeError` be displayed +when event loop has already been closed at :meth:`StreamWriter.__del__`. + +.. + +.. date: 2023-11-10-22-08-28 +.. gh-issue: 111942 +.. nonce: MDFm6v +.. section: Library + +Fix crashes in :meth:`io.TextIOWrapper.reconfigure` when pass invalid +arguments, e.g. non-string encoding. + +.. + +.. date: 2023-11-09-12-57-43 +.. gh-issue: 111460 +.. nonce: TQaz9I +.. section: Library + +:mod:`curses`: restore wide character support (including +:func:`curses.unget_wch` and :meth:`~curses.window.get_wch`) on macOS, which +was unavailable due to a regression in Python 3.12. + +.. + +.. date: 2023-11-09-10-45-56 +.. gh-issue: 103791 +.. nonce: sdfkja +.. section: Library + +:class:`contextlib.suppress` now supports suppressing exceptions raised as +part of a :exc:`BaseExceptionGroup`, in addition to the recent support for +:exc:`ExceptionGroup`. + +.. + +.. date: 2023-11-08-23-32-03 +.. gh-issue: 111835 +.. nonce: ufFiuW +.. section: Library + +The :class:`mmap.mmap` class now has an :meth:`~mmap.mmap.seekable` method +that can be used where it requires a file-like object with seekable and the +:meth:`~mmap.mmap.seek` method return the new absolute position. Patch by +Donghee Na. + +.. + +.. date: 2023-11-08-15-58-57 +.. gh-issue: 111804 +.. nonce: uAXTOL +.. section: Library + +Remove posix.fallocate() under WASI as the underlying posix_fallocate() is +not available in WASI preview2. + +.. + +.. date: 2023-11-08-11-50-49 +.. gh-issue: 111841 +.. nonce: iSqdQf +.. section: Library + +Fix truncating arguments on an embedded null character in :meth:`os.putenv` +and :meth:`os.unsetenv` on Windows. + +.. + +.. date: 2023-11-08-07-42-53 +.. gh-issue: 111768 +.. nonce: g-WpnV +.. section: Library + +:func:`wsgiref.util.is_hop_by_hop` is now exposed correctly in ``__all__``. + +.. + +.. date: 2023-11-04-21-12-27 +.. gh-issue: 80731 +.. nonce: Wq51xg +.. section: Library + +Avoid executing the default function in :class:`cmd.Cmd` in an except block + +.. + +.. date: 2023-11-04-10-24-25 +.. gh-issue: 111541 +.. nonce: x0RBI1 +.. section: Library + +Fix :mod:`doctest` for :exc:`SyntaxError` not-builtin subclasses. + +.. + +.. date: 2023-11-04-01-20-23 +.. gh-issue: 111719 +.. nonce: fUiKBD +.. section: Library + +Add extra argument validation for ``alias`` command in :mod:`pdb` + +.. + +.. date: 2023-11-02-12-15-46 +.. gh-issue: 111482 +.. nonce: FWqZIU +.. section: Library + +:mod:`time`: Make :func:`time.clock_gettime()` and +:func:`time.clock_gettime_ns()` functions up to 2x faster by faster calling +convention. Patch by Victor Stinner. + +.. + +.. date: 2023-11-01-14-03-24 +.. gh-issue: 110894 +.. nonce: 7-wZxC +.. section: Library + +Call loop exception handler for exceptions in ``client_connected_cb`` of +:func:`asyncio.start_server` so that applications can handle it. Patch by +Kumar Aditya. + +.. + +.. date: 2023-10-31-07-46-56 +.. gh-issue: 111531 +.. nonce: 6zUV_G +.. section: Library + +Fix reference leaks in ``bind_class()`` and ``bind_all()`` methods of +:mod:`tkinter` widgets. + +.. + +.. date: 2023-10-30-14-47-23 +.. gh-issue: 111246 +.. nonce: QJ_ehs +.. section: Library + +:meth:`asyncio.loop.create_unix_server` will now automatically remove the +Unix socket when the server is closed. + +.. + +.. date: 2023-10-30-08-50-46 +.. gh-issue: 111356 +.. nonce: Bc8LvA +.. section: Library + +Added :func:`io.text_encoding()`, :data:`io.DEFAULT_BUFFER_SIZE`, and +:class:`io.IncrementalNewlineDecoder` to ``io.__all__``. + +.. + +.. date: 2023-10-29-03-46-27 +.. gh-issue: 66425 +.. nonce: FWTdDo +.. section: Library + +Remove the code to set the REMOTE_HOST header from wsgiref module, as it is +unreachable. This header is used for performance reasons, which is not +necessary in the wsgiref module. + +.. + +.. date: 2023-10-28-22-11-11 +.. gh-issue: 111429 +.. nonce: mJGxuQ +.. section: Library + +Speed up :meth:`pathlib.PurePath.relative_to` and +:meth:`~pathlib.PurePath.is_relative_to`. + +.. + +.. date: 2023-10-28-04-21-17 +.. gh-issue: 111342 +.. nonce: m8Ln1k +.. section: Library + +Fixed typo in :func:`math.sumprod`. + +.. + +.. date: 2023-10-27-12-46-56 +.. gh-issue: 68166 +.. nonce: 0EbWW4 +.. section: Library + +Remove mention of not supported "vsapi" element type in +:meth:`tkinter.ttk.Style.element_create`. Add tests for ``element_create()`` +and other ``ttk.Style`` methods. Add examples for ``element_create()`` in +the documentation. + +.. + +.. date: 2023-10-27-09-56-20 +.. gh-issue: 111388 +.. nonce: SlmDbC +.. section: Library + +Add ``show_group`` parameter to :func:`traceback.format_exception_only`, +which allows to format :exc:`ExceptionGroup` instances. + +.. + +.. date: 2023-10-25-11-54-00 +.. gh-issue: 79033 +.. nonce: 5ePgFl +.. section: Library + +Another attempt at fixing :func:`asyncio.Server.wait_closed()`. It now +blocks until both conditions are true: the server is closed, *and* there are +no more active connections. (This means that in some cases where in 3.12.0 +this function would *incorrectly* have returned immediately, it will now +block; in particular, when there are no active connections but the server +hasn't been closed yet.) + +.. + +.. date: 2023-10-25-11-13-35 +.. gh-issue: 111259 +.. nonce: z7ndeA +.. section: Library + +Optimize recursive wildcards in :mod:`pathlib`. + +.. + +.. date: 2023-10-25-08-42-05 +.. gh-issue: 111295 +.. nonce: H2K4lf +.. section: Library + +Fix :mod:`time` not checking for errors when initializing. + +.. + +.. date: 2023-10-24-12-20-46 +.. gh-issue: 111253 +.. nonce: HFywSK +.. section: Library + +Add error checking during :mod:`!_socket` module init. + +.. + +.. date: 2023-10-24-12-09-46 +.. gh-issue: 111251 +.. nonce: urFYtn +.. section: Library + +Fix :mod:`_blake2` not checking for errors when initializing. + +.. + +.. date: 2023-10-23-23-14-54 +.. gh-issue: 111233 +.. nonce: sCdCC0 +.. section: Library + +Fix :mod:`select` not checking for errors when initializing. + +.. + +.. date: 2023-10-23-22-40-47 +.. gh-issue: 111230 +.. nonce: k3Jm84 +.. section: Library + +Fix :mod:`ssl` not checking for errors when initializing. + +.. + +.. date: 2023-10-23-13-53-58 +.. gh-issue: 111174 +.. nonce: Oohmzd +.. section: Library + +Fix crash in :meth:`io.BytesIO.getbuffer` called repeatedly for empty +BytesIO. + +.. + +.. date: 2023-10-22-21-28-05 +.. gh-issue: 111187 +.. nonce: _W11Ab +.. section: Library + +Postpone removal version for locale.getdefaultlocale() to Python 3.15. + +.. + +.. date: 2023-10-21-13-57-06 +.. gh-issue: 111159 +.. nonce: GoHp7s +.. section: Library + +Fix :mod:`doctest` output comparison for exceptions with notes. + +.. + +.. date: 2023-10-20-15-29-10 +.. gh-issue: 110910 +.. nonce: u2oPwX +.. section: Library + +Fix invalid state handling in :class:`asyncio.TaskGroup` and +:class:`asyncio.Timeout`. They now raise proper RuntimeError if they are +improperly used and are left in consistent state after this. + +.. + +.. date: 2023-10-19-22-46-34 +.. gh-issue: 111092 +.. nonce: hgut12 +.. section: Library + +Make turtledemo run without default root enabled. + +.. + +.. date: 2023-10-16-18-41-51 +.. gh-issue: 110944 +.. nonce: CmUKXo +.. section: Library + +Support alias and convenience vars for :mod:`pdb` completion + +.. + +.. date: 2023-10-15-08-08-26 +.. gh-issue: 110745 +.. nonce: mxEkh0 +.. section: Library + +Added *newline* parameter to :meth:`pathlib.Path.read_text`. Patch by Junya +Okabe. + +.. + +.. date: 2023-10-14-21-33-57 +.. gh-issue: 84583 +.. nonce: -Cmn4_ +.. section: Library + +Make :mod:`pdb` enter post-mortem mode even for :exc:`SyntaxError` + +.. + +.. date: 2023-10-14-20-15-53 +.. gh-issue: 80675 +.. nonce: _M-cQC +.. section: Library + +Set ``f_trace_lines = True`` on all frames upon :func:`pdb.set_trace()` + +.. + +.. date: 2023-10-13-06-47-20 +.. gh-issue: 110771 +.. nonce: opwdlc +.. section: Library + +Expose the setup and cleanup portions of ``asyncio.run_forever()`` as the +standalone methods ``asyncio.run_forever_setup()`` and +``asyncio.run_forever_cleanup()``. This allows for tighter integration with +GUI event loops. + +.. + +.. date: 2023-10-12-15-16-44 +.. gh-issue: 110774 +.. nonce: AdCb5A +.. section: Library + +Support setting the :class:`asyncio.Runner` loop_factory kwarg in +:class:`unittest.IsolatedAsyncioTestCase` + +.. + +.. date: 2023-10-10-17-56-41 +.. gh-issue: 110392 +.. nonce: 6g6CnP +.. section: Library + +Fix :func:`tty.setraw` and :func:`tty.setcbreak`: previously they returned +partially modified list of the original tty attributes. +:func:`tty.cfmakeraw` and :func:`tty.cfmakecbreak` now make a copy of the +list of special characters before modifying it. + +.. + +.. date: 2023-10-09-23-59-04 +.. gh-issue: 59013 +.. nonce: qPbS-G +.. section: Library + +Make line number of function breakpoint more precise in :mod:`pdb` + +.. + +.. date: 2023-10-08-18-38-09 +.. gh-issue: 88434 +.. nonce: 2Q_IkG +.. section: Library + +Emit deprecation warning for non-integer numbers in :mod:`gettext` functions +and methods that consider plural forms even if the translation was not +found. + +.. + +.. date: 2023-10-08-14-17-06 +.. gh-issue: 110395 +.. nonce: _tdCsV +.. section: Library + +Ensure that :func:`select.kqueue` objects correctly appear as closed in +forked children, to prevent operations on an invalid file descriptor. + +.. + +.. date: 2023-10-02-05-23-27 +.. gh-issue: 110196 +.. nonce: djwt0z +.. section: Library + +Add ``__reduce__`` method to :class:`IPv6Address` in order to keep +``scope_id`` + +.. + +.. date: 2023-09-25-20-05-41 +.. gh-issue: 109747 +.. nonce: _cRJH8 +.. section: Library + +Improve errors for unsupported look-behind patterns. Now re.error is raised +instead of OverflowError or RuntimeError for too large width of look-behind +pattern. + +.. + +.. date: 2023-09-15-12-30-21 +.. gh-issue: 109466 +.. nonce: 6ah-aw +.. section: Library + +Add the :attr:`ipaddress.IPv4Address.ipv6_mapped` property, which retuns the +IPv4-mapped IPv6 address. + +.. + +.. date: 2023-09-08-12-10-10 +.. gh-issue: 85098 +.. nonce: DfQbeJ +.. section: Library + +Implement the CLI of the :mod:`symtable` module and improve the repr of +:class:`~symtable.Symbol`. + +.. + +.. date: 2023-09-02-16-07-23 +.. gh-issue: 108791 +.. nonce: fBcAqh +.. section: Library + +Improved error handling in :mod:`pdb` command line interface, making it +produce more concise error messages. + +.. + +.. date: 2023-08-30-19-10-35 +.. gh-issue: 105931 +.. nonce: Lpwve8 +.. section: Library + +Change :mod:`compileall` to only strip the stripdir prefix from the full +path recorded in the compiled ``.pyc`` file, when the prefix matches the +start of the full path in its entirety. When the prefix does not match, no +stripping is performed and a warning to this effect is displayed. + +Previously all path components of the stripdir prefix that matched the full +path were removed, while those that did not match were left alone (including +ones interspersed between matching components). + +.. + +.. date: 2023-07-29-19-00-39 +.. gh-issue: 107431 +.. nonce: 1GzJ2p +.. section: Library + +Make the ``DictProxy`` and ``ListProxy`` types in +:mod:`multiprocessing.managers` :ref:`Generic Alias +Types` for ``[]`` use in typing contexts. + +.. + +.. date: 2023-07-13-00-24-52 +.. gh-issue: 72904 +.. nonce: Yn5-j0 +.. section: Library + +Add :func:`glob.translate`. This function converts a pathname with +shell-style wildcards to a regular expression. + +.. + +.. date: 2023-05-30-02-01-14 +.. gh-issue: 90026 +.. nonce: FyCXw8 +.. section: Library + +Define ``USE_XATTRS`` on Cygwin so that XATTR-related functions in the +:mod:`os` module become available. + +.. + +.. date: 2023-04-26-16-37-00 +.. gh-issue: 90890 +.. nonce: fIag4w +.. section: Library + +New methods :meth:`mailbox.Maildir.get_info`, +:meth:`mailbox.Maildir.set_info`, :meth:`mailbox.Maildir.get_flags`, +:meth:`mailbox.Maildir.set_flags`, :meth:`mailbox.Maildir.add_flag`, +:meth:`mailbox.Maildir.remove_flag`. These methods speed up accessing a +message's info and/or flags and are useful when it is not necessary to +access the message's contents, as when iterating over a Maildir to find +messages with specific flags. + +.. + +.. date: 2023-04-15-14-45-21 +.. gh-issue: 102956 +.. nonce: Z6qeUy +.. section: Library + +Fix returning of empty byte strings after seek in zipfile module + +.. + +.. date: 2023-03-22-02-01-30 +.. gh-issue: 102895 +.. nonce: HiEqaZ +.. section: Library + +Added a parameter ``local_exit`` for :func:`code.interact` to prevent +``exit()`` and ``quit`` from closing ``sys.stdin`` and raise ``SystemExit``. + +.. + +.. date: 2022-10-14-21-11-10 +.. gh-issue: 97928 +.. nonce: Pdxh1G +.. section: Library + +Change the behavior of :meth:`tkinter.Text.count`. It now always returns an +integer if one or less counting options are specified. Previously it could +return a single count as a 1-tuple, an integer (only if option ``"update"`` +was specified) or ``None`` if no items found. The result is now the same if +``wantobjects`` is set to ``0``. + +.. + +.. date: 2022-10-05-15-01-36 +.. gh-issue: 96954 +.. nonce: ezwkrU +.. section: Library + +Switch the storage of the unicode codepoint names to use a different +data-structure, a `directed acyclic word graph +`_. +This makes the unicodedata shared library about 440 KiB smaller. Contributed +by Carl Friedrich Bolz-Tereick using code from the PyPy project. + +.. + +.. date: 2022-05-28-20-55-07 +.. gh-issue: 73561 +.. nonce: YRmAvy +.. section: Library + +Omit the interface scope from an IPv6 address when used as Host header by +:mod:`http.client`. + +.. + +.. date: 2022-05-06-15-49-57 +.. gh-issue: 86826 +.. nonce: rf006W +.. section: Library + +:mod:`zipinfo` now supports the full range of values in the TZ string +determined by RFC 8536 and detects all invalid formats. Both Python and C +implementations now raise exceptions of the same type on invalid data. + +.. + +.. date: 2023-11-17-15-20-41 +.. gh-issue: 111808 +.. nonce: jtIayt +.. section: Tests + +Make the default value of ``test.support.infinite_recursion()`` to be +conditional based on whether optimizations were used when compiling the +interpreter. This helps with platforms like WASI whose stack size is greatly +restricted in debug builds. + +.. + +.. date: 2023-11-03-18-59-13 +.. gh-issue: 110722 +.. nonce: jvT1pb +.. section: Tests + +Gathering line coverage of standard libraries within the regression test +suite is now precise, as well as much faster. Patch by Łukasz Langa. + +.. + +.. date: 2023-10-31-22-09-25 +.. gh-issue: 110367 +.. nonce: UhQi44 +.. section: Tests + +Make regrtest ``--verbose3`` option compatible with ``--huntrleaks -jN`` +options. The ``./python -m test -j1 -R 3:3 --verbose3`` command now works as +expected. Patch by Victor Stinner. + +.. + +.. date: 2023-10-21-19-27-36 +.. gh-issue: 111165 +.. nonce: FU6mUk +.. section: Tests + +Remove no longer used functions ``run_unittest()`` and ``run_doctest()`` +from the :mod:`test.support` module. + +.. + +.. date: 2023-10-21-00-10-36 +.. gh-issue: 110932 +.. nonce: jktjJU +.. section: Tests + +Fix regrtest if the ``SOURCE_DATE_EPOCH`` environment variable is defined: +use the variable value as the random seed. Patch by Victor Stinner. + +.. + +.. date: 2023-10-17-17-54-36 +.. gh-issue: 110995 +.. nonce: Fx8KRD +.. section: Tests + +test_gdb: Fix detection of gdb built without Python scripting support. Patch +by Victor Stinner. + +.. + +.. date: 2023-10-16-13-47-24 +.. gh-issue: 110918 +.. nonce: aFgZK3 +.. section: Tests + +Test case matching patterns specified by options ``--match``, ``--ignore``, +``--matchfile`` and ``--ignorefile`` are now tested in the order of +specification, and the last match determines whether the test case be run or +ignored. + +.. + +.. date: 2023-09-15-15-00-14 +.. gh-issue: 108747 +.. nonce: ql0owS +.. section: Tests + +Add unit test for ``usercustomize`` and ``sitecustomize`` hooks from +:class:`site`. + +.. + +.. date: 2023-11-15-16-56-20 +.. gh-issue: 96954 +.. nonce: 6FYvKn +.. section: Build + +Make ``make regen-unicodedata`` work for out-of-tree builds of CPython. + +.. + +.. date: 2023-11-15-13-40-29 +.. gh-issue: 112088 +.. nonce: UJQxxh +.. section: Build + +Add ``Tools/build/regen-configure.sh`` script to regenerate the +``configure`` with an Ubuntu container image. The +``quay.io/tiran/cpython_autoconf:271`` container image +(`tiran/cpython_autoconf `_) is +no longer used. Patch by Victor Stinner. + +.. + +.. date: 2023-10-20-15-29-31 +.. gh-issue: 111046 +.. nonce: 2DxQl8 +.. section: Build + +For wasi-threads, memory is now exported to fix compatibility issues with +some wasm runtimes. + +.. + +.. date: 2023-10-17-03-10-40 +.. gh-issue: 110828 +.. nonce: 31vQ9B +.. section: Build + +AIX 32bit needs ``-latomic`` to build the :mod:`!_testcapi` extension +module. + +.. + +.. date: 2023-10-17-01-56-11 +.. gh-issue: 85283 +.. nonce: V156T2 +.. section: Build + +The ``errno``, ``md5``, ``resource``, ``winsound``, ``_ctypes_test``, +``_multiprocessing.posixshmem``, ``_scproxy``, ``_stat``, +``_testimportmultiple`` and ``_uuid`` C extensions are now built with the +:ref:`limited C API `. Patch by Victor Stinner. + +.. + +.. date: 2023-11-13-22-35-27 +.. gh-issue: 111856 +.. nonce: vEtA5z +.. section: Windows + +Fixes :func:`~os.fstat` on file systems that do not support file ID +requests. This includes FAT32 and exFAT. + +.. + +.. date: 2023-10-25-05-01-28 +.. gh-issue: 111293 +.. nonce: FSsLT6 +.. section: Windows + +Fix :data:`os.DirEntry.inode` dropping higher 64 bits of a file id on some +filesystems on Windows. + +.. + +.. date: 2023-10-19-21-46-18 +.. gh-issue: 110913 +.. nonce: CWlPfg +.. section: Windows + +WindowsConsoleIO now correctly chunks large buffers without splitting up +UTF-8 sequences. + +.. + +.. date: 2023-10-31-22-13-05 +.. gh-issue: 59703 +.. nonce: SML6Ag +.. section: macOS + +For macOS framework builds, in ``getpath.c`` use the system ``dladdr`` +function to find the path to the shared library rather than depending on +deprecated macOS APIs. + +.. + +.. date: 2023-10-18-17-26-36 +.. gh-issue: 110950 +.. nonce: sonoma +.. section: macOS + +Update macOS installer to include an upstream Tcl/Tk fix for the ``Secure +coding is not enabled for restorable state!`` warning encountered in Tkinter +on macOS 14 Sonoma. + +.. + +.. date: 2023-10-18-01-40-36 +.. gh-issue: 111015 +.. nonce: NaLI2L +.. section: macOS + +Ensure that IDLE.app and Python Launcher.app are installed with appropriate +permissions on macOS builds. + +.. + +.. date: 2023-09-02-08-49-57 +.. gh-issue: 71383 +.. nonce: Ttkchg +.. section: macOS + +Update macOS installer to include an upstream Tcl/Tk fix for the +``ttk::ThemeChanged`` error encountered in Tkinter. + +.. + +.. date: 2023-08-30-16-33-57 +.. gh-issue: 92603 +.. nonce: ATkKVO +.. section: macOS + +Update macOS installer to include a fix accepted by upstream Tcl/Tk for a +crash encountered after the first :meth:`tkinter.Tk` instance is destroyed. + +.. + +.. bpo: 35668 +.. date: 2019-01-07-06-18-25 +.. nonce: JimxP5 +.. section: IDLE + +Add docstrings to the IDLE debugger module. Fix two bugs: initialize +Idb.botframe (should be in Bdb); in Idb.in_rpc_code, check whether +prev_frame is None before trying to use it. Greatly expand test_debugger. + +.. + +.. date: 2023-11-09-13-04-29 +.. gh-issue: 111903 +.. nonce: 7Prryr +.. section: Tools/Demos + +Argument Clinic now supports the ``@critical_section`` directive that +instructs Argument Clinic to generate a critical section around the function +call, which locks the ``self`` object in ``--disable-gil`` builds. Patch by +Sam Gross. + +.. + +.. date: 2023-11-15-18-36-21 +.. gh-issue: 112026 +.. nonce: _Yybr5 +.. section: C API + +Add again the private ``_PyThreadState_UncheckedGet()`` function as an alias +to the new public :c:func:`PyThreadState_GetUnchecked` function. Patch by +Victor Stinner. + +.. + +.. date: 2023-11-15-17-10-09 +.. gh-issue: 112026 +.. nonce: ts9yyn +.. section: C API + +Restore the removed ``_PyDict_GetItemStringWithError()`` function. It is +used by numpy. Patch by Victor Stinner. + +.. + +.. date: 2023-11-15-16-07-57 +.. gh-issue: 112026 +.. nonce: bnr8dd +.. section: C API + +Restore removed private C API functions, macros and structures which have no +simple replacement for now: + +* _PyDict_GetItem_KnownHash() +* _PyDict_NewPresized() +* _PyHASH_BITS +* _PyHASH_IMAG +* _PyHASH_INF +* _PyHASH_MODULUS +* _PyHASH_MULTIPLIER +* _PyLong_Copy() +* _PyLong_FromDigits() +* _PyLong_New() +* _PyLong_Sign() +* _PyObject_CallMethodId() +* _PyObject_CallMethodNoArgs() +* _PyObject_CallMethodOneArg() +* _PyObject_CallOneArg() +* _PyObject_EXTRA_INIT +* _PyObject_FastCallDict() +* _PyObject_GetAttrId() +* _PyObject_Vectorcall() +* _PyObject_VectorcallMethod() +* _PyStack_AsDict() +* _PyThread_CurrentFrames() +* _PyUnicodeWriter structure +* _PyUnicodeWriter_Dealloc() +* _PyUnicodeWriter_Finish() +* _PyUnicodeWriter_Init() +* _PyUnicodeWriter_Prepare() +* _PyUnicodeWriter_PrepareKind() +* _PyUnicodeWriter_WriteASCIIString() +* _PyUnicodeWriter_WriteChar() +* _PyUnicodeWriter_WriteLatin1String() +* _PyUnicodeWriter_WriteStr() +* _PyUnicodeWriter_WriteSubstring() +* _PyUnicode_AsString() +* _PyUnicode_FromId() +* _PyVectorcall_Function() +* _Py_IDENTIFIER() +* _Py_c_abs() +* _Py_c_diff() +* _Py_c_neg() +* _Py_c_pow() +* _Py_c_prod() +* _Py_c_quot() +* _Py_c_sum() +* _Py_static_string() +* _Py_static_string_init() + +Patch by Victor Stinner. + +.. + +.. date: 2023-11-13-17-57-11 +.. gh-issue: 112026 +.. nonce: WJLJcI +.. section: C API + +Add again ```` and ```` includes in ``Python.h``, but +don't include them in the limited C API version 3.13 and newer. Patch by +Victor Stinner. + +.. + +.. date: 2023-11-10-10-24-28 +.. gh-issue: 111956 +.. nonce: ImE6Cx +.. section: C API + +Add internal-only one-time initialization API: ``_PyOnceFlag`` and +``_PyOnceFlag_CallOnce``. + +.. + +.. date: 2023-11-10-10-21-38 +.. gh-issue: 111262 +.. nonce: 2utB5m +.. section: C API + +Add :c:func:`PyDict_Pop` and :c:func:`PyDict_PopString` functions: remove a +key from a dictionary and optionally return the removed value. This is +similar to :meth:`dict.pop`, but without the default value and not raising +:exc:`KeyError` if the key missing. Patch by Stefan Behnel and Victor +Stinner. + +.. + +.. date: 2023-11-08-20-28-03 +.. gh-issue: 111863 +.. nonce: RPeFAX +.. section: C API + +Rename ``Py_NOGIL`` to ``Py_GIL_DISABLED``. Patch by Hugo van Kemenade. + +.. + +.. date: 2023-11-08-18-37-19 +.. gh-issue: 111138 +.. nonce: 3Ypq8h +.. section: C API + +Add :c:func:`PyList_Extend` and :c:func:`PyList_Clear` functions: similar to +Python ``list.extend()`` and ``list.clear()`` methods. Patch by Victor +Stinner. + +.. + +.. date: 2023-10-31-18-22-03 +.. gh-issue: 108765 +.. nonce: _beYv8 +.. section: C API + +On Windows, ``Python.h`` no longer includes the ```` standard +header file. If needed, it should now be included explicitly. Patch by +Victor Stinner. + +.. + +.. date: 2023-10-31-14-58-17 +.. gh-issue: 111569 +.. nonce: _V8iu4 +.. section: C API + +Implement "Python Critical Sections" from :pep:`703`. These are macros to +help replace the GIL with per-object locks in the ``--disable-gil`` build of +CPython. The macros are no-ops in the default build. + +.. + +.. date: 2023-10-30-18-13-01 +.. gh-issue: 111506 +.. nonce: EUdO22 +.. section: C API + +In the limited C API version 3.13, :c:func:`Py_SET_REFCNT` function is now +implemented as an opaque function call. Patch by Victor Stinner. + +.. + +.. date: 2023-10-19-22-39-24 +.. gh-issue: 108082 +.. nonce: uJytvc +.. section: C API + +Add :c:func:`PyErr_FormatUnraisable` function. + +.. + +.. date: 2023-10-17-10-21-59 +.. gh-issue: 110964 +.. nonce: OxqEjd +.. section: C API + +Move the undocumented private _PyArg functions and _PyArg_Parser structure +to internal C API (``pycore_modsupport.h``). Patch by Victor Stinner. + +.. + +.. date: 2023-10-13-14-18-06 +.. gh-issue: 110815 +.. nonce: tEFLVl +.. section: C API + +Support non-ASCII keyword names in :c:func:`PyArg_ParseTupleAndKeywords`. + +.. + +.. date: 2023-10-02-23-08-53 +.. gh-issue: 109587 +.. nonce: UqqnDY +.. section: C API + +Introduced :c:func:`PyUnstable_PerfTrampoline_CompileCode`, +:c:func:`PyUnstable_PerfTrampoline_SetPersistAfterFork` and +:c:func:`PyUnstable_CopyPerfMapFile`. These functions allow extension +modules to initialize trampolines eagerly, after the application is "warmed +up". This makes it possible to have perf-trampolines running in an +always-enabled fashion. + +.. + +.. date: 2023-08-28-17-40-51 +.. gh-issue: 85283 +.. nonce: raFNiD +.. section: C API + +Add the :c:func:`PySys_Audit` function to the limited C API. Patch by Victor +Stinner. + +.. + +.. date: 2023-08-28-17-34-10 +.. gh-issue: 85283 +.. nonce: f1zXcc +.. section: C API + +Add :c:func:`PyMem_RawMalloc`, :c:func:`PyMem_RawCalloc`, +:c:func:`PyMem_RawRealloc` and :c:func:`PyMem_RawFree` to the limited C API. +Patch by Victor Stinner. + +.. + +.. date: 2023-07-12-12-14-52 +.. gh-issue: 106672 +.. nonce: fkRjmi +.. section: C API + +Functions :c:func:`PyDict_GetItem`, :c:func:`PyDict_GetItemString`, +:c:func:`PyMapping_HasKey`, :c:func:`PyMapping_HasKeyString`, +:c:func:`PyObject_HasAttr`, :c:func:`PyObject_HasAttrString`, and +:c:func:`PySys_GetObject`, which clear all errors occurred during calling +the function, report now them using :func:`sys.unraisablehook`. + +.. + +.. date: 2023-06-08-21-12-44 +.. gh-issue: 67565 +.. nonce: UkK3x- +.. section: C API + +Remove redundant C-contiguity check in :file:`getargs.c`, :mod:`binascii`, +:mod:`ssl` and Argument Clinic. Patched by Stefan Krah and Furkan Onder diff --git a/Misc/NEWS.d/next/Build/2023-10-17-01-56-11.gh-issue-85283.V156T2.rst b/Misc/NEWS.d/next/Build/2023-10-17-01-56-11.gh-issue-85283.V156T2.rst deleted file mode 100644 index 399054040f2ec7..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-10-17-01-56-11.gh-issue-85283.V156T2.rst +++ /dev/null @@ -1,5 +0,0 @@ -The ``errno``, ``md5``, ``resource``, ``winsound``, ``_ctypes_test``, -``_multiprocessing.posixshmem``, ``_scproxy``, ``_stat``, -``_testimportmultiple`` and ``_uuid`` C extensions are now built with the -:ref:`limited C API `. -Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Build/2023-10-17-03-10-40.gh-issue-110828.31vQ9B.rst b/Misc/NEWS.d/next/Build/2023-10-17-03-10-40.gh-issue-110828.31vQ9B.rst deleted file mode 100644 index 13647fee058021..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-10-17-03-10-40.gh-issue-110828.31vQ9B.rst +++ /dev/null @@ -1 +0,0 @@ -AIX 32bit needs ``-latomic`` to build the :mod:`!_testcapi` extension module. diff --git a/Misc/NEWS.d/next/Build/2023-10-20-15-29-31.gh-issue-111046.2DxQl8.rst b/Misc/NEWS.d/next/Build/2023-10-20-15-29-31.gh-issue-111046.2DxQl8.rst deleted file mode 100644 index 446b8b612862f9..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-10-20-15-29-31.gh-issue-111046.2DxQl8.rst +++ /dev/null @@ -1 +0,0 @@ -For wasi-threads, memory is now exported to fix compatibility issues with some wasm runtimes. diff --git a/Misc/NEWS.d/next/Build/2023-11-15-13-40-29.gh-issue-112088.UJQxxh.rst b/Misc/NEWS.d/next/Build/2023-11-15-13-40-29.gh-issue-112088.UJQxxh.rst deleted file mode 100644 index b176d06ec8d749..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-11-15-13-40-29.gh-issue-112088.UJQxxh.rst +++ /dev/null @@ -1,5 +0,0 @@ -Add ``Tools/build/regen-configure.sh`` script to regenerate the ``configure`` -with an Ubuntu container image. The ``quay.io/tiran/cpython_autoconf:271`` -container image (`tiran/cpython_autoconf -`_) is no longer used. Patch by -Victor Stinner. diff --git a/Misc/NEWS.d/next/Build/2023-11-15-16-56-20.gh-issue-96954.6FYvKn.rst b/Misc/NEWS.d/next/Build/2023-11-15-16-56-20.gh-issue-96954.6FYvKn.rst deleted file mode 100644 index e9a30e5f4bbaa7..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-11-15-16-56-20.gh-issue-96954.6FYvKn.rst +++ /dev/null @@ -1 +0,0 @@ -Make ``make regen-unicodedata`` work for out-of-tree builds of CPython. diff --git a/Misc/NEWS.d/next/C API/2023-06-08-21-12-44.gh-issue-67565.UkK3x-.rst b/Misc/NEWS.d/next/C API/2023-06-08-21-12-44.gh-issue-67565.UkK3x-.rst deleted file mode 100644 index 0e50976b0e1ddf..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-06-08-21-12-44.gh-issue-67565.UkK3x-.rst +++ /dev/null @@ -1 +0,0 @@ -Remove redundant C-contiguity check in :file:`getargs.c`, :mod:`binascii`, :mod:`ssl` and Argument Clinic. Patched by Stefan Krah and Furkan Onder diff --git a/Misc/NEWS.d/next/C API/2023-07-12-12-14-52.gh-issue-106672.fkRjmi.rst b/Misc/NEWS.d/next/C API/2023-07-12-12-14-52.gh-issue-106672.fkRjmi.rst deleted file mode 100644 index 420f43175e595a..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-07-12-12-14-52.gh-issue-106672.fkRjmi.rst +++ /dev/null @@ -1,5 +0,0 @@ -Functions :c:func:`PyDict_GetItem`, :c:func:`PyDict_GetItemString`, -:c:func:`PyMapping_HasKey`, :c:func:`PyMapping_HasKeyString`, -:c:func:`PyObject_HasAttr`, :c:func:`PyObject_HasAttrString`, and -:c:func:`PySys_GetObject`, which clear all errors occurred during calling -the function, report now them using :func:`sys.unraisablehook`. diff --git a/Misc/NEWS.d/next/C API/2023-08-28-17-34-10.gh-issue-85283.f1zXcc.rst b/Misc/NEWS.d/next/C API/2023-08-28-17-34-10.gh-issue-85283.f1zXcc.rst deleted file mode 100644 index 1c25fdb6ec8d2f..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-08-28-17-34-10.gh-issue-85283.f1zXcc.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add :c:func:`PyMem_RawMalloc`, :c:func:`PyMem_RawCalloc`, -:c:func:`PyMem_RawRealloc` and :c:func:`PyMem_RawFree` to the limited C API. -Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-08-28-17-40-51.gh-issue-85283.raFNiD.rst b/Misc/NEWS.d/next/C API/2023-08-28-17-40-51.gh-issue-85283.raFNiD.rst deleted file mode 100644 index 45ffda3d23dd22..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-08-28-17-40-51.gh-issue-85283.raFNiD.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add the :c:func:`PySys_Audit` function to the limited C API. Patch by Victor -Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-10-02-23-08-53.gh-issue-109587.UqqnDY.rst b/Misc/NEWS.d/next/C API/2023-10-02-23-08-53.gh-issue-109587.UqqnDY.rst deleted file mode 100644 index c6fa24f024c20c..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-10-02-23-08-53.gh-issue-109587.UqqnDY.rst +++ /dev/null @@ -1,2 +0,0 @@ -Introduced :c:func:`PyUnstable_PerfTrampoline_CompileCode`, :c:func:`PyUnstable_PerfTrampoline_SetPersistAfterFork` and -:c:func:`PyUnstable_CopyPerfMapFile`. These functions allow extension modules to initialize trampolines eagerly, after the application is "warmed up". This makes it possible to have perf-trampolines running in an always-enabled fashion. diff --git a/Misc/NEWS.d/next/C API/2023-10-13-14-18-06.gh-issue-110815.tEFLVl.rst b/Misc/NEWS.d/next/C API/2023-10-13-14-18-06.gh-issue-110815.tEFLVl.rst deleted file mode 100644 index 216d2d211644a8..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-10-13-14-18-06.gh-issue-110815.tEFLVl.rst +++ /dev/null @@ -1 +0,0 @@ -Support non-ASCII keyword names in :c:func:`PyArg_ParseTupleAndKeywords`. diff --git a/Misc/NEWS.d/next/C API/2023-10-17-10-21-59.gh-issue-110964.OxqEjd.rst b/Misc/NEWS.d/next/C API/2023-10-17-10-21-59.gh-issue-110964.OxqEjd.rst deleted file mode 100644 index e45a25d8661ca4..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-10-17-10-21-59.gh-issue-110964.OxqEjd.rst +++ /dev/null @@ -1,2 +0,0 @@ -Move the undocumented private _PyArg functions and _PyArg_Parser structure -to internal C API (``pycore_modsupport.h``). Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-10-19-22-39-24.gh-issue-108082.uJytvc.rst b/Misc/NEWS.d/next/C API/2023-10-19-22-39-24.gh-issue-108082.uJytvc.rst deleted file mode 100644 index b99a829e3f2a52..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-10-19-22-39-24.gh-issue-108082.uJytvc.rst +++ /dev/null @@ -1 +0,0 @@ -Add :c:func:`PyErr_FormatUnraisable` function. diff --git a/Misc/NEWS.d/next/C API/2023-10-30-18-13-01.gh-issue-111506.EUdO22.rst b/Misc/NEWS.d/next/C API/2023-10-30-18-13-01.gh-issue-111506.EUdO22.rst deleted file mode 100644 index f4d71fd008241a..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-10-30-18-13-01.gh-issue-111506.EUdO22.rst +++ /dev/null @@ -1,2 +0,0 @@ -In the limited C API version 3.13, :c:func:`Py_SET_REFCNT` function is now -implemented as an opaque function call. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-10-31-14-58-17.gh-issue-111569._V8iu4.rst b/Misc/NEWS.d/next/C API/2023-10-31-14-58-17.gh-issue-111569._V8iu4.rst deleted file mode 100644 index c2bd3ae36e6439..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-10-31-14-58-17.gh-issue-111569._V8iu4.rst +++ /dev/null @@ -1,3 +0,0 @@ -Implement "Python Critical Sections" from :pep:`703`. These are macros to -help replace the GIL with per-object locks in the ``--disable-gil`` build of -CPython. The macros are no-ops in the default build. diff --git a/Misc/NEWS.d/next/C API/2023-10-31-18-22-03.gh-issue-108765._beYv8.rst b/Misc/NEWS.d/next/C API/2023-10-31-18-22-03.gh-issue-108765._beYv8.rst deleted file mode 100644 index 14af79eaea9a03..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-10-31-18-22-03.gh-issue-108765._beYv8.rst +++ /dev/null @@ -1,3 +0,0 @@ -On Windows, ``Python.h`` no longer includes the ```` standard -header file. If needed, it should now be included explicitly. Patch by -Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-11-08-18-37-19.gh-issue-111138.3Ypq8h.rst b/Misc/NEWS.d/next/C API/2023-11-08-18-37-19.gh-issue-111138.3Ypq8h.rst deleted file mode 100644 index 15c3b9b3a6b9ad..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-11-08-18-37-19.gh-issue-111138.3Ypq8h.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add :c:func:`PyList_Extend` and :c:func:`PyList_Clear` functions: similar to -Python ``list.extend()`` and ``list.clear()`` methods. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-11-08-20-28-03.gh-issue-111863.RPeFAX.rst b/Misc/NEWS.d/next/C API/2023-11-08-20-28-03.gh-issue-111863.RPeFAX.rst deleted file mode 100644 index 9e9145ceecf567..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-11-08-20-28-03.gh-issue-111863.RPeFAX.rst +++ /dev/null @@ -1 +0,0 @@ -Rename ``Py_NOGIL`` to ``Py_GIL_DISABLED``. Patch by Hugo van Kemenade. diff --git a/Misc/NEWS.d/next/C API/2023-11-10-10-21-38.gh-issue-111262.2utB5m.rst b/Misc/NEWS.d/next/C API/2023-11-10-10-21-38.gh-issue-111262.2utB5m.rst deleted file mode 100644 index d432b7ebd3a565..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-11-10-10-21-38.gh-issue-111262.2utB5m.rst +++ /dev/null @@ -1,4 +0,0 @@ -Add :c:func:`PyDict_Pop` and :c:func:`PyDict_PopString` functions: remove a key -from a dictionary and optionally return the removed value. This is similar to -:meth:`dict.pop`, but without the default value and not raising :exc:`KeyError` -if the key missing. Patch by Stefan Behnel and Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-11-10-10-24-28.gh-issue-111956.ImE6Cx.rst b/Misc/NEWS.d/next/C API/2023-11-10-10-24-28.gh-issue-111956.ImE6Cx.rst deleted file mode 100644 index 30ee07aa2f1f9b..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-11-10-10-24-28.gh-issue-111956.ImE6Cx.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add internal-only one-time initialization API: ``_PyOnceFlag`` and -``_PyOnceFlag_CallOnce``. diff --git a/Misc/NEWS.d/next/C API/2023-11-13-17-57-11.gh-issue-112026.WJLJcI.rst b/Misc/NEWS.d/next/C API/2023-11-13-17-57-11.gh-issue-112026.WJLJcI.rst deleted file mode 100644 index deb82ff7af7d54..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-11-13-17-57-11.gh-issue-112026.WJLJcI.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add again ```` and ```` includes in ``Python.h``, but -don't include them in the limited C API version 3.13 and newer. Patch by -Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-11-15-16-07-57.gh-issue-112026.bnr8dd.rst b/Misc/NEWS.d/next/C API/2023-11-15-16-07-57.gh-issue-112026.bnr8dd.rst deleted file mode 100644 index 5dbbde88fccd2c..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-11-15-16-07-57.gh-issue-112026.bnr8dd.rst +++ /dev/null @@ -1,51 +0,0 @@ -Restore removed private C API functions, macros and structures which have no -simple replacement for now: - -* _PyDict_GetItem_KnownHash() -* _PyDict_NewPresized() -* _PyHASH_BITS -* _PyHASH_IMAG -* _PyHASH_INF -* _PyHASH_MODULUS -* _PyHASH_MULTIPLIER -* _PyLong_Copy() -* _PyLong_FromDigits() -* _PyLong_New() -* _PyLong_Sign() -* _PyObject_CallMethodId() -* _PyObject_CallMethodNoArgs() -* _PyObject_CallMethodOneArg() -* _PyObject_CallOneArg() -* _PyObject_EXTRA_INIT -* _PyObject_FastCallDict() -* _PyObject_GetAttrId() -* _PyObject_Vectorcall() -* _PyObject_VectorcallMethod() -* _PyStack_AsDict() -* _PyThread_CurrentFrames() -* _PyUnicodeWriter structure -* _PyUnicodeWriter_Dealloc() -* _PyUnicodeWriter_Finish() -* _PyUnicodeWriter_Init() -* _PyUnicodeWriter_Prepare() -* _PyUnicodeWriter_PrepareKind() -* _PyUnicodeWriter_WriteASCIIString() -* _PyUnicodeWriter_WriteChar() -* _PyUnicodeWriter_WriteLatin1String() -* _PyUnicodeWriter_WriteStr() -* _PyUnicodeWriter_WriteSubstring() -* _PyUnicode_AsString() -* _PyUnicode_FromId() -* _PyVectorcall_Function() -* _Py_IDENTIFIER() -* _Py_c_abs() -* _Py_c_diff() -* _Py_c_neg() -* _Py_c_pow() -* _Py_c_prod() -* _Py_c_quot() -* _Py_c_sum() -* _Py_static_string() -* _Py_static_string_init() - -Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-11-15-17-10-09.gh-issue-112026.ts9yyn.rst b/Misc/NEWS.d/next/C API/2023-11-15-17-10-09.gh-issue-112026.ts9yyn.rst deleted file mode 100644 index 7d54f6b3626828..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-11-15-17-10-09.gh-issue-112026.ts9yyn.rst +++ /dev/null @@ -1,2 +0,0 @@ -Restore the removed ``_PyDict_GetItemStringWithError()`` function. It is -used by numpy. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-11-15-18-36-21.gh-issue-112026._Yybr5.rst b/Misc/NEWS.d/next/C API/2023-11-15-18-36-21.gh-issue-112026._Yybr5.rst deleted file mode 100644 index 0c39c78a78d1e0..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-11-15-18-36-21.gh-issue-112026._Yybr5.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add again the private ``_PyThreadState_UncheckedGet()`` function as an alias -to the new public :c:func:`PyThreadState_GetUnchecked` function. Patch by -Victor Stinner. diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-11-10-10-40-05.bpo-45759.WJoB3D.rst b/Misc/NEWS.d/next/Core and Builtins/2021-11-10-10-40-05.bpo-45759.WJoB3D.rst deleted file mode 100644 index 6d7b0209fc04b1..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2021-11-10-10-40-05.bpo-45759.WJoB3D.rst +++ /dev/null @@ -1 +0,0 @@ -Improved error messages for ``elif``/``else`` statements not matching any valid statements. Patch by Jeremiah Vivian. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-27-02-51-45.gh-issue-100445.C8f6ph.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-27-02-51-45.gh-issue-100445.C8f6ph.rst deleted file mode 100644 index 72f38849df9b82..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-27-02-51-45.gh-issue-100445.C8f6ph.rst +++ /dev/null @@ -1 +0,0 @@ -Improve error message for unterminated strings with escapes. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-20-11-41-16.gh-issue-106905.AyZpuB.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-20-11-41-16.gh-issue-106905.AyZpuB.rst deleted file mode 100644 index 73b75833a71284..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-07-20-11-41-16.gh-issue-106905.AyZpuB.rst +++ /dev/null @@ -1 +0,0 @@ -Fix incorrect SystemError about AST constructor recursion depth mismatch. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-08-31-11-42-16.gh-issue-106718._-57DA.rst b/Misc/NEWS.d/next/Core and Builtins/2023-08-31-11-42-16.gh-issue-106718._-57DA.rst deleted file mode 100644 index 4c564bba4122c9..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-08-31-11-42-16.gh-issue-106718._-57DA.rst +++ /dev/null @@ -1,2 +0,0 @@ -When PyConfig.stdlib_dir is explicitly set, it's now respected and won't be -overridden by PyConfig.home. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-06-12-36-11.bpo-46657.xea1T_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-06-12-36-11.bpo-46657.xea1T_.rst deleted file mode 100644 index 193b02a3b911f5..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-09-06-12-36-11.bpo-46657.xea1T_.rst +++ /dev/null @@ -1 +0,0 @@ -Add mimalloc memory allocator support. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-15-23-39-43.gh-issue-103615.WZavly.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-15-23-39-43.gh-issue-103615.WZavly.rst deleted file mode 100644 index 2a0e10b84ccf32..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-09-15-23-39-43.gh-issue-103615.WZavly.rst +++ /dev/null @@ -1 +0,0 @@ -Use local events for opcode tracing diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-30-17-30-11.gh-issue-89519.hz2pZf.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-30-17-30-11.gh-issue-89519.hz2pZf.rst deleted file mode 100644 index fd9d0edf6666c7..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-09-30-17-30-11.gh-issue-89519.hz2pZf.rst +++ /dev/null @@ -1,6 +0,0 @@ -Removed chained :class:`classmethod` descriptors (introduced in -:issue:`19072`). This can no longer be used to wrap other descriptors such -as :class:`property`. The core design of this feature was flawed and caused -a number of downstream problems. To "pass-through" a :class:`classmethod`, -consider using the :attr:`!__wrapped__` attribute that was added in Python -3.10. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-09-19-54-33.gh-issue-110543.1wrxO8.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-09-19-54-33.gh-issue-110543.1wrxO8.rst deleted file mode 100644 index 5f9571566da18d..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-09-19-54-33.gh-issue-110543.1wrxO8.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix regression in Python 3.12 where :meth:`types.CodeType.replace` would -produce a broken code object if called on a module or class code object that -contains a comprehension. Patch by Jelle Zijlstra. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-12-12-09-01.gh-issue-110481.3Er3it.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-12-12-09-01.gh-issue-110481.3Er3it.rst deleted file mode 100644 index 984548aced3df8..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-12-12-09-01.gh-issue-110481.3Er3it.rst +++ /dev/null @@ -1 +0,0 @@ -Implement biased reference counting in ``--disable-gil`` builds. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-12-17-15-23.gh-issue-110722.sjMwQe.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-12-17-15-23.gh-issue-110722.sjMwQe.rst deleted file mode 100644 index 79b941ef6879f8..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-12-17-15-23.gh-issue-110722.sjMwQe.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add :envvar:`PYTHON_PRESITE=package.module` to import a module early in the -interpreter lifecycle before ``site.py`` is executed. Python needs to be -:ref:`built in debug mode ` for this option to exist. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-13-09-21-29.gh-issue-110805.vhU7A7.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-13-09-21-29.gh-issue-110805.vhU7A7.rst deleted file mode 100644 index be90bb3564fd54..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-13-09-21-29.gh-issue-110805.vhU7A7.rst +++ /dev/null @@ -1,2 +0,0 @@ -Allow the repl to show source code and complete tracebacks. Patch by Pablo -Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-13-16-55-55.gh-issue-109094.ziL4cJ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-13-16-55-55.gh-issue-109094.ziL4cJ.rst deleted file mode 100644 index 332afb61e349c9..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-13-16-55-55.gh-issue-109094.ziL4cJ.rst +++ /dev/null @@ -1,3 +0,0 @@ -Replace ``prev_instr`` on the interpreter frame by ``instr_ptr`` which -points to the beginning of the instruction that is currently executing (or -will execute once the frame resumes). diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-14-12-19-34.gh-issue-110864.-baPDE.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-14-12-19-34.gh-issue-110864.-baPDE.rst deleted file mode 100644 index 3d79a7124bd2f2..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-14-12-19-34.gh-issue-110864.-baPDE.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix argument parsing by ``_PyArg_UnpackKeywordsWithVararg`` for functions -defining pos-or-keyword, vararg, and kw-only parameters. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-15-20-45-35.gh-issue-110892.oA6eVY.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-15-20-45-35.gh-issue-110892.oA6eVY.rst deleted file mode 100644 index 4422273d51757c..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-15-20-45-35.gh-issue-110892.oA6eVY.rst +++ /dev/null @@ -1 +0,0 @@ -Return ``NULL`` for ``PyTrace_RETURN`` events caused by an exception diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-15-22-18-45.gh-issue-109894.UAmo06.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-15-22-18-45.gh-issue-109894.UAmo06.rst deleted file mode 100644 index 214853660c5a57..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-15-22-18-45.gh-issue-109894.UAmo06.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed crash due to improperly initialized static :exc:`MemoryError` in subinterpreter. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-16-12-12-48.gh-issue-110912.uEJGi_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-16-12-12-48.gh-issue-110912.uEJGi_.rst deleted file mode 100644 index d70d45ebb931ea..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-16-12-12-48.gh-issue-110912.uEJGi_.rst +++ /dev/null @@ -1,2 +0,0 @@ -Correctly display the traceback for :exc:`MemoryError` exceptions using the -:mod:`traceback` module. Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-16-15-51-37.gh-issue-109214.-RGTFH.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-16-15-51-37.gh-issue-109214.-RGTFH.rst deleted file mode 100644 index c24f18cee71fa0..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-16-15-51-37.gh-issue-109214.-RGTFH.rst +++ /dev/null @@ -1 +0,0 @@ -Remove unnecessary instruction pointer updates before returning from frames. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-17-11-03-45.gh-issue-110938.X3sbMb.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-17-11-03-45.gh-issue-110938.X3sbMb.rst deleted file mode 100644 index a2f9319e69de9b..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-17-11-03-45.gh-issue-110938.X3sbMb.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix error messages for indented blocks with functions and classes with -generic type parameters. Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-20-23-14-06.gh-issue-111123.jjVc3M.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-20-23-14-06.gh-issue-111123.jjVc3M.rst deleted file mode 100644 index f2cebe287db3ee..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-20-23-14-06.gh-issue-111123.jjVc3M.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a bug where a :keyword:`global` declaration in an :keyword:`except` block -is rejected when the global is used in the :keyword:`else` block. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-23-15-44-47.gh-issue-67224.S4D6CR.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-23-15-44-47.gh-issue-67224.S4D6CR.rst deleted file mode 100644 index b0474f38cff6f6..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-23-15-44-47.gh-issue-67224.S4D6CR.rst +++ /dev/null @@ -1,2 +0,0 @@ -Show source lines in tracebacks when using the ``-c`` option when running -Python. Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-23-22-11-09.gh-issue-94438.y2pITu.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-23-22-11-09.gh-issue-94438.y2pITu.rst deleted file mode 100644 index b6e147a48a8cd8..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-23-22-11-09.gh-issue-94438.y2pITu.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a regression that prevented jumping across ``is None`` and ``is not None`` when debugging. Patch by Savannah Ostrowski. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-26-18-45-20.gh-issue-111354.GrT-Wf.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-26-18-45-20.gh-issue-111354.GrT-Wf.rst deleted file mode 100644 index 96798701066df9..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-26-18-45-20.gh-issue-111354.GrT-Wf.rst +++ /dev/null @@ -1,3 +0,0 @@ -Remove ``oparg`` from :opcode:`YIELD_VALUE`. Change ``oparg`` of -:opcode:`RESUME` to include information about the except-depth. -These changes make it possible to simplify the code in generator close. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-27-11-22-09.gh-issue-111374.e9lrPZ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-27-11-22-09.gh-issue-111374.e9lrPZ.rst deleted file mode 100644 index 6f18339112466d..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-27-11-22-09.gh-issue-111374.e9lrPZ.rst +++ /dev/null @@ -1,3 +0,0 @@ -Added a new environment variable :envvar:`PYTHON_FROZEN_MODULES`. It -determines whether or not frozen modules are ignored by the import machinery, -equivalent of the :option:`-X frozen_modules <-X>` command-line option. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-27-11-51-40.gh-issue-111380.vgSbir.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-27-11-51-40.gh-issue-111380.vgSbir.rst deleted file mode 100644 index 4ce6398dbfe3b1..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-27-11-51-40.gh-issue-111380.vgSbir.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a bug that was causing :exc:`SyntaxWarning` to appear twice when parsing -if invalid syntax is encountered later. Patch by Pablo galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-27-12-17-49.gh-issue-111366._TSknV.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-27-12-17-49.gh-issue-111366._TSknV.rst deleted file mode 100644 index 7e76ce916ea714..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-27-12-17-49.gh-issue-111366._TSknV.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix an issue in the :mod:`codeop` that was causing :exc:`SyntaxError` -exceptions raised in the presence of invalid syntax to not contain precise -error messages. Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-27-19-38-33.gh-issue-102388.vd5YUZ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-27-19-38-33.gh-issue-102388.vd5YUZ.rst deleted file mode 100644 index 268a3d310f2b49..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-27-19-38-33.gh-issue-102388.vd5YUZ.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a bug where ``iso2022_jp_3`` and ``iso2022_jp_2004`` codecs read out of bounds diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-29-11-35-21.gh-issue-111435.ageUWQ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-29-11-35-21.gh-issue-111435.ageUWQ.rst deleted file mode 100644 index 034615581b6789..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-29-11-35-21.gh-issue-111435.ageUWQ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add support for sharing of True and False between interpreters using the cross-interpreter -API. Patch by Anthony Shaw. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-29-12-33-33.gh-issue-111438.bHTLLl.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-29-12-33-33.gh-issue-111438.bHTLLl.rst deleted file mode 100644 index 009ba11ae16683..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-29-12-33-33.gh-issue-111438.bHTLLl.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add support for sharing floats between interpreters using the cross-interpreter -API. Patch by Anthony Shaw. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-29-20-11-21.gh-issue-111420.IUT-GK.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-29-20-11-21.gh-issue-111420.IUT-GK.rst deleted file mode 100644 index 6646ecf920eeb5..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-29-20-11-21.gh-issue-111420.IUT-GK.rst +++ /dev/null @@ -1 +0,0 @@ -Allow type comments in parenthesized ``with`` statements diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-31-14-25-21.gh-issue-109181.11h6Mc.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-31-14-25-21.gh-issue-109181.11h6Mc.rst deleted file mode 100644 index 61a15b471cfb27..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-31-14-25-21.gh-issue-109181.11h6Mc.rst +++ /dev/null @@ -1,2 +0,0 @@ -Speed up :obj:`Traceback` object creation by lazily compute the line number. -Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst deleted file mode 100644 index 67ca9c6671f99b..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst +++ /dev/null @@ -1,9 +0,0 @@ -Merge the Tier 1 (bytecode) and Tier 2 (micro-ops) interpreters together, -moving the Tier 2 interpreter loop and switch into -``_PyEval_EvalFrameDefault()`` in ``Python/ceval.c``. -The ``Python/executor.c`` file is gone. -Also the ``TIER_ONE`` and ``TIER_TWO`` macros are now handled -by the code generator. - -**Beware!** This changes the environment variables to enable micro-ops and -their debugging to ``PYTHON_UOPS`` and ``PYTHON_LLTRACE``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-02-14-49-19.gh-issue-111354.gIS3f-.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-02-14-49-19.gh-issue-111354.gIS3f-.rst deleted file mode 100644 index 21d45340489caa..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-02-14-49-19.gh-issue-111354.gIS3f-.rst +++ /dev/null @@ -1,5 +0,0 @@ -The oparg of :opcode:`YIELD_VALUE` is now ``1`` if the instruction is part -of a yield-from or await, and ``0`` otherwise. - -The SUSPENDED frame state is now split into ``SUSPENDED`` and -``SUSPENDED_YIELD_FROM``. This simplifies the code in ``_PyGen_yf``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-02-15-00-57.gh-issue-111623.BZxYc8.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-02-15-00-57.gh-issue-111623.BZxYc8.rst deleted file mode 100644 index 3a75d5e36841b4..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-02-15-00-57.gh-issue-111623.BZxYc8.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add support for sharing tuples between interpreters using the cross-interpreter -API. Patch by Anthony Shaw. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-03-01-04-55.gh-issue-111654.scUhDO.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-03-01-04-55.gh-issue-111654.scUhDO.rst deleted file mode 100644 index e9a896e660916f..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-03-01-04-55.gh-issue-111654.scUhDO.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix runtime crash when some error happens in opcode -``LOAD_FROM_DICT_OR_DEREF``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-03-01-23-48.gh-issue-111666.l8Q8G5.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-03-01-23-48.gh-issue-111666.l8Q8G5.rst deleted file mode 100644 index 1d742a7c918599..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-03-01-23-48.gh-issue-111666.l8Q8G5.rst +++ /dev/null @@ -1,3 +0,0 @@ -Speed up :meth:`BaseExceptionGroup.derive`, -:meth:`BaseExceptionGroup.subgroup`, and :meth:`BaseExceptionGroup.split` by -changing how they parse passed arguments. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-03-19-25-38.gh-issue-111772.aRQvOn.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-03-19-25-38.gh-issue-111772.aRQvOn.rst deleted file mode 100644 index 79ae5ab4c8ff3b..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-03-19-25-38.gh-issue-111772.aRQvOn.rst +++ /dev/null @@ -1 +0,0 @@ -Specialize slot loads and stores for _Py_T_OBJECT as well as Py_T_OBJECT_EX diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-03-22-48-29.gh-issue-109369.ELYaxJ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-03-22-48-29.gh-issue-109369.ELYaxJ.rst deleted file mode 100644 index 1a73afca756f9e..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-03-22-48-29.gh-issue-109369.ELYaxJ.rst +++ /dev/null @@ -1 +0,0 @@ -Make sure that tier 2 traces are de-optimized if the code is instrumented diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-04-13-36-51.gh-issue-110829.Pa0CJI.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-04-13-36-51.gh-issue-110829.Pa0CJI.rst deleted file mode 100644 index f4fa61db369ece..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-04-13-36-51.gh-issue-110829.Pa0CJI.rst +++ /dev/null @@ -1 +0,0 @@ -Joining a thread now ensures the underlying OS thread has exited. This is required for safer fork() in multi-threaded processes. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-05-06-40-35.gh-issue-111843.c045cB.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-05-06-40-35.gh-issue-111843.c045cB.rst deleted file mode 100644 index 280f8f9bf0b8c0..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-05-06-40-35.gh-issue-111843.c045cB.rst +++ /dev/null @@ -1,2 +0,0 @@ -Use exponential backoff to reduce the number of failed tier 2 optimization -attempts by over 99%. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-05-20-59-10.gh-issue-81925.wKHLSS.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-05-20-59-10.gh-issue-81925.wKHLSS.rst deleted file mode 100644 index 9caa5cf6abe1ff..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-05-20-59-10.gh-issue-81925.wKHLSS.rst +++ /dev/null @@ -1 +0,0 @@ -Implement native thread ids for GNU KFreeBSD. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-06-16-44-09.gh-issue-79932.2qv7uD.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-06-16-44-09.gh-issue-79932.2qv7uD.rst deleted file mode 100644 index 543dbe4413027a..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-06-16-44-09.gh-issue-79932.2qv7uD.rst +++ /dev/null @@ -1 +0,0 @@ -Raise exception if :meth:`frame.clear` is called on a suspended frame. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-07-12-59-02.gh-issue-81137.qFpJCY.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-07-12-59-02.gh-issue-81137.qFpJCY.rst deleted file mode 100644 index 5ca1dda63cb222..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-07-12-59-02.gh-issue-81137.qFpJCY.rst +++ /dev/null @@ -1,2 +0,0 @@ -Deprecate assignment to a function's ``__code__`` field when the new code -object is of a mismatched type (e.g., from a generator to a plain function). diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-14-22-12-11.gh-issue-111916.ZGCayL.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-14-22-12-11.gh-issue-111916.ZGCayL.rst deleted file mode 100644 index b3814828e476b9..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-14-22-12-11.gh-issue-111916.ZGCayL.rst +++ /dev/null @@ -1 +0,0 @@ -Make hashlib related modules thread-safe without the GIL diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-15-16-14-10.gh-issue-106529.Y48ax9.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-15-16-14-10.gh-issue-106529.Y48ax9.rst deleted file mode 100644 index b2a34ac735cdeb..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-15-16-14-10.gh-issue-106529.Y48ax9.rst +++ /dev/null @@ -1 +0,0 @@ -Enable translating unspecialized ``FOR_ITER`` to Tier 2. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-15-20-20-51.gh-issue-111798.cs-3t3.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-15-20-20-51.gh-issue-111798.cs-3t3.rst deleted file mode 100644 index 24bb4ec096c2d1..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-15-20-20-51.gh-issue-111798.cs-3t3.rst +++ /dev/null @@ -1,4 +0,0 @@ -When Python is built in debug mode, set the C recursion limit to 500 instead -of 1500. A debug build is likely built with low optimization level which -implies higher stack memory usage than a release build. Patch by Victor -Stinner. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-17-16-49-32.gh-issue-111807.QvjP9_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-17-16-49-32.gh-issue-111807.QvjP9_.rst deleted file mode 100644 index 6f075845e11b86..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-17-16-49-32.gh-issue-111807.QvjP9_.rst +++ /dev/null @@ -1 +0,0 @@ -Lower the max parser stack depth to 1000 under WASI debug builds. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-19-15-57-23.gh-issue-112266.BSJMbR.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-19-15-57-23.gh-issue-112266.BSJMbR.rst deleted file mode 100644 index 18433db9bb976e..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-19-15-57-23.gh-issue-112266.BSJMbR.rst +++ /dev/null @@ -1,2 +0,0 @@ -Change docstrings of :attr:`~object.__dict__` and -:attr:`~object.__weakref__`. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-20-10-40-40.gh-issue-112287.15gWAK.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-20-10-40-40.gh-issue-112287.15gWAK.rst deleted file mode 100644 index 3f31a0f55ca44e..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-20-10-40-40.gh-issue-112287.15gWAK.rst +++ /dev/null @@ -1,3 +0,0 @@ -Slightly optimize the Tier 2 (uop) interpreter by only loading ``oparg`` and -``operand`` when needed. Also double the trace size limit again, to 512 this -time. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-20-14-13-02.gh-issue-112243.FKdQnr.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-20-14-13-02.gh-issue-112243.FKdQnr.rst deleted file mode 100644 index d69f29f5c63490..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-20-14-13-02.gh-issue-112243.FKdQnr.rst +++ /dev/null @@ -1 +0,0 @@ -Don't include comments in f-string debug expressions. Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/IDLE/2019-01-07-06-18-25.bpo-35668.JimxP5.rst b/Misc/NEWS.d/next/IDLE/2019-01-07-06-18-25.bpo-35668.JimxP5.rst deleted file mode 100644 index 8bb5420517d55f..00000000000000 --- a/Misc/NEWS.d/next/IDLE/2019-01-07-06-18-25.bpo-35668.JimxP5.rst +++ /dev/null @@ -1,4 +0,0 @@ -Add docstrings to the IDLE debugger module. Fix two bugs: -initialize Idb.botframe (should be in Bdb); in Idb.in_rpc_code, -check whether prev_frame is None before trying to use it. -Greatly expand test_debugger. diff --git a/Misc/NEWS.d/next/Library/2022-05-06-15-49-57.gh-issue-86826.rf006W.rst b/Misc/NEWS.d/next/Library/2022-05-06-15-49-57.gh-issue-86826.rf006W.rst deleted file mode 100644 index 02cd75eec4be9e..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-05-06-15-49-57.gh-issue-86826.rf006W.rst +++ /dev/null @@ -1,4 +0,0 @@ -:mod:`zipinfo` now supports the full range of values in the TZ string -determined by RFC 8536 and detects all invalid formats. -Both Python and C implementations now raise exceptions of the same -type on invalid data. diff --git a/Misc/NEWS.d/next/Library/2022-05-28-20-55-07.gh-issue-73561.YRmAvy.rst b/Misc/NEWS.d/next/Library/2022-05-28-20-55-07.gh-issue-73561.YRmAvy.rst deleted file mode 100644 index 5e00b7d20b8ca8..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-05-28-20-55-07.gh-issue-73561.YRmAvy.rst +++ /dev/null @@ -1 +0,0 @@ -Omit the interface scope from an IPv6 address when used as Host header by :mod:`http.client`. diff --git a/Misc/NEWS.d/next/Library/2022-10-05-15-01-36.gh-issue-96954.ezwkrU.rst b/Misc/NEWS.d/next/Library/2022-10-05-15-01-36.gh-issue-96954.ezwkrU.rst deleted file mode 100644 index f4886bd3cc27f5..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-10-05-15-01-36.gh-issue-96954.ezwkrU.rst +++ /dev/null @@ -1,5 +0,0 @@ -Switch the storage of the unicode codepoint names to use a different -data-structure, a `directed acyclic word graph -`_. -This makes the unicodedata shared library about 440 KiB smaller. Contributed by -Carl Friedrich Bolz-Tereick using code from the PyPy project. diff --git a/Misc/NEWS.d/next/Library/2022-10-14-21-11-10.gh-issue-97928.Pdxh1G.rst b/Misc/NEWS.d/next/Library/2022-10-14-21-11-10.gh-issue-97928.Pdxh1G.rst deleted file mode 100644 index 4acf396f840d61..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-10-14-21-11-10.gh-issue-97928.Pdxh1G.rst +++ /dev/null @@ -1,6 +0,0 @@ -Change the behavior of :meth:`tkinter.Text.count`. It now always returns an -integer if one or less counting options are specified. Previously it could -return a single count as a 1-tuple, an integer (only if option ``"update"`` -was specified) or ``None`` if no items found. The result is now the same if -``wantobjects`` is set to ``0``. - diff --git a/Misc/NEWS.d/next/Library/2023-03-22-02-01-30.gh-issue-102895.HiEqaZ.rst b/Misc/NEWS.d/next/Library/2023-03-22-02-01-30.gh-issue-102895.HiEqaZ.rst deleted file mode 100644 index 20a1a5baccd24b..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-03-22-02-01-30.gh-issue-102895.HiEqaZ.rst +++ /dev/null @@ -1 +0,0 @@ -Added a parameter ``local_exit`` for :func:`code.interact` to prevent ``exit()`` and ``quit`` from closing ``sys.stdin`` and raise ``SystemExit``. diff --git a/Misc/NEWS.d/next/Library/2023-04-15-14-45-21.gh-issue-102956.Z6qeUy.rst b/Misc/NEWS.d/next/Library/2023-04-15-14-45-21.gh-issue-102956.Z6qeUy.rst deleted file mode 100644 index 1a4bb9bc0dc46a..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-04-15-14-45-21.gh-issue-102956.Z6qeUy.rst +++ /dev/null @@ -1 +0,0 @@ -Fix returning of empty byte strings after seek in zipfile module diff --git a/Misc/NEWS.d/next/Library/2023-04-26-16-37-00.gh-issue-90890.fIag4w.rst b/Misc/NEWS.d/next/Library/2023-04-26-16-37-00.gh-issue-90890.fIag4w.rst deleted file mode 100644 index ee2e69eb27980f..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-04-26-16-37-00.gh-issue-90890.fIag4w.rst +++ /dev/null @@ -1,7 +0,0 @@ -New methods :meth:`mailbox.Maildir.get_info`, -:meth:`mailbox.Maildir.set_info`, :meth:`mailbox.Maildir.get_flags`, -:meth:`mailbox.Maildir.set_flags`, :meth:`mailbox.Maildir.add_flag`, -:meth:`mailbox.Maildir.remove_flag`. These methods speed up accessing a -message's info and/or flags and are useful when it is not necessary to -access the message's contents, as when iterating over a Maildir to find -messages with specific flags. diff --git a/Misc/NEWS.d/next/Library/2023-05-30-02-01-14.gh-issue-90026.FyCXw8.rst b/Misc/NEWS.d/next/Library/2023-05-30-02-01-14.gh-issue-90026.FyCXw8.rst deleted file mode 100644 index 3e32c9836f0cff..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-05-30-02-01-14.gh-issue-90026.FyCXw8.rst +++ /dev/null @@ -1 +0,0 @@ -Define ``USE_XATTRS`` on Cygwin so that XATTR-related functions in the :mod:`os` module become available. diff --git a/Misc/NEWS.d/next/Library/2023-07-13-00-24-52.gh-issue-72904.Yn5-j0.rst b/Misc/NEWS.d/next/Library/2023-07-13-00-24-52.gh-issue-72904.Yn5-j0.rst deleted file mode 100644 index edc8ab07bb06b3..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-13-00-24-52.gh-issue-72904.Yn5-j0.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add :func:`glob.translate`. This function converts a pathname with shell-style -wildcards to a regular expression. diff --git a/Misc/NEWS.d/next/Library/2023-07-29-19-00-39.gh-issue-107431.1GzJ2p.rst b/Misc/NEWS.d/next/Library/2023-07-29-19-00-39.gh-issue-107431.1GzJ2p.rst deleted file mode 100644 index fb5bd8cb7cad47..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-29-19-00-39.gh-issue-107431.1GzJ2p.rst +++ /dev/null @@ -1,2 +0,0 @@ -Make the ``DictProxy`` and ``ListProxy`` types in :mod:`multiprocessing.managers` -:ref:`Generic Alias Types` for ``[]`` use in typing contexts. diff --git a/Misc/NEWS.d/next/Library/2023-08-30-19-10-35.gh-issue-105931.Lpwve8.rst b/Misc/NEWS.d/next/Library/2023-08-30-19-10-35.gh-issue-105931.Lpwve8.rst deleted file mode 100644 index 4e769ec4c47561..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-08-30-19-10-35.gh-issue-105931.Lpwve8.rst +++ /dev/null @@ -1,8 +0,0 @@ -Change :mod:`compileall` to only strip the stripdir prefix from the full path -recorded in the compiled ``.pyc`` file, when the prefix matches the start of -the full path in its entirety. When the prefix does not match, no stripping is -performed and a warning to this effect is displayed. - -Previously all path components of the stripdir prefix that matched the full -path were removed, while those that did not match were left alone (including -ones interspersed between matching components). diff --git a/Misc/NEWS.d/next/Library/2023-09-02-16-07-23.gh-issue-108791.fBcAqh.rst b/Misc/NEWS.d/next/Library/2023-09-02-16-07-23.gh-issue-108791.fBcAqh.rst deleted file mode 100644 index 84a2cd589e10d5..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-02-16-07-23.gh-issue-108791.fBcAqh.rst +++ /dev/null @@ -1 +0,0 @@ -Improved error handling in :mod:`pdb` command line interface, making it produce more concise error messages. diff --git a/Misc/NEWS.d/next/Library/2023-09-08-12-10-10.gh-issue-85098.DfQbeJ.rst b/Misc/NEWS.d/next/Library/2023-09-08-12-10-10.gh-issue-85098.DfQbeJ.rst deleted file mode 100644 index cf0e782237b2c0..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-08-12-10-10.gh-issue-85098.DfQbeJ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Implement the CLI of the :mod:`symtable` module and improve the repr of -:class:`~symtable.Symbol`. diff --git a/Misc/NEWS.d/next/Library/2023-09-15-12-30-21.gh-issue-109466.6ah-aw.rst b/Misc/NEWS.d/next/Library/2023-09-15-12-30-21.gh-issue-109466.6ah-aw.rst deleted file mode 100644 index e8e5be320ea805..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-15-12-30-21.gh-issue-109466.6ah-aw.rst +++ /dev/null @@ -1 +0,0 @@ -Add the :attr:`ipaddress.IPv4Address.ipv6_mapped` property, which retuns the IPv4-mapped IPv6 address. diff --git a/Misc/NEWS.d/next/Library/2023-09-25-20-05-41.gh-issue-109747._cRJH8.rst b/Misc/NEWS.d/next/Library/2023-09-25-20-05-41.gh-issue-109747._cRJH8.rst deleted file mode 100644 index b64ba627897a1a..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-25-20-05-41.gh-issue-109747._cRJH8.rst +++ /dev/null @@ -1,3 +0,0 @@ -Improve errors for unsupported look-behind patterns. Now re.error is raised -instead of OverflowError or RuntimeError for too large width of look-behind -pattern. diff --git a/Misc/NEWS.d/next/Library/2023-10-02-05-23-27.gh-issue-110196.djwt0z.rst b/Misc/NEWS.d/next/Library/2023-10-02-05-23-27.gh-issue-110196.djwt0z.rst deleted file mode 100644 index 341f3380fffd60..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-02-05-23-27.gh-issue-110196.djwt0z.rst +++ /dev/null @@ -1 +0,0 @@ -Add ``__reduce__`` method to :class:`IPv6Address` in order to keep ``scope_id`` diff --git a/Misc/NEWS.d/next/Library/2023-10-08-14-17-06.gh-issue-110395._tdCsV.rst b/Misc/NEWS.d/next/Library/2023-10-08-14-17-06.gh-issue-110395._tdCsV.rst deleted file mode 100644 index eb9bcf1f337fb3..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-08-14-17-06.gh-issue-110395._tdCsV.rst +++ /dev/null @@ -1,2 +0,0 @@ -Ensure that :func:`select.kqueue` objects correctly appear as closed in -forked children, to prevent operations on an invalid file descriptor. diff --git a/Misc/NEWS.d/next/Library/2023-10-08-18-38-09.gh-issue-88434.2Q_IkG.rst b/Misc/NEWS.d/next/Library/2023-10-08-18-38-09.gh-issue-88434.2Q_IkG.rst deleted file mode 100644 index 9722be251bfcad..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-08-18-38-09.gh-issue-88434.2Q_IkG.rst +++ /dev/null @@ -1,3 +0,0 @@ -Emit deprecation warning for non-integer numbers in :mod:`gettext` functions -and methods that consider plural forms even if the translation was not -found. diff --git a/Misc/NEWS.d/next/Library/2023-10-09-23-59-04.gh-issue-59013.qPbS-G.rst b/Misc/NEWS.d/next/Library/2023-10-09-23-59-04.gh-issue-59013.qPbS-G.rst deleted file mode 100644 index 57915f5fb4368d..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-09-23-59-04.gh-issue-59013.qPbS-G.rst +++ /dev/null @@ -1 +0,0 @@ -Make line number of function breakpoint more precise in :mod:`pdb` diff --git a/Misc/NEWS.d/next/Library/2023-10-10-17-56-41.gh-issue-110392.6g6CnP.rst b/Misc/NEWS.d/next/Library/2023-10-10-17-56-41.gh-issue-110392.6g6CnP.rst deleted file mode 100644 index 47e4e8ee1f058d..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-10-17-56-41.gh-issue-110392.6g6CnP.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix :func:`tty.setraw` and :func:`tty.setcbreak`: previously they returned -partially modified list of the original tty attributes. -:func:`tty.cfmakeraw` and :func:`tty.cfmakecbreak` now make a copy of the -list of special characters before modifying it. diff --git a/Misc/NEWS.d/next/Library/2023-10-12-15-16-44.gh-issue-110774.AdCb5A.rst b/Misc/NEWS.d/next/Library/2023-10-12-15-16-44.gh-issue-110774.AdCb5A.rst deleted file mode 100644 index a5a9fed11864d6..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-12-15-16-44.gh-issue-110774.AdCb5A.rst +++ /dev/null @@ -1 +0,0 @@ -Support setting the :class:`asyncio.Runner` loop_factory kwarg in :class:`unittest.IsolatedAsyncioTestCase` diff --git a/Misc/NEWS.d/next/Library/2023-10-13-06-47-20.gh-issue-110771.opwdlc.rst b/Misc/NEWS.d/next/Library/2023-10-13-06-47-20.gh-issue-110771.opwdlc.rst deleted file mode 100644 index a22f8a0d5e56c3..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-13-06-47-20.gh-issue-110771.opwdlc.rst +++ /dev/null @@ -1 +0,0 @@ -Expose the setup and cleanup portions of ``asyncio.run_forever()`` as the standalone methods ``asyncio.run_forever_setup()`` and ``asyncio.run_forever_cleanup()``. This allows for tighter integration with GUI event loops. diff --git a/Misc/NEWS.d/next/Library/2023-10-14-20-15-53.gh-issue-80675._M-cQC.rst b/Misc/NEWS.d/next/Library/2023-10-14-20-15-53.gh-issue-80675._M-cQC.rst deleted file mode 100644 index a4bc679be905d6..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-14-20-15-53.gh-issue-80675._M-cQC.rst +++ /dev/null @@ -1 +0,0 @@ -Set ``f_trace_lines = True`` on all frames upon :func:`pdb.set_trace()` diff --git a/Misc/NEWS.d/next/Library/2023-10-14-21-33-57.gh-issue-84583.-Cmn4_.rst b/Misc/NEWS.d/next/Library/2023-10-14-21-33-57.gh-issue-84583.-Cmn4_.rst deleted file mode 100644 index aa86da461a0b6d..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-14-21-33-57.gh-issue-84583.-Cmn4_.rst +++ /dev/null @@ -1 +0,0 @@ -Make :mod:`pdb` enter post-mortem mode even for :exc:`SyntaxError` diff --git a/Misc/NEWS.d/next/Library/2023-10-15-08-08-26.gh-issue-110745.mxEkh0.rst b/Misc/NEWS.d/next/Library/2023-10-15-08-08-26.gh-issue-110745.mxEkh0.rst deleted file mode 100644 index b99f968dc20ae9..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-15-08-08-26.gh-issue-110745.mxEkh0.rst +++ /dev/null @@ -1,2 +0,0 @@ -Added *newline* parameter to :meth:`pathlib.Path.read_text`. -Patch by Junya Okabe. diff --git a/Misc/NEWS.d/next/Library/2023-10-16-18-41-51.gh-issue-110944.CmUKXo.rst b/Misc/NEWS.d/next/Library/2023-10-16-18-41-51.gh-issue-110944.CmUKXo.rst deleted file mode 100644 index ec9ca5a11f1ac8..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-16-18-41-51.gh-issue-110944.CmUKXo.rst +++ /dev/null @@ -1 +0,0 @@ -Support alias and convenience vars for :mod:`pdb` completion diff --git a/Misc/NEWS.d/next/Library/2023-10-19-22-46-34.gh-issue-111092.hgut12.rst b/Misc/NEWS.d/next/Library/2023-10-19-22-46-34.gh-issue-111092.hgut12.rst deleted file mode 100644 index 487bd177d27e31..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-19-22-46-34.gh-issue-111092.hgut12.rst +++ /dev/null @@ -1 +0,0 @@ -Make turtledemo run without default root enabled. diff --git a/Misc/NEWS.d/next/Library/2023-10-20-15-29-10.gh-issue-110910.u2oPwX.rst b/Misc/NEWS.d/next/Library/2023-10-20-15-29-10.gh-issue-110910.u2oPwX.rst deleted file mode 100644 index c750447e9fe4a5..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-20-15-29-10.gh-issue-110910.u2oPwX.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix invalid state handling in :class:`asyncio.TaskGroup` and -:class:`asyncio.Timeout`. They now raise proper RuntimeError if they are -improperly used and are left in consistent state after this. diff --git a/Misc/NEWS.d/next/Library/2023-10-21-13-57-06.gh-issue-111159.GoHp7s.rst b/Misc/NEWS.d/next/Library/2023-10-21-13-57-06.gh-issue-111159.GoHp7s.rst deleted file mode 100644 index bdec4f4443d80b..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-21-13-57-06.gh-issue-111159.GoHp7s.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :mod:`doctest` output comparison for exceptions with notes. diff --git a/Misc/NEWS.d/next/Library/2023-10-22-21-28-05.gh-issue-111187._W11Ab.rst b/Misc/NEWS.d/next/Library/2023-10-22-21-28-05.gh-issue-111187._W11Ab.rst deleted file mode 100644 index dc2424370bb96c..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-22-21-28-05.gh-issue-111187._W11Ab.rst +++ /dev/null @@ -1 +0,0 @@ -Postpone removal version for locale.getdefaultlocale() to Python 3.15. diff --git a/Misc/NEWS.d/next/Library/2023-10-23-13-53-58.gh-issue-111174.Oohmzd.rst b/Misc/NEWS.d/next/Library/2023-10-23-13-53-58.gh-issue-111174.Oohmzd.rst deleted file mode 100644 index 95c315404d0ee6..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-23-13-53-58.gh-issue-111174.Oohmzd.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix crash in :meth:`io.BytesIO.getbuffer` called repeatedly for empty -BytesIO. diff --git a/Misc/NEWS.d/next/Library/2023-10-23-22-40-47.gh-issue-111230.k3Jm84.rst b/Misc/NEWS.d/next/Library/2023-10-23-22-40-47.gh-issue-111230.k3Jm84.rst deleted file mode 100644 index 61d035853db070..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-23-22-40-47.gh-issue-111230.k3Jm84.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :mod:`ssl` not checking for errors when initializing. diff --git a/Misc/NEWS.d/next/Library/2023-10-23-23-14-54.gh-issue-111233.sCdCC0.rst b/Misc/NEWS.d/next/Library/2023-10-23-23-14-54.gh-issue-111233.sCdCC0.rst deleted file mode 100644 index 86d622a74095ab..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-23-23-14-54.gh-issue-111233.sCdCC0.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :mod:`select` not checking for errors when initializing. diff --git a/Misc/NEWS.d/next/Library/2023-10-24-12-09-46.gh-issue-111251.urFYtn.rst b/Misc/NEWS.d/next/Library/2023-10-24-12-09-46.gh-issue-111251.urFYtn.rst deleted file mode 100644 index 3a87cb25da5cb4..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-24-12-09-46.gh-issue-111251.urFYtn.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :mod:`_blake2` not checking for errors when initializing. diff --git a/Misc/NEWS.d/next/Library/2023-10-24-12-20-46.gh-issue-111253.HFywSK.rst b/Misc/NEWS.d/next/Library/2023-10-24-12-20-46.gh-issue-111253.HFywSK.rst deleted file mode 100644 index e21a42605aeaf6..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-24-12-20-46.gh-issue-111253.HFywSK.rst +++ /dev/null @@ -1 +0,0 @@ -Add error checking during :mod:`!_socket` module init. diff --git a/Misc/NEWS.d/next/Library/2023-10-25-08-42-05.gh-issue-111295.H2K4lf.rst b/Misc/NEWS.d/next/Library/2023-10-25-08-42-05.gh-issue-111295.H2K4lf.rst deleted file mode 100644 index 28b85ec3eadab7..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-25-08-42-05.gh-issue-111295.H2K4lf.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :mod:`time` not checking for errors when initializing. diff --git a/Misc/NEWS.d/next/Library/2023-10-25-11-13-35.gh-issue-111259.z7ndeA.rst b/Misc/NEWS.d/next/Library/2023-10-25-11-13-35.gh-issue-111259.z7ndeA.rst deleted file mode 100644 index 4b597f51a955a7..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-25-11-13-35.gh-issue-111259.z7ndeA.rst +++ /dev/null @@ -1 +0,0 @@ -Optimize recursive wildcards in :mod:`pathlib`. diff --git a/Misc/NEWS.d/next/Library/2023-10-25-11-54-00.gh-issue-79033.5ePgFl.rst b/Misc/NEWS.d/next/Library/2023-10-25-11-54-00.gh-issue-79033.5ePgFl.rst deleted file mode 100644 index f131bf590870ad..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-25-11-54-00.gh-issue-79033.5ePgFl.rst +++ /dev/null @@ -1,6 +0,0 @@ -Another attempt at fixing :func:`asyncio.Server.wait_closed()`. It now -blocks until both conditions are true: the server is closed, *and* there -are no more active connections. (This means that in some cases where in -3.12.0 this function would *incorrectly* have returned immediately, -it will now block; in particular, when there are no active connections -but the server hasn't been closed yet.) diff --git a/Misc/NEWS.d/next/Library/2023-10-27-09-56-20.gh-issue-111388.SlmDbC.rst b/Misc/NEWS.d/next/Library/2023-10-27-09-56-20.gh-issue-111388.SlmDbC.rst deleted file mode 100644 index 353196439a9cff..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-27-09-56-20.gh-issue-111388.SlmDbC.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add ``show_group`` parameter to :func:`traceback.format_exception_only`, -which allows to format :exc:`ExceptionGroup` instances. diff --git a/Misc/NEWS.d/next/Library/2023-10-27-12-46-56.gh-issue-68166.0EbWW4.rst b/Misc/NEWS.d/next/Library/2023-10-27-12-46-56.gh-issue-68166.0EbWW4.rst deleted file mode 100644 index 757a7004cc1dc0..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-27-12-46-56.gh-issue-68166.0EbWW4.rst +++ /dev/null @@ -1,4 +0,0 @@ -Remove mention of not supported "vsapi" element type in -:meth:`tkinter.ttk.Style.element_create`. Add tests for ``element_create()`` -and other ``ttk.Style`` methods. Add examples for ``element_create()`` in -the documentation. diff --git a/Misc/NEWS.d/next/Library/2023-10-28-04-21-17.gh-issue-111342.m8Ln1k.rst b/Misc/NEWS.d/next/Library/2023-10-28-04-21-17.gh-issue-111342.m8Ln1k.rst deleted file mode 100644 index 57707fd4acf1b2..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-28-04-21-17.gh-issue-111342.m8Ln1k.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed typo in :func:`math.sumprod`. diff --git a/Misc/NEWS.d/next/Library/2023-10-28-22-11-11.gh-issue-111429.mJGxuQ.rst b/Misc/NEWS.d/next/Library/2023-10-28-22-11-11.gh-issue-111429.mJGxuQ.rst deleted file mode 100644 index c8bc4c5295a106..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-28-22-11-11.gh-issue-111429.mJGxuQ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Speed up :meth:`pathlib.PurePath.relative_to` and -:meth:`~pathlib.PurePath.is_relative_to`. diff --git a/Misc/NEWS.d/next/Library/2023-10-29-03-46-27.gh-issue-66425.FWTdDo.rst b/Misc/NEWS.d/next/Library/2023-10-29-03-46-27.gh-issue-66425.FWTdDo.rst deleted file mode 100644 index e7ede35891fb33..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-29-03-46-27.gh-issue-66425.FWTdDo.rst +++ /dev/null @@ -1,3 +0,0 @@ -Remove the code to set the REMOTE_HOST header from wsgiref module, -as it is unreachable. This header is used for performance reasons, -which is not necessary in the wsgiref module. diff --git a/Misc/NEWS.d/next/Library/2023-10-30-08-50-46.gh-issue-111356.Bc8LvA.rst b/Misc/NEWS.d/next/Library/2023-10-30-08-50-46.gh-issue-111356.Bc8LvA.rst deleted file mode 100644 index a821b52b982175..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-30-08-50-46.gh-issue-111356.Bc8LvA.rst +++ /dev/null @@ -1 +0,0 @@ -Added :func:`io.text_encoding()`, :data:`io.DEFAULT_BUFFER_SIZE`, and :class:`io.IncrementalNewlineDecoder` to ``io.__all__``. diff --git a/Misc/NEWS.d/next/Library/2023-10-30-14-47-23.gh-issue-111246.QJ_ehs.rst b/Misc/NEWS.d/next/Library/2023-10-30-14-47-23.gh-issue-111246.QJ_ehs.rst deleted file mode 100644 index a9630de5cc6947..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-30-14-47-23.gh-issue-111246.QJ_ehs.rst +++ /dev/null @@ -1,2 +0,0 @@ -:meth:`asyncio.loop.create_unix_server` will now automatically remove the -Unix socket when the server is closed. diff --git a/Misc/NEWS.d/next/Library/2023-10-31-07-46-56.gh-issue-111531.6zUV_G.rst b/Misc/NEWS.d/next/Library/2023-10-31-07-46-56.gh-issue-111531.6zUV_G.rst deleted file mode 100644 index b722f0414184b1..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-31-07-46-56.gh-issue-111531.6zUV_G.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix reference leaks in ``bind_class()`` and ``bind_all()`` methods of -:mod:`tkinter` widgets. diff --git a/Misc/NEWS.d/next/Library/2023-11-01-14-03-24.gh-issue-110894.7-wZxC.rst b/Misc/NEWS.d/next/Library/2023-11-01-14-03-24.gh-issue-110894.7-wZxC.rst deleted file mode 100644 index c59fe6b9119eca..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-01-14-03-24.gh-issue-110894.7-wZxC.rst +++ /dev/null @@ -1 +0,0 @@ -Call loop exception handler for exceptions in ``client_connected_cb`` of :func:`asyncio.start_server` so that applications can handle it. Patch by Kumar Aditya. diff --git a/Misc/NEWS.d/next/Library/2023-11-02-12-15-46.gh-issue-111482.FWqZIU.rst b/Misc/NEWS.d/next/Library/2023-11-02-12-15-46.gh-issue-111482.FWqZIU.rst deleted file mode 100644 index d73e45ccf09b48..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-02-12-15-46.gh-issue-111482.FWqZIU.rst +++ /dev/null @@ -1,3 +0,0 @@ -:mod:`time`: Make :func:`time.clock_gettime()` and -:func:`time.clock_gettime_ns()` functions up to 2x faster by faster calling -convention. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2023-11-04-01-20-23.gh-issue-111719.fUiKBD.rst b/Misc/NEWS.d/next/Library/2023-11-04-01-20-23.gh-issue-111719.fUiKBD.rst deleted file mode 100644 index 1d3b948644d232..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-04-01-20-23.gh-issue-111719.fUiKBD.rst +++ /dev/null @@ -1 +0,0 @@ -Add extra argument validation for ``alias`` command in :mod:`pdb` diff --git a/Misc/NEWS.d/next/Library/2023-11-04-10-24-25.gh-issue-111541.x0RBI1.rst b/Misc/NEWS.d/next/Library/2023-11-04-10-24-25.gh-issue-111541.x0RBI1.rst deleted file mode 100644 index 719b63dad36fb7..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-04-10-24-25.gh-issue-111541.x0RBI1.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :mod:`doctest` for :exc:`SyntaxError` not-builtin subclasses. diff --git a/Misc/NEWS.d/next/Library/2023-11-04-21-12-27.gh-issue-80731.Wq51xg.rst b/Misc/NEWS.d/next/Library/2023-11-04-21-12-27.gh-issue-80731.Wq51xg.rst deleted file mode 100644 index 5f957a39b2e34b..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-04-21-12-27.gh-issue-80731.Wq51xg.rst +++ /dev/null @@ -1 +0,0 @@ -Avoid executing the default function in :class:`cmd.Cmd` in an except block diff --git a/Misc/NEWS.d/next/Library/2023-11-08-07-42-53.gh-issue-111768.g-WpnV.rst b/Misc/NEWS.d/next/Library/2023-11-08-07-42-53.gh-issue-111768.g-WpnV.rst deleted file mode 100644 index 501cfa3920a0bf..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-08-07-42-53.gh-issue-111768.g-WpnV.rst +++ /dev/null @@ -1 +0,0 @@ -:func:`wsgiref.util.is_hop_by_hop` is now exposed correctly in ``__all__``. diff --git a/Misc/NEWS.d/next/Library/2023-11-08-11-50-49.gh-issue-111841.iSqdQf.rst b/Misc/NEWS.d/next/Library/2023-11-08-11-50-49.gh-issue-111841.iSqdQf.rst deleted file mode 100644 index cd1780988aeac7..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-08-11-50-49.gh-issue-111841.iSqdQf.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix truncating arguments on an embedded null character in :meth:`os.putenv` -and :meth:`os.unsetenv` on Windows. diff --git a/Misc/NEWS.d/next/Library/2023-11-08-15-58-57.gh-issue-111804.uAXTOL.rst b/Misc/NEWS.d/next/Library/2023-11-08-15-58-57.gh-issue-111804.uAXTOL.rst deleted file mode 100644 index 2696f2f492a8b0..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-08-15-58-57.gh-issue-111804.uAXTOL.rst +++ /dev/null @@ -1,2 +0,0 @@ -Remove posix.fallocate() under WASI as the underlying posix_fallocate() is -not available in WASI preview2. diff --git a/Misc/NEWS.d/next/Library/2023-11-08-23-32-03.gh-issue-111835.ufFiuW.rst b/Misc/NEWS.d/next/Library/2023-11-08-23-32-03.gh-issue-111835.ufFiuW.rst deleted file mode 100644 index 5d06c22f477bab..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-08-23-32-03.gh-issue-111835.ufFiuW.rst +++ /dev/null @@ -1,4 +0,0 @@ -The :class:`mmap.mmap` class now has an :meth:`~mmap.mmap.seekable` method -that can be used where it requires a file-like object with seekable and -the :meth:`~mmap.mmap.seek` method return the new absolute position. -Patch by Donghee Na. diff --git a/Misc/NEWS.d/next/Library/2023-11-09-10-45-56.gh-issue-103791.sdfkja.rst b/Misc/NEWS.d/next/Library/2023-11-09-10-45-56.gh-issue-103791.sdfkja.rst deleted file mode 100644 index 5bfdd75ccc93e8..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-09-10-45-56.gh-issue-103791.sdfkja.rst +++ /dev/null @@ -1,3 +0,0 @@ -:class:`contextlib.suppress` now supports suppressing exceptions raised as -part of a :exc:`BaseExceptionGroup`, in addition to the recent support for -:exc:`ExceptionGroup`. diff --git a/Misc/NEWS.d/next/Library/2023-11-09-12-57-43.gh-issue-111460.TQaz9I.rst b/Misc/NEWS.d/next/Library/2023-11-09-12-57-43.gh-issue-111460.TQaz9I.rst deleted file mode 100644 index 956c536a291a9c..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-09-12-57-43.gh-issue-111460.TQaz9I.rst +++ /dev/null @@ -1,3 +0,0 @@ -:mod:`curses`: restore wide character support (including -:func:`curses.unget_wch` and :meth:`~curses.window.get_wch`) on macOS, which -was unavailable due to a regression in Python 3.12. diff --git a/Misc/NEWS.d/next/Library/2023-11-10-22-08-28.gh-issue-111942.MDFm6v.rst b/Misc/NEWS.d/next/Library/2023-11-10-22-08-28.gh-issue-111942.MDFm6v.rst deleted file mode 100644 index 4fc505c8f257a6..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-10-22-08-28.gh-issue-111942.MDFm6v.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix crashes in :meth:`io.TextIOWrapper.reconfigure` when pass invalid -arguments, e.g. non-string encoding. diff --git a/Misc/NEWS.d/next/Library/2023-11-11-16-42-48.gh-issue-109538.cMG5ux.rst b/Misc/NEWS.d/next/Library/2023-11-11-16-42-48.gh-issue-109538.cMG5ux.rst deleted file mode 100644 index d1ee4c054a3f19..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-11-16-42-48.gh-issue-109538.cMG5ux.rst +++ /dev/null @@ -1 +0,0 @@ -Issue warning message instead of having :class:`RuntimeError` be displayed when event loop has already been closed at :meth:`StreamWriter.__del__`. diff --git a/Misc/NEWS.d/next/Library/2023-11-14-16-31-59.gh-issue-111995.OoX8JJ.rst b/Misc/NEWS.d/next/Library/2023-11-14-16-31-59.gh-issue-111995.OoX8JJ.rst deleted file mode 100644 index 773f620c97b608..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-14-16-31-59.gh-issue-111995.OoX8JJ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Added the ``NI_IDN`` constant to the :mod:`socket` module when present in C -at build time for use with :func:`socket.getnameinfo`. diff --git a/Misc/NEWS.d/next/Library/2023-11-14-18-43-55.gh-issue-111942.x1pnrj.rst b/Misc/NEWS.d/next/Library/2023-11-14-18-43-55.gh-issue-111942.x1pnrj.rst deleted file mode 100644 index ca58a6fa5d6ae1..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-14-18-43-55.gh-issue-111942.x1pnrj.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix SystemError in the TextIOWrapper constructor with non-encodable "errors" -argument in non-debug mode. diff --git a/Misc/NEWS.d/next/Tests/2023-09-15-15-00-14.gh-issue-108747.ql0owS.rst b/Misc/NEWS.d/next/Tests/2023-09-15-15-00-14.gh-issue-108747.ql0owS.rst deleted file mode 100644 index ba1fe97871929c..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-15-15-00-14.gh-issue-108747.ql0owS.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add unit test for ``usercustomize`` and ``sitecustomize`` hooks from -:class:`site`. diff --git a/Misc/NEWS.d/next/Tests/2023-10-16-13-47-24.gh-issue-110918.aFgZK3.rst b/Misc/NEWS.d/next/Tests/2023-10-16-13-47-24.gh-issue-110918.aFgZK3.rst deleted file mode 100644 index 7cb79c0cbf29f1..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-10-16-13-47-24.gh-issue-110918.aFgZK3.rst +++ /dev/null @@ -1,4 +0,0 @@ -Test case matching patterns specified by options ``--match``, ``--ignore``, -``--matchfile`` and ``--ignorefile`` are now tested in the order of -specification, and the last match determines whether the test case be run or -ignored. diff --git a/Misc/NEWS.d/next/Tests/2023-10-17-17-54-36.gh-issue-110995.Fx8KRD.rst b/Misc/NEWS.d/next/Tests/2023-10-17-17-54-36.gh-issue-110995.Fx8KRD.rst deleted file mode 100644 index db29eaf234b731..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-10-17-17-54-36.gh-issue-110995.Fx8KRD.rst +++ /dev/null @@ -1,2 +0,0 @@ -test_gdb: Fix detection of gdb built without Python scripting support. Patch -by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-10-21-00-10-36.gh-issue-110932.jktjJU.rst b/Misc/NEWS.d/next/Tests/2023-10-21-00-10-36.gh-issue-110932.jktjJU.rst deleted file mode 100644 index 45bb0774a9abe3..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-10-21-00-10-36.gh-issue-110932.jktjJU.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix regrtest if the ``SOURCE_DATE_EPOCH`` environment variable is defined: -use the variable value as the random seed. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-10-21-19-27-36.gh-issue-111165.FU6mUk.rst b/Misc/NEWS.d/next/Tests/2023-10-21-19-27-36.gh-issue-111165.FU6mUk.rst deleted file mode 100644 index 11f302d943c29a..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-10-21-19-27-36.gh-issue-111165.FU6mUk.rst +++ /dev/null @@ -1,2 +0,0 @@ -Remove no longer used functions ``run_unittest()`` and ``run_doctest()`` -from the :mod:`test.support` module. diff --git a/Misc/NEWS.d/next/Tests/2023-10-31-22-09-25.gh-issue-110367.UhQi44.rst b/Misc/NEWS.d/next/Tests/2023-10-31-22-09-25.gh-issue-110367.UhQi44.rst deleted file mode 100644 index 0254288d3626cc..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-10-31-22-09-25.gh-issue-110367.UhQi44.rst +++ /dev/null @@ -1,3 +0,0 @@ -Make regrtest ``--verbose3`` option compatible with ``--huntrleaks -jN`` -options. The ``./python -m test -j1 -R 3:3 --verbose3`` command now works as -expected. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-11-03-18-59-13.gh-issue-110722.jvT1pb.rst b/Misc/NEWS.d/next/Tests/2023-11-03-18-59-13.gh-issue-110722.jvT1pb.rst deleted file mode 100644 index ad1ac536a092ea..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-11-03-18-59-13.gh-issue-110722.jvT1pb.rst +++ /dev/null @@ -1,2 +0,0 @@ -Gathering line coverage of standard libraries within the regression test -suite is now precise, as well as much faster. Patch by Łukasz Langa. diff --git a/Misc/NEWS.d/next/Tests/2023-11-17-15-20-41.gh-issue-111808.jtIayt.rst b/Misc/NEWS.d/next/Tests/2023-11-17-15-20-41.gh-issue-111808.jtIayt.rst deleted file mode 100644 index 36151d4b0adf03..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-11-17-15-20-41.gh-issue-111808.jtIayt.rst +++ /dev/null @@ -1,4 +0,0 @@ -Make the default value of ``test.support.infinite_recursion()`` to be -conditional based on whether optimizations were used when compiling the -interpreter. This helps with platforms like WASI whose stack size is greatly -restricted in debug builds. diff --git a/Misc/NEWS.d/next/Tools-Demos/2023-11-09-13-04-29.gh-issue-111903.7Prryr.rst b/Misc/NEWS.d/next/Tools-Demos/2023-11-09-13-04-29.gh-issue-111903.7Prryr.rst deleted file mode 100644 index 41601f7511797a..00000000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2023-11-09-13-04-29.gh-issue-111903.7Prryr.rst +++ /dev/null @@ -1,4 +0,0 @@ -Argument Clinic now supports the ``@critical_section`` directive that -instructs Argument Clinic to generate a critical section around the function -call, which locks the ``self`` object in ``--disable-gil`` builds. Patch by -Sam Gross. diff --git a/Misc/NEWS.d/next/Windows/2023-10-19-21-46-18.gh-issue-110913.CWlPfg.rst b/Misc/NEWS.d/next/Windows/2023-10-19-21-46-18.gh-issue-110913.CWlPfg.rst deleted file mode 100644 index d4c1b56d98ef0e..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-10-19-21-46-18.gh-issue-110913.CWlPfg.rst +++ /dev/null @@ -1 +0,0 @@ -WindowsConsoleIO now correctly chunks large buffers without splitting up UTF-8 sequences. diff --git a/Misc/NEWS.d/next/Windows/2023-10-25-05-01-28.gh-issue-111293.FSsLT6.rst b/Misc/NEWS.d/next/Windows/2023-10-25-05-01-28.gh-issue-111293.FSsLT6.rst deleted file mode 100644 index 4c6b255bc44c7b..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-10-25-05-01-28.gh-issue-111293.FSsLT6.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :data:`os.DirEntry.inode` dropping higher 64 bits of a file id on some filesystems on Windows. diff --git a/Misc/NEWS.d/next/Windows/2023-11-13-22-35-27.gh-issue-111856.vEtA5z.rst b/Misc/NEWS.d/next/Windows/2023-11-13-22-35-27.gh-issue-111856.vEtA5z.rst deleted file mode 100644 index b1388df8e4c479..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-11-13-22-35-27.gh-issue-111856.vEtA5z.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixes :func:`~os.fstat` on file systems that do not support file ID -requests. This includes FAT32 and exFAT. diff --git a/Misc/NEWS.d/next/macOS/2023-08-30-16-33-57.gh-issue-92603.ATkKVO.rst b/Misc/NEWS.d/next/macOS/2023-08-30-16-33-57.gh-issue-92603.ATkKVO.rst deleted file mode 100644 index 477463c0c21264..00000000000000 --- a/Misc/NEWS.d/next/macOS/2023-08-30-16-33-57.gh-issue-92603.ATkKVO.rst +++ /dev/null @@ -1,3 +0,0 @@ -Update macOS installer to include a fix accepted by upstream Tcl/Tk -for a crash encountered after the first :meth:`tkinter.Tk` instance -is destroyed. diff --git a/Misc/NEWS.d/next/macOS/2023-09-02-08-49-57.gh-issue-71383.Ttkchg.rst b/Misc/NEWS.d/next/macOS/2023-09-02-08-49-57.gh-issue-71383.Ttkchg.rst deleted file mode 100644 index d8f3e429aab815..00000000000000 --- a/Misc/NEWS.d/next/macOS/2023-09-02-08-49-57.gh-issue-71383.Ttkchg.rst +++ /dev/null @@ -1,2 +0,0 @@ -Update macOS installer to include an upstream Tcl/Tk fix -for the ``ttk::ThemeChanged`` error encountered in Tkinter. diff --git a/Misc/NEWS.d/next/macOS/2023-10-18-01-40-36.gh-issue-111015.NaLI2L.rst b/Misc/NEWS.d/next/macOS/2023-10-18-01-40-36.gh-issue-111015.NaLI2L.rst deleted file mode 100644 index 4c6eea136554c8..00000000000000 --- a/Misc/NEWS.d/next/macOS/2023-10-18-01-40-36.gh-issue-111015.NaLI2L.rst +++ /dev/null @@ -1 +0,0 @@ -Ensure that IDLE.app and Python Launcher.app are installed with appropriate permissions on macOS builds. diff --git a/Misc/NEWS.d/next/macOS/2023-10-18-17-26-36.gh-issue-110950.sonoma.rst b/Misc/NEWS.d/next/macOS/2023-10-18-17-26-36.gh-issue-110950.sonoma.rst deleted file mode 100644 index c678c09f6aa55b..00000000000000 --- a/Misc/NEWS.d/next/macOS/2023-10-18-17-26-36.gh-issue-110950.sonoma.rst +++ /dev/null @@ -1,3 +0,0 @@ -Update macOS installer to include an upstream Tcl/Tk fix for the -``Secure coding is not enabled for restorable state!`` warning -encountered in Tkinter on macOS 14 Sonoma. diff --git a/Misc/NEWS.d/next/macOS/2023-10-31-22-13-05.gh-issue-59703.SML6Ag.rst b/Misc/NEWS.d/next/macOS/2023-10-31-22-13-05.gh-issue-59703.SML6Ag.rst deleted file mode 100644 index eeb014dd92ba36..00000000000000 --- a/Misc/NEWS.d/next/macOS/2023-10-31-22-13-05.gh-issue-59703.SML6Ag.rst +++ /dev/null @@ -1,4 +0,0 @@ -For macOS framework builds, in ``getpath.c`` use the system ``dladdr`` -function to find the path to the shared library rather than depending -on deprecated macOS APIs. - diff --git a/README.rst b/README.rst index 9ba06f76ba5da8..4c8602c97ac8ef 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -This is Python version 3.13.0 alpha 1 +This is Python version 3.13.0 alpha 2 ===================================== .. image:: https://github.com/python/cpython/workflows/Tests/badge.svg From fef6fb876267f28fbb2c5fcb17aebe1a52cc8e12 Mon Sep 17 00:00:00 2001 From: Mayuresh Kedari <43478802+Mayuresh16@users.noreply.github.com> Date: Wed, 22 Nov 2023 16:55:07 +0530 Subject: [PATCH 035/228] gh-111965: Use critical sections to make io.BufferedIOBase and its related classes thread safe (gh-112298) --- Modules/_io/bufferedio.c | 116 ++++++++++++++++++++++-------- Modules/_io/clinic/bufferedio.c.h | 104 ++++++++++++++++++++++++--- 2 files changed, 180 insertions(+), 40 deletions(-) diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index d7123ab005aa62..4f3786676d131f 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -8,11 +8,12 @@ */ #include "Python.h" -#include "pycore_bytesobject.h" // _PyBytes_Join() -#include "pycore_call.h" // _PyObject_CallNoArgs() -#include "pycore_object.h" // _PyObject_GC_UNTRACK() -#include "pycore_pyerrors.h" // _Py_FatalErrorFormat() -#include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing() +#include "pycore_bytesobject.h" // _PyBytes_Join() +#include "pycore_call.h" // _PyObject_CallNoArgs() +#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION() +#include "pycore_object.h" // _PyObject_GC_UNTRACK() +#include "pycore_pyerrors.h" // _Py_FatalErrorFormat() +#include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing() #include "_iomodule.h" @@ -82,6 +83,7 @@ _bufferediobase_readinto_generic(PyObject *self, Py_buffer *buffer, char readint } /*[clinic input] +@critical_section _io._BufferedIOBase.readinto buffer: Py_buffer(accept={rwbuffer}) / @@ -89,12 +91,13 @@ _io._BufferedIOBase.readinto static PyObject * _io__BufferedIOBase_readinto_impl(PyObject *self, Py_buffer *buffer) -/*[clinic end generated code: output=8c8cda6684af8038 input=00a6b9a38f29830a]*/ +/*[clinic end generated code: output=8c8cda6684af8038 input=5273d20db7f56e1a]*/ { return _bufferediobase_readinto_generic(self, buffer, 0); } /*[clinic input] +@critical_section _io._BufferedIOBase.readinto1 buffer: Py_buffer(accept={rwbuffer}) / @@ -102,7 +105,7 @@ _io._BufferedIOBase.readinto1 static PyObject * _io__BufferedIOBase_readinto1_impl(PyObject *self, Py_buffer *buffer) -/*[clinic end generated code: output=358623e4fd2b69d3 input=ebad75b4aadfb9be]*/ +/*[clinic end generated code: output=358623e4fd2b69d3 input=d6eb723dedcee654]*/ { return _bufferediobase_readinto_generic(self, buffer, 1); } @@ -431,12 +434,13 @@ buffered_dealloc(buffered *self) } /*[clinic input] +@critical_section _io._Buffered.__sizeof__ [clinic start generated code]*/ static PyObject * _io__Buffered___sizeof___impl(buffered *self) -/*[clinic end generated code: output=0231ef7f5053134e input=753c782d808d34df]*/ +/*[clinic end generated code: output=0231ef7f5053134e input=07a32d578073ea64]*/ { size_t res = _PyObject_SIZE(Py_TYPE(self)); if (self->buffer) { @@ -488,12 +492,13 @@ _io__Buffered__dealloc_warn(buffered *self, PyObject *source) /* Flush and close */ /*[clinic input] +@critical_section _io._Buffered.flush as _io__Buffered_simple_flush [clinic start generated code]*/ static PyObject * _io__Buffered_simple_flush_impl(buffered *self) -/*[clinic end generated code: output=29ebb3820db1bdfd input=f33ef045e7250767]*/ +/*[clinic end generated code: output=29ebb3820db1bdfd input=5248cb84a65f80bd]*/ { CHECK_INITIALIZED(self) return PyObject_CallMethodNoArgs(self->raw, &_Py_ID(flush)); @@ -514,12 +519,24 @@ buffered_closed(buffered *self) } static PyObject * -buffered_closed_get(buffered *self, void *context) +buffered_closed_get_impl(buffered *self, void *context) { CHECK_INITIALIZED(self) return PyObject_GetAttr(self->raw, &_Py_ID(closed)); } +static PyObject * +buffered_closed_get(buffered *self, void *context) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = buffered_closed_get_impl(self, context); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + /*[clinic input] @critical_section _io._Buffered.close @@ -584,12 +601,13 @@ _io__Buffered_close_impl(buffered *self) } /*[clinic input] +@critical_section _io._Buffered.detach [clinic start generated code]*/ static PyObject * _io__Buffered_detach_impl(buffered *self) -/*[clinic end generated code: output=dd0fc057b8b779f7 input=482762a345cc9f44]*/ +/*[clinic end generated code: output=dd0fc057b8b779f7 input=d4ef1828a678be37]*/ { PyObject *raw; CHECK_INITIALIZED(self) @@ -606,76 +624,105 @@ _io__Buffered_detach_impl(buffered *self) /* Inquiries */ /*[clinic input] +@critical_section _io._Buffered.seekable [clinic start generated code]*/ static PyObject * _io__Buffered_seekable_impl(buffered *self) -/*[clinic end generated code: output=90172abb5ceb6e8f input=7d35764f5fb5262b]*/ +/*[clinic end generated code: output=90172abb5ceb6e8f input=e3a4fc1d297b2fd3]*/ { CHECK_INITIALIZED(self) return PyObject_CallMethodNoArgs(self->raw, &_Py_ID(seekable)); } /*[clinic input] +@critical_section _io._Buffered.readable [clinic start generated code]*/ static PyObject * _io__Buffered_readable_impl(buffered *self) -/*[clinic end generated code: output=92afa07661ecb698 input=640619addb513b8b]*/ +/*[clinic end generated code: output=92afa07661ecb698 input=abe54107d59bca9a]*/ { CHECK_INITIALIZED(self) return PyObject_CallMethodNoArgs(self->raw, &_Py_ID(readable)); } /*[clinic input] +@critical_section _io._Buffered.writable [clinic start generated code]*/ static PyObject * _io__Buffered_writable_impl(buffered *self) -/*[clinic end generated code: output=4e3eee8d6f9d8552 input=b35ea396b2201554]*/ +/*[clinic end generated code: output=4e3eee8d6f9d8552 input=45eb76bf6a10e6f7]*/ { CHECK_INITIALIZED(self) return PyObject_CallMethodNoArgs(self->raw, &_Py_ID(writable)); } static PyObject * -buffered_name_get(buffered *self, void *context) +buffered_name_get_impl(buffered *self, void *context) { CHECK_INITIALIZED(self) return PyObject_GetAttr(self->raw, &_Py_ID(name)); } static PyObject * -buffered_mode_get(buffered *self, void *context) +buffered_name_get(buffered *self, void *context) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = buffered_name_get_impl(self, context); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +static PyObject * +buffered_mode_get_impl(buffered *self, void *context) { CHECK_INITIALIZED(self) return PyObject_GetAttr(self->raw, &_Py_ID(mode)); } +static PyObject * +buffered_mode_get(buffered *self, void *context) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = buffered_mode_get_impl(self, context); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + /* Lower-level APIs */ /*[clinic input] +@critical_section _io._Buffered.fileno [clinic start generated code]*/ static PyObject * _io__Buffered_fileno_impl(buffered *self) -/*[clinic end generated code: output=b717648d58a95ee3 input=768ea30b3f6314a7]*/ +/*[clinic end generated code: output=b717648d58a95ee3 input=1c4fead777bae20a]*/ { CHECK_INITIALIZED(self) return PyObject_CallMethodNoArgs(self->raw, &_Py_ID(fileno)); } /*[clinic input] +@critical_section _io._Buffered.isatty [clinic start generated code]*/ static PyObject * _io__Buffered_isatty_impl(buffered *self) -/*[clinic end generated code: output=c20e55caae67baea input=9ea007b11559bee4]*/ +/*[clinic end generated code: output=c20e55caae67baea input=e53d182d7e490e3a]*/ { CHECK_INITIALIZED(self) return PyObject_CallMethodNoArgs(self->raw, &_Py_ID(isatty)); @@ -881,12 +928,13 @@ buffered_flush_and_rewind_unlocked(buffered *self) } /*[clinic input] +@critical_section _io._Buffered.flush [clinic start generated code]*/ static PyObject * _io__Buffered_flush_impl(buffered *self) -/*[clinic end generated code: output=da2674ef1ce71f3a input=fda63444697c6bf4]*/ +/*[clinic end generated code: output=da2674ef1ce71f3a input=6b30de9f083419c2]*/ { PyObject *res; @@ -902,6 +950,7 @@ _io__Buffered_flush_impl(buffered *self) } /*[clinic input] +@critical_section _io._Buffered.peek size: Py_ssize_t = 0 / @@ -910,7 +959,7 @@ _io._Buffered.peek static PyObject * _io__Buffered_peek_impl(buffered *self, Py_ssize_t size) -/*[clinic end generated code: output=ba7a097ca230102b input=37ffb97d06ff4adb]*/ +/*[clinic end generated code: output=ba7a097ca230102b input=56733376f926d982]*/ { PyObject *res = NULL; @@ -934,6 +983,7 @@ _io__Buffered_peek_impl(buffered *self, Py_ssize_t size) } /*[clinic input] +@critical_section _io._Buffered.read size as n: Py_ssize_t(accept={int, NoneType}) = -1 / @@ -941,7 +991,7 @@ _io._Buffered.read static PyObject * _io__Buffered_read_impl(buffered *self, Py_ssize_t n) -/*[clinic end generated code: output=f41c78bb15b9bbe9 input=7df81e82e08a68a2]*/ +/*[clinic end generated code: output=f41c78bb15b9bbe9 input=bdb4b0425b295472]*/ { PyObject *res; @@ -975,6 +1025,7 @@ _io__Buffered_read_impl(buffered *self, Py_ssize_t n) } /*[clinic input] +@critical_section _io._Buffered.read1 size as n: Py_ssize_t = -1 / @@ -982,7 +1033,7 @@ _io._Buffered.read1 static PyObject * _io__Buffered_read1_impl(buffered *self, Py_ssize_t n) -/*[clinic end generated code: output=bcc4fb4e54d103a3 input=7d22de9630b61774]*/ +/*[clinic end generated code: output=bcc4fb4e54d103a3 input=3d0ad241aa52b36c]*/ { Py_ssize_t have, r; PyObject *res = NULL; @@ -1111,6 +1162,7 @@ _buffered_readinto_generic(buffered *self, Py_buffer *buffer, char readinto1) } /*[clinic input] +@critical_section _io._Buffered.readinto buffer: Py_buffer(accept={rwbuffer}) / @@ -1118,12 +1170,13 @@ _io._Buffered.readinto static PyObject * _io__Buffered_readinto_impl(buffered *self, Py_buffer *buffer) -/*[clinic end generated code: output=bcb376580b1d8170 input=ed6b98b7a20a3008]*/ +/*[clinic end generated code: output=bcb376580b1d8170 input=777c33e7adaa2bcd]*/ { return _buffered_readinto_generic(self, buffer, 0); } /*[clinic input] +@critical_section _io._Buffered.readinto1 buffer: Py_buffer(accept={rwbuffer}) / @@ -1131,7 +1184,7 @@ _io._Buffered.readinto1 static PyObject * _io__Buffered_readinto1_impl(buffered *self, Py_buffer *buffer) -/*[clinic end generated code: output=6e5c6ac5868205d6 input=4455c5d55fdf1687]*/ +/*[clinic end generated code: output=6e5c6ac5868205d6 input=ef03cc5fc92a6895]*/ { return _buffered_readinto_generic(self, buffer, 1); } @@ -1246,6 +1299,7 @@ _buffered_readline(buffered *self, Py_ssize_t limit) } /*[clinic input] +@critical_section _io._Buffered.readline size: Py_ssize_t(accept={int, NoneType}) = -1 / @@ -1253,7 +1307,7 @@ _io._Buffered.readline static PyObject * _io__Buffered_readline_impl(buffered *self, Py_ssize_t size) -/*[clinic end generated code: output=24dd2aa6e33be83c input=673b6240e315ef8a]*/ +/*[clinic end generated code: output=24dd2aa6e33be83c input=e81ca5abd4280776]*/ { CHECK_INITIALIZED(self) return _buffered_readline(self, size); @@ -1261,12 +1315,13 @@ _io__Buffered_readline_impl(buffered *self, Py_ssize_t size) /*[clinic input] +@critical_section _io._Buffered.tell [clinic start generated code]*/ static PyObject * _io__Buffered_tell_impl(buffered *self) -/*[clinic end generated code: output=386972ae84716c1e input=ad61e04a6b349573]*/ +/*[clinic end generated code: output=386972ae84716c1e input=ab12e67d8abcb42f]*/ { Py_off_t pos; @@ -1280,6 +1335,7 @@ _io__Buffered_tell_impl(buffered *self) } /*[clinic input] +@critical_section _io._Buffered.seek target as targetobj: object whence: int = 0 @@ -1288,7 +1344,7 @@ _io._Buffered.seek static PyObject * _io__Buffered_seek_impl(buffered *self, PyObject *targetobj, int whence) -/*[clinic end generated code: output=7ae0e8dc46efdefb input=a9c4920bfcba6163]*/ +/*[clinic end generated code: output=7ae0e8dc46efdefb input=b5a12be70e0ad07b]*/ { Py_off_t target, n; PyObject *res = NULL; @@ -1377,6 +1433,7 @@ _io__Buffered_seek_impl(buffered *self, PyObject *targetobj, int whence) } /*[clinic input] +@critical_section _io._Buffered.truncate cls: defining_class pos: object = None @@ -1385,7 +1442,7 @@ _io._Buffered.truncate static PyObject * _io__Buffered_truncate_impl(buffered *self, PyTypeObject *cls, PyObject *pos) -/*[clinic end generated code: output=fe3882fbffe79f1a input=f5b737d97d76303f]*/ +/*[clinic end generated code: output=fe3882fbffe79f1a input=e3cbf794575bd794]*/ { PyObject *res = NULL; @@ -1999,6 +2056,7 @@ _bufferedwriter_flush_unlocked(buffered *self) } /*[clinic input] +@critical_section _io.BufferedWriter.write buffer: Py_buffer / @@ -2006,7 +2064,7 @@ _io.BufferedWriter.write static PyObject * _io_BufferedWriter_write_impl(buffered *self, Py_buffer *buffer) -/*[clinic end generated code: output=7f8d1365759bfc6b input=dd87dd85fc7f8850]*/ +/*[clinic end generated code: output=7f8d1365759bfc6b input=6a9c041de0c337be]*/ { PyObject *res = NULL; Py_ssize_t written, avail, remaining; diff --git a/Modules/_io/clinic/bufferedio.c.h b/Modules/_io/clinic/bufferedio.c.h index dd2fbb403c4a28..20833a10139681 100644 --- a/Modules/_io/clinic/bufferedio.c.h +++ b/Modules/_io/clinic/bufferedio.c.h @@ -31,7 +31,9 @@ _io__BufferedIOBase_readinto(PyObject *self, PyObject *arg) _PyArg_BadArgument("readinto", "argument", "read-write bytes-like object", arg); goto exit; } + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__BufferedIOBase_readinto_impl(self, &buffer); + Py_END_CRITICAL_SECTION(); exit: /* Cleanup for buffer */ @@ -63,7 +65,9 @@ _io__BufferedIOBase_readinto1(PyObject *self, PyObject *arg) _PyArg_BadArgument("readinto1", "argument", "read-write bytes-like object", arg); goto exit; } + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__BufferedIOBase_readinto1_impl(self, &buffer); + Py_END_CRITICAL_SECTION(); exit: /* Cleanup for buffer */ @@ -283,7 +287,13 @@ _io__Buffered___sizeof___impl(buffered *self); static PyObject * _io__Buffered___sizeof__(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered___sizeof___impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered___sizeof___impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered__dealloc_warn__doc__, @@ -308,7 +318,13 @@ _io__Buffered_simple_flush_impl(buffered *self); static PyObject * _io__Buffered_simple_flush(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_simple_flush_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_simple_flush_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_close__doc__, @@ -348,7 +364,13 @@ _io__Buffered_detach_impl(buffered *self); static PyObject * _io__Buffered_detach(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_detach_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_detach_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_seekable__doc__, @@ -365,7 +387,13 @@ _io__Buffered_seekable_impl(buffered *self); static PyObject * _io__Buffered_seekable(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_seekable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_seekable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_readable__doc__, @@ -382,7 +410,13 @@ _io__Buffered_readable_impl(buffered *self); static PyObject * _io__Buffered_readable(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_readable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_readable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_writable__doc__, @@ -399,7 +433,13 @@ _io__Buffered_writable_impl(buffered *self); static PyObject * _io__Buffered_writable(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_writable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_writable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_fileno__doc__, @@ -416,7 +456,13 @@ _io__Buffered_fileno_impl(buffered *self); static PyObject * _io__Buffered_fileno(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_fileno_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_fileno_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_isatty__doc__, @@ -433,7 +479,13 @@ _io__Buffered_isatty_impl(buffered *self); static PyObject * _io__Buffered_isatty(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_isatty_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_isatty_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_flush__doc__, @@ -450,7 +502,13 @@ _io__Buffered_flush_impl(buffered *self); static PyObject * _io__Buffered_flush(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_flush_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_flush_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_peek__doc__, @@ -489,7 +547,9 @@ _io__Buffered_peek(buffered *self, PyObject *const *args, Py_ssize_t nargs) size = ival; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__Buffered_peek_impl(self, size); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -522,7 +582,9 @@ _io__Buffered_read(buffered *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__Buffered_read_impl(self, n); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -564,7 +626,9 @@ _io__Buffered_read1(buffered *self, PyObject *const *args, Py_ssize_t nargs) n = ival; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__Buffered_read1_impl(self, n); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -591,7 +655,9 @@ _io__Buffered_readinto(buffered *self, PyObject *arg) _PyArg_BadArgument("readinto", "argument", "read-write bytes-like object", arg); goto exit; } + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__Buffered_readinto_impl(self, &buffer); + Py_END_CRITICAL_SECTION(); exit: /* Cleanup for buffer */ @@ -623,7 +689,9 @@ _io__Buffered_readinto1(buffered *self, PyObject *arg) _PyArg_BadArgument("readinto1", "argument", "read-write bytes-like object", arg); goto exit; } + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__Buffered_readinto1_impl(self, &buffer); + Py_END_CRITICAL_SECTION(); exit: /* Cleanup for buffer */ @@ -661,7 +729,9 @@ _io__Buffered_readline(buffered *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__Buffered_readline_impl(self, size); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -681,7 +751,13 @@ _io__Buffered_tell_impl(buffered *self); static PyObject * _io__Buffered_tell(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_tell_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_tell_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_seek__doc__, @@ -714,7 +790,9 @@ _io__Buffered_seek(buffered *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__Buffered_seek_impl(self, targetobj, whence); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -760,7 +838,9 @@ _io__Buffered_truncate(buffered *self, PyTypeObject *cls, PyObject *const *args, } pos = args[0]; skip_optional_posonly: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__Buffered_truncate_impl(self, cls, pos); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -936,7 +1016,9 @@ _io_BufferedWriter_write(buffered *self, PyObject *arg) if (PyObject_GetBuffer(arg, &buffer, PyBUF_SIMPLE) != 0) { goto exit; } + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_BufferedWriter_write_impl(self, &buffer); + Py_END_CRITICAL_SECTION(); exit: /* Cleanup for buffer */ @@ -1082,4 +1164,4 @@ _io_BufferedRandom___init__(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=2d5f735188df3163 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=e8ad39a45531d7f2 input=a9049054013a1b77]*/ From 1619f4350ee431d2fa2f7c0b89702e897d9d14a2 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 22 Nov 2023 15:19:50 +0000 Subject: [PATCH 036/228] GH-111485: Sort cases in the case generator output (GH-112315) --- Lib/test/test_generated_cases.py | 26 +- Python/generated_cases.c.h | 9112 +++++++++++------------ Tools/cases_generator/generate_cases.py | 6 +- 3 files changed, 4574 insertions(+), 4570 deletions(-) diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 585be642f0427d..98a8fff4268746 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -405,19 +405,6 @@ def test_macro_instruction(self): family(OP, INLINE_CACHE_ENTRIES_OP) = { OP3 }; """ output = """ - TARGET(OP1) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(OP1); - PyObject *right; - PyObject *left; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - uint16_t counter = read_u16(&this_instr[1].cache); - op1(left, right); - DISPATCH(); - } - TARGET(OP) { frame->instr_ptr = next_instr; next_instr += 6; @@ -447,6 +434,19 @@ def test_macro_instruction(self): DISPATCH(); } + TARGET(OP1) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(OP1); + PyObject *right; + PyObject *left; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + uint16_t counter = read_u16(&this_instr[1].cache); + op1(left, right); + DISPATCH(); + } + TARGET(OP3) { frame->instr_ptr = next_instr; next_instr += 6; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 3b4cc7562da081..dedd793111b7ff 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -8,1230 +8,1703 @@ #endif #define TIER_ONE 1 - TARGET(NOP) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(NOP); - DISPATCH(); - } - - TARGET(RESUME) { + TARGET(BEFORE_ASYNC_WITH) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(RESUME); - PREDICTED(RESUME); - _Py_CODEUNIT *this_instr = next_instr - 1; - static_assert(0 == 0, "incorrect cache size"); - TIER_ONE_ONLY - assert(frame == tstate->current_frame); - uintptr_t global_version = - _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker) & - ~_PY_EVAL_EVENTS_MASK; - uintptr_t code_version = _PyFrame_GetCode(frame)->_co_instrumentation_version; - assert((code_version & 255) == 0); - if (code_version != global_version) { - int err = _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp); - if (err) goto error; - next_instr = this_instr; + INSTRUCTION_STATS(BEFORE_ASYNC_WITH); + PyObject *mgr; + PyObject *exit; + PyObject *res; + mgr = stack_pointer[-1]; + PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__aenter__)); + if (enter == NULL) { + if (!_PyErr_Occurred(tstate)) { + _PyErr_Format(tstate, PyExc_TypeError, + "'%.200s' object does not support the " + "asynchronous context manager protocol", + Py_TYPE(mgr)->tp_name); + } + GOTO_ERROR(error); } - else { - if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { - CHECK_EVAL_BREAKER(); + exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__)); + if (exit == NULL) { + if (!_PyErr_Occurred(tstate)) { + _PyErr_Format(tstate, PyExc_TypeError, + "'%.200s' object does not support the " + "asynchronous context manager protocol " + "(missed __aexit__ method)", + Py_TYPE(mgr)->tp_name); } - this_instr->op.code = RESUME_CHECK; + Py_DECREF(enter); + GOTO_ERROR(error); + } + Py_DECREF(mgr); + res = _PyObject_CallNoArgsTstate(tstate, enter); + Py_DECREF(enter); + if (res == NULL) { + Py_DECREF(exit); + if (true) goto pop_1_error; } + STACK_GROW(1); + stack_pointer[-2] = exit; + stack_pointer[-1] = res; DISPATCH(); } - TARGET(RESUME_CHECK) { + TARGET(BEFORE_WITH) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(RESUME_CHECK); -#if defined(__EMSCRIPTEN__) - DEOPT_IF(_Py_emscripten_signal_clock == 0, RESUME); - _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; -#endif - uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker); - uintptr_t version = _PyFrame_GetCode(frame)->_co_instrumentation_version; - assert((version & _PY_EVAL_EVENTS_MASK) == 0); - DEOPT_IF(eval_breaker != version, RESUME); - DISPATCH(); - } - - TARGET(INSTRUMENTED_RESUME) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_RESUME); - uintptr_t global_version = _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker) & ~_PY_EVAL_EVENTS_MASK; - uintptr_t code_version = _PyFrame_GetCode(frame)->_co_instrumentation_version; - if (code_version != global_version) { - if (_Py_Instrument(_PyFrame_GetCode(frame), tstate->interp)) { - GOTO_ERROR(error); + INSTRUCTION_STATS(BEFORE_WITH); + PyObject *mgr; + PyObject *exit; + PyObject *res; + mgr = stack_pointer[-1]; + /* pop the context manager, push its __exit__ and the + * value returned from calling its __enter__ + */ + PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__enter__)); + if (enter == NULL) { + if (!_PyErr_Occurred(tstate)) { + _PyErr_Format(tstate, PyExc_TypeError, + "'%.200s' object does not support the " + "context manager protocol", + Py_TYPE(mgr)->tp_name); } - next_instr = this_instr; + GOTO_ERROR(error); } - else { - if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { - CHECK_EVAL_BREAKER(); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_call_instrumentation( - tstate, oparg > 0, frame, this_instr); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err) goto error; - if (frame->instr_ptr != this_instr) { - /* Instrumentation has jumped */ - next_instr = this_instr; - DISPATCH(); + exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__)); + if (exit == NULL) { + if (!_PyErr_Occurred(tstate)) { + _PyErr_Format(tstate, PyExc_TypeError, + "'%.200s' object does not support the " + "context manager protocol " + "(missed __exit__ method)", + Py_TYPE(mgr)->tp_name); } + Py_DECREF(enter); + GOTO_ERROR(error); + } + Py_DECREF(mgr); + res = _PyObject_CallNoArgsTstate(tstate, enter); + Py_DECREF(enter); + if (res == NULL) { + Py_DECREF(exit); + if (true) goto pop_1_error; } + STACK_GROW(1); + stack_pointer[-2] = exit; + stack_pointer[-1] = res; DISPATCH(); } - TARGET(LOAD_FAST_CHECK) { + TARGET(BINARY_OP) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(LOAD_FAST_CHECK); - PyObject *value; - value = GETLOCAL(oparg); - if (value == NULL) goto unbound_local_error; - Py_INCREF(value); - STACK_GROW(1); - stack_pointer[-1] = value; + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP); + PREDICTED(BINARY_OP); + _Py_CODEUNIT *this_instr = next_instr - 2; + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); + PyObject *rhs; + PyObject *lhs; + PyObject *res; + // _SPECIALIZE_BINARY_OP + rhs = stack_pointer[-1]; + lhs = stack_pointer[-2]; + { + uint16_t counter = read_u16(&this_instr[1].cache); + TIER_ONE_ONLY + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + next_instr = this_instr; + _Py_Specialize_BinaryOp(lhs, rhs, next_instr, oparg, LOCALS_ARRAY); + DISPATCH_SAME_OPARG(); + } + STAT_INC(BINARY_OP, deferred); + DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + #endif /* ENABLE_SPECIALIZATION */ + assert(NB_ADD <= oparg); + assert(oparg <= NB_INPLACE_XOR); + } + // _BINARY_OP + { + assert(_PyEval_BinaryOps[oparg]); + res = _PyEval_BinaryOps[oparg](lhs, rhs); + Py_DECREF(lhs); + Py_DECREF(rhs); + if (res == NULL) goto pop_2_error; + } + STACK_SHRINK(1); + stack_pointer[-1] = res; DISPATCH(); } - TARGET(LOAD_FAST) { + TARGET(BINARY_OP_ADD_FLOAT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(LOAD_FAST); - PyObject *value; - value = GETLOCAL(oparg); - assert(value != NULL); - Py_INCREF(value); - STACK_GROW(1); - stack_pointer[-1] = value; + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP_ADD_FLOAT); + PyObject *right; + PyObject *left; + PyObject *res; + // _GUARD_BOTH_FLOAT + right = stack_pointer[-1]; + left = stack_pointer[-2]; + { + DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP); + } + // _BINARY_OP_ADD_FLOAT + { + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left)->ob_fval + + ((PyFloatObject *)right)->ob_fval; + DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); + } + STACK_SHRINK(1); + stack_pointer[-1] = res; DISPATCH(); } - TARGET(LOAD_FAST_AND_CLEAR) { + TARGET(BINARY_OP_ADD_INT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(LOAD_FAST_AND_CLEAR); - PyObject *value; - value = GETLOCAL(oparg); - // do not use SETLOCAL here, it decrefs the old value - GETLOCAL(oparg) = NULL; - STACK_GROW(1); - stack_pointer[-1] = value; + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP_ADD_INT); + PyObject *right; + PyObject *left; + PyObject *res; + // _GUARD_BOTH_INT + right = stack_pointer[-1]; + left = stack_pointer[-2]; + { + DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP); + } + // _BINARY_OP_ADD_INT + { + STAT_INC(BINARY_OP, hit); + res = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right); + _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); + _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); + if (res == NULL) goto pop_2_error; + } + STACK_SHRINK(1); + stack_pointer[-1] = res; DISPATCH(); } - TARGET(LOAD_FAST_LOAD_FAST) { + TARGET(BINARY_OP_ADD_UNICODE) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(LOAD_FAST_LOAD_FAST); - PyObject *value1; - PyObject *value2; - uint32_t oparg1 = oparg >> 4; - uint32_t oparg2 = oparg & 15; - value1 = GETLOCAL(oparg1); - value2 = GETLOCAL(oparg2); - Py_INCREF(value1); - Py_INCREF(value2); - STACK_GROW(2); - stack_pointer[-2] = value1; - stack_pointer[-1] = value2; + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP_ADD_UNICODE); + PyObject *right; + PyObject *left; + PyObject *res; + // _GUARD_BOTH_UNICODE + right = stack_pointer[-1]; + left = stack_pointer[-2]; + { + DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyUnicode_CheckExact(right), BINARY_OP); + } + // _BINARY_OP_ADD_UNICODE + { + STAT_INC(BINARY_OP, hit); + res = PyUnicode_Concat(left, right); + _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); + _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); + if (res == NULL) goto pop_2_error; + } + STACK_SHRINK(1); + stack_pointer[-1] = res; DISPATCH(); } - TARGET(LOAD_CONST) { + TARGET(BINARY_OP_INPLACE_ADD_UNICODE) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(LOAD_CONST); - PyObject *value; - value = GETITEM(FRAME_CO_CONSTS, oparg); - Py_INCREF(value); - STACK_GROW(1); - stack_pointer[-1] = value; + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP_INPLACE_ADD_UNICODE); + PyObject *right; + PyObject *left; + // _GUARD_BOTH_UNICODE + right = stack_pointer[-1]; + left = stack_pointer[-2]; + { + DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyUnicode_CheckExact(right), BINARY_OP); + } + // _BINARY_OP_INPLACE_ADD_UNICODE + { + assert(next_instr->op.code == STORE_FAST); + PyObject **target_local = &GETLOCAL(next_instr->op.arg); + DEOPT_IF(*target_local != left, BINARY_OP); + STAT_INC(BINARY_OP, hit); + /* Handle `left = left + right` or `left += right` for str. + * + * When possible, extend `left` in place rather than + * allocating a new PyUnicodeObject. This attempts to avoid + * quadratic behavior when one neglects to use str.join(). + * + * If `left` has only two references remaining (one from + * the stack, one in the locals), DECREFing `left` leaves + * only the locals reference, so PyUnicode_Append knows + * that the string is safe to mutate. + */ + assert(Py_REFCNT(left) >= 2); + _Py_DECREF_NO_DEALLOC(left); + PyUnicode_Append(target_local, right); + _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); + if (*target_local == NULL) goto pop_2_error; + // The STORE_FAST is already done. + assert(next_instr->op.code == STORE_FAST); + SKIP_OVER(1); + } + STACK_SHRINK(2); DISPATCH(); } - TARGET(STORE_FAST) { + TARGET(BINARY_OP_MULTIPLY_FLOAT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(STORE_FAST); - PyObject *value; - value = stack_pointer[-1]; - SETLOCAL(oparg, value); + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP_MULTIPLY_FLOAT); + PyObject *right; + PyObject *left; + PyObject *res; + // _GUARD_BOTH_FLOAT + right = stack_pointer[-1]; + left = stack_pointer[-2]; + { + DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP); + } + // _BINARY_OP_MULTIPLY_FLOAT + { + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left)->ob_fval * + ((PyFloatObject *)right)->ob_fval; + DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); + } STACK_SHRINK(1); + stack_pointer[-1] = res; DISPATCH(); } - TARGET(STORE_FAST_LOAD_FAST) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(STORE_FAST_LOAD_FAST); - PyObject *value1; - PyObject *value2; - value1 = stack_pointer[-1]; - uint32_t oparg1 = oparg >> 4; - uint32_t oparg2 = oparg & 15; - SETLOCAL(oparg1, value1); - value2 = GETLOCAL(oparg2); - Py_INCREF(value2); - stack_pointer[-1] = value2; - DISPATCH(); - } - - TARGET(STORE_FAST_STORE_FAST) { + TARGET(BINARY_OP_MULTIPLY_INT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(STORE_FAST_STORE_FAST); - PyObject *value1; - PyObject *value2; - value1 = stack_pointer[-1]; - value2 = stack_pointer[-2]; - uint32_t oparg1 = oparg >> 4; - uint32_t oparg2 = oparg & 15; - SETLOCAL(oparg1, value1); - SETLOCAL(oparg2, value2); - STACK_SHRINK(2); + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP_MULTIPLY_INT); + PyObject *right; + PyObject *left; + PyObject *res; + // _GUARD_BOTH_INT + right = stack_pointer[-1]; + left = stack_pointer[-2]; + { + DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP); + } + // _BINARY_OP_MULTIPLY_INT + { + STAT_INC(BINARY_OP, hit); + res = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right); + _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); + _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); + if (res == NULL) goto pop_2_error; + } + STACK_SHRINK(1); + stack_pointer[-1] = res; DISPATCH(); } - TARGET(POP_TOP) { + TARGET(BINARY_OP_SUBTRACT_FLOAT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(POP_TOP); - PyObject *value; - value = stack_pointer[-1]; - Py_DECREF(value); + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP_SUBTRACT_FLOAT); + PyObject *right; + PyObject *left; + PyObject *res; + // _GUARD_BOTH_FLOAT + right = stack_pointer[-1]; + left = stack_pointer[-2]; + { + DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP); + } + // _BINARY_OP_SUBTRACT_FLOAT + { + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left)->ob_fval - + ((PyFloatObject *)right)->ob_fval; + DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); + } STACK_SHRINK(1); + stack_pointer[-1] = res; DISPATCH(); } - TARGET(PUSH_NULL) { + TARGET(BINARY_OP_SUBTRACT_INT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(PUSH_NULL); + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP_SUBTRACT_INT); + PyObject *right; + PyObject *left; PyObject *res; - res = NULL; - STACK_GROW(1); + // _GUARD_BOTH_INT + right = stack_pointer[-1]; + left = stack_pointer[-2]; + { + DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP); + } + // _BINARY_OP_SUBTRACT_INT + { + STAT_INC(BINARY_OP, hit); + res = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right); + _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); + _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); + if (res == NULL) goto pop_2_error; + } + STACK_SHRINK(1); stack_pointer[-1] = res; DISPATCH(); } - TARGET(END_FOR) { + TARGET(BINARY_SLICE) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(END_FOR); - PyObject *value; - // POP_TOP - value = stack_pointer[-1]; - { - Py_DECREF(value); + INSTRUCTION_STATS(BINARY_SLICE); + PyObject *stop; + PyObject *start; + PyObject *container; + PyObject *res; + stop = stack_pointer[-1]; + start = stack_pointer[-2]; + container = stack_pointer[-3]; + PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop); + // Can't use ERROR_IF() here, because we haven't + // DECREF'ed container yet, and we still own slice. + if (slice == NULL) { + res = NULL; } - // POP_TOP - value = stack_pointer[-2]; - { - Py_DECREF(value); + else { + res = PyObject_GetItem(container, slice); + Py_DECREF(slice); } + Py_DECREF(container); + if (res == NULL) goto pop_3_error; STACK_SHRINK(2); + stack_pointer[-1] = res; DISPATCH(); } - TARGET(INSTRUMENTED_END_FOR) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_END_FOR); - PyObject *value; - PyObject *receiver; - value = stack_pointer[-1]; - receiver = stack_pointer[-2]; - TIER_ONE_ONLY - /* Need to create a fake StopIteration error here, - * to conform to PEP 380 */ - if (PyGen_Check(receiver)) { - PyErr_SetObject(PyExc_StopIteration, value); - if (monitor_stop_iteration(tstate, frame, this_instr)) { - GOTO_ERROR(error); - } - PyErr_SetRaisedException(NULL); - } - Py_DECREF(receiver); - Py_DECREF(value); - STACK_SHRINK(2); - DISPATCH(); - } - - TARGET(END_SEND) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(END_SEND); - PyObject *value; - PyObject *receiver; - value = stack_pointer[-1]; - receiver = stack_pointer[-2]; - Py_DECREF(receiver); - STACK_SHRINK(1); - stack_pointer[-1] = value; - DISPATCH(); - } - - TARGET(INSTRUMENTED_END_SEND) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_END_SEND); - PyObject *value; - PyObject *receiver; - value = stack_pointer[-1]; - receiver = stack_pointer[-2]; - TIER_ONE_ONLY - if (PyGen_Check(receiver) || PyCoro_CheckExact(receiver)) { - PyErr_SetObject(PyExc_StopIteration, value); - if (monitor_stop_iteration(tstate, frame, this_instr)) { - GOTO_ERROR(error); - } - PyErr_SetRaisedException(NULL); - } - Py_DECREF(receiver); - STACK_SHRINK(1); - stack_pointer[-1] = value; - DISPATCH(); - } - - TARGET(UNARY_NEGATIVE) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(UNARY_NEGATIVE); - PyObject *value; - PyObject *res; - value = stack_pointer[-1]; - res = PyNumber_Negative(value); - Py_DECREF(value); - if (res == NULL) goto pop_1_error; - stack_pointer[-1] = res; - DISPATCH(); - } - - TARGET(UNARY_NOT) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(UNARY_NOT); - PyObject *value; - PyObject *res; - value = stack_pointer[-1]; - assert(PyBool_Check(value)); - res = Py_IsFalse(value) ? Py_True : Py_False; - stack_pointer[-1] = res; - DISPATCH(); - } - - TARGET(TO_BOOL) { + TARGET(BINARY_SUBSCR) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(TO_BOOL); - PREDICTED(TO_BOOL); - _Py_CODEUNIT *this_instr = next_instr - 4; - static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); - PyObject *value; + next_instr += 2; + INSTRUCTION_STATS(BINARY_SUBSCR); + PREDICTED(BINARY_SUBSCR); + _Py_CODEUNIT *this_instr = next_instr - 2; + static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); + PyObject *sub; + PyObject *container; PyObject *res; - // _SPECIALIZE_TO_BOOL - value = stack_pointer[-1]; + // _SPECIALIZE_BINARY_SUBSCR + sub = stack_pointer[-1]; + container = stack_pointer[-2]; { uint16_t counter = read_u16(&this_instr[1].cache); TIER_ONE_ONLY #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { next_instr = this_instr; - _Py_Specialize_ToBool(value, next_instr); + _Py_Specialize_BinarySubscr(container, sub, next_instr); DISPATCH_SAME_OPARG(); } - STAT_INC(TO_BOOL, deferred); + STAT_INC(BINARY_SUBSCR, deferred); DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); #endif /* ENABLE_SPECIALIZATION */ } - // _TO_BOOL + // _BINARY_SUBSCR { - int err = PyObject_IsTrue(value); - Py_DECREF(value); - if (err < 0) goto pop_1_error; - res = err ? Py_True : Py_False; + res = PyObject_GetItem(container, sub); + Py_DECREF(container); + Py_DECREF(sub); + if (res == NULL) goto pop_2_error; } + STACK_SHRINK(1); stack_pointer[-1] = res; DISPATCH(); } - TARGET(TO_BOOL_BOOL) { + TARGET(BINARY_SUBSCR_DICT) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(TO_BOOL_BOOL); - PyObject *value; - value = stack_pointer[-1]; - DEOPT_IF(!PyBool_Check(value), TO_BOOL); - STAT_INC(TO_BOOL, hit); + next_instr += 2; + INSTRUCTION_STATS(BINARY_SUBSCR_DICT); + PyObject *sub; + PyObject *dict; + PyObject *res; + sub = stack_pointer[-1]; + dict = stack_pointer[-2]; + DEOPT_IF(!PyDict_CheckExact(dict), BINARY_SUBSCR); + STAT_INC(BINARY_SUBSCR, hit); + int rc = PyDict_GetItemRef(dict, sub, &res); + if (rc == 0) { + _PyErr_SetKeyError(sub); + } + Py_DECREF(dict); + Py_DECREF(sub); + if (rc <= 0) goto pop_2_error; + STACK_SHRINK(1); + stack_pointer[-1] = res; DISPATCH(); } - TARGET(TO_BOOL_INT) { + TARGET(BINARY_SUBSCR_GETITEM) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(BINARY_SUBSCR_GETITEM); + PyObject *sub; + PyObject *container; + sub = stack_pointer[-1]; + container = stack_pointer[-2]; + DEOPT_IF(tstate->interp->eval_frame, BINARY_SUBSCR); + PyTypeObject *tp = Py_TYPE(container); + DEOPT_IF(!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE), BINARY_SUBSCR); + PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; + PyObject *cached = ht->_spec_cache.getitem; + DEOPT_IF(cached == NULL, BINARY_SUBSCR); + assert(PyFunction_Check(cached)); + PyFunctionObject *getitem = (PyFunctionObject *)cached; + uint32_t cached_version = ht->_spec_cache.getitem_version; + DEOPT_IF(getitem->func_version != cached_version, BINARY_SUBSCR); + PyCodeObject *code = (PyCodeObject *)getitem->func_code; + assert(code->co_argcount == 2); + DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), BINARY_SUBSCR); + STAT_INC(BINARY_SUBSCR, hit); + Py_INCREF(getitem); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, getitem, 2); + STACK_SHRINK(2); + new_frame->localsplus[0] = container; + new_frame->localsplus[1] = sub; + frame->return_offset = (uint16_t)(next_instr - this_instr); + DISPATCH_INLINED(new_frame); + } + + TARGET(BINARY_SUBSCR_LIST_INT) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(TO_BOOL_INT); - PyObject *value; + next_instr += 2; + INSTRUCTION_STATS(BINARY_SUBSCR_LIST_INT); + PyObject *sub; + PyObject *list; PyObject *res; - value = stack_pointer[-1]; - DEOPT_IF(!PyLong_CheckExact(value), TO_BOOL); - STAT_INC(TO_BOOL, hit); - if (_PyLong_IsZero((PyLongObject *)value)) { - assert(_Py_IsImmortal(value)); - res = Py_False; - } - else { - Py_DECREF(value); - res = Py_True; - } + sub = stack_pointer[-1]; + list = stack_pointer[-2]; + DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); + DEOPT_IF(!PyList_CheckExact(list), BINARY_SUBSCR); + + // Deopt unless 0 <= sub < PyList_Size(list) + DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR); + STAT_INC(BINARY_SUBSCR, hit); + res = PyList_GET_ITEM(list, index); + assert(res != NULL); + Py_INCREF(res); + _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); + Py_DECREF(list); + STACK_SHRINK(1); stack_pointer[-1] = res; DISPATCH(); } - TARGET(TO_BOOL_LIST) { + TARGET(BINARY_SUBSCR_STR_INT) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(TO_BOOL_LIST); - PyObject *value; + next_instr += 2; + INSTRUCTION_STATS(BINARY_SUBSCR_STR_INT); + PyObject *sub; + PyObject *str; PyObject *res; - value = stack_pointer[-1]; - DEOPT_IF(!PyList_CheckExact(value), TO_BOOL); - STAT_INC(TO_BOOL, hit); - res = Py_SIZE(value) ? Py_True : Py_False; - Py_DECREF(value); + sub = stack_pointer[-1]; + str = stack_pointer[-2]; + DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); + DEOPT_IF(!PyUnicode_CheckExact(str), BINARY_SUBSCR); + DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + DEOPT_IF(PyUnicode_GET_LENGTH(str) <= index, BINARY_SUBSCR); + // Specialize for reading an ASCII character from any string: + Py_UCS4 c = PyUnicode_READ_CHAR(str, index); + DEOPT_IF(Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c, BINARY_SUBSCR); + STAT_INC(BINARY_SUBSCR, hit); + res = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; + _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); + Py_DECREF(str); + STACK_SHRINK(1); stack_pointer[-1] = res; DISPATCH(); } - TARGET(TO_BOOL_NONE) { + TARGET(BINARY_SUBSCR_TUPLE_INT) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(TO_BOOL_NONE); - PyObject *value; + next_instr += 2; + INSTRUCTION_STATS(BINARY_SUBSCR_TUPLE_INT); + PyObject *sub; + PyObject *tuple; PyObject *res; - value = stack_pointer[-1]; - // This one is a bit weird, because we expect *some* failures: - DEOPT_IF(!Py_IsNone(value), TO_BOOL); - STAT_INC(TO_BOOL, hit); - res = Py_False; + sub = stack_pointer[-1]; + tuple = stack_pointer[-2]; + DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); + DEOPT_IF(!PyTuple_CheckExact(tuple), BINARY_SUBSCR); + + // Deopt unless 0 <= sub < PyTuple_Size(list) + DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + DEOPT_IF(index >= PyTuple_GET_SIZE(tuple), BINARY_SUBSCR); + STAT_INC(BINARY_SUBSCR, hit); + res = PyTuple_GET_ITEM(tuple, index); + assert(res != NULL); + Py_INCREF(res); + _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); + Py_DECREF(tuple); + STACK_SHRINK(1); stack_pointer[-1] = res; DISPATCH(); } - TARGET(TO_BOOL_STR) { + TARGET(BUILD_CONST_KEY_MAP) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(TO_BOOL_STR); - PyObject *value; - PyObject *res; - value = stack_pointer[-1]; - DEOPT_IF(!PyUnicode_CheckExact(value), TO_BOOL); - STAT_INC(TO_BOOL, hit); - if (value == &_Py_STR(empty)) { - assert(_Py_IsImmortal(value)); - res = Py_False; + next_instr += 1; + INSTRUCTION_STATS(BUILD_CONST_KEY_MAP); + PyObject *keys; + PyObject **values; + PyObject *map; + keys = stack_pointer[-1]; + values = stack_pointer - 1 - oparg; + if (!PyTuple_CheckExact(keys) || + PyTuple_GET_SIZE(keys) != (Py_ssize_t)oparg) { + _PyErr_SetString(tstate, PyExc_SystemError, + "bad BUILD_CONST_KEY_MAP keys argument"); + GOTO_ERROR(error); // Pop the keys and values. } - else { - assert(Py_SIZE(value)); - Py_DECREF(value); - res = Py_True; + map = _PyDict_FromItems( + &PyTuple_GET_ITEM(keys, 0), 1, + values, 1, oparg); + for (int _i = oparg; --_i >= 0;) { + Py_DECREF(values[_i]); } - stack_pointer[-1] = res; + Py_DECREF(keys); + if (map == NULL) { STACK_SHRINK(oparg); goto pop_1_error; } + STACK_SHRINK(oparg); + stack_pointer[-1] = map; DISPATCH(); } - TARGET(TO_BOOL_ALWAYS_TRUE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(TO_BOOL_ALWAYS_TRUE); - PyObject *value; - PyObject *res; - value = stack_pointer[-1]; - uint32_t version = read_u32(&this_instr[2].cache); - // This one is a bit weird, because we expect *some* failures: - assert(version); - DEOPT_IF(Py_TYPE(value)->tp_version_tag != version, TO_BOOL); - STAT_INC(TO_BOOL, hit); - Py_DECREF(value); - res = Py_True; - stack_pointer[-1] = res; + TARGET(BUILD_LIST) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(BUILD_LIST); + PyObject **values; + PyObject *list; + values = stack_pointer - oparg; + list = _PyList_FromArraySteal(values, oparg); + if (list == NULL) { STACK_SHRINK(oparg); goto error; } + STACK_SHRINK(oparg); + STACK_GROW(1); + stack_pointer[-1] = list; DISPATCH(); } - TARGET(UNARY_INVERT) { + TARGET(BUILD_MAP) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(UNARY_INVERT); - PyObject *value; - PyObject *res; - value = stack_pointer[-1]; - res = PyNumber_Invert(value); - Py_DECREF(value); - if (res == NULL) goto pop_1_error; - stack_pointer[-1] = res; + INSTRUCTION_STATS(BUILD_MAP); + PyObject **values; + PyObject *map; + values = stack_pointer - oparg*2; + map = _PyDict_FromItems( + values, 2, + values+1, 2, + oparg); + for (int _i = oparg*2; --_i >= 0;) { + Py_DECREF(values[_i]); + } + if (map == NULL) { STACK_SHRINK(oparg*2); goto error; } + STACK_SHRINK(oparg*2); + STACK_GROW(1); + stack_pointer[-1] = map; DISPATCH(); } - TARGET(BINARY_OP_MULTIPLY_INT) { + TARGET(BUILD_SET) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_OP_MULTIPLY_INT); - PyObject *right; - PyObject *left; - PyObject *res; - // _GUARD_BOTH_INT - right = stack_pointer[-1]; - left = stack_pointer[-2]; - { - DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); - DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP); + next_instr += 1; + INSTRUCTION_STATS(BUILD_SET); + PyObject **values; + PyObject *set; + values = stack_pointer - oparg; + set = PySet_New(NULL); + if (set == NULL) + GOTO_ERROR(error); + int err = 0; + for (int i = 0; i < oparg; i++) { + PyObject *item = values[i]; + if (err == 0) + err = PySet_Add(set, item); + Py_DECREF(item); } - // _BINARY_OP_MULTIPLY_INT - { - STAT_INC(BINARY_OP, hit); - res = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right); - _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); - _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - if (res == NULL) goto pop_2_error; + if (err != 0) { + Py_DECREF(set); + if (true) { STACK_SHRINK(oparg); goto error; } } - STACK_SHRINK(1); - stack_pointer[-1] = res; + STACK_SHRINK(oparg); + STACK_GROW(1); + stack_pointer[-1] = set; DISPATCH(); } - TARGET(BINARY_OP_ADD_INT) { + TARGET(BUILD_SLICE) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_OP_ADD_INT); - PyObject *right; - PyObject *left; - PyObject *res; - // _GUARD_BOTH_INT - right = stack_pointer[-1]; - left = stack_pointer[-2]; - { - DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); - DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP); - } - // _BINARY_OP_ADD_INT - { - STAT_INC(BINARY_OP, hit); - res = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right); - _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); - _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - if (res == NULL) goto pop_2_error; - } + next_instr += 1; + INSTRUCTION_STATS(BUILD_SLICE); + PyObject *step = NULL; + PyObject *stop; + PyObject *start; + PyObject *slice; + if (oparg == 3) { step = stack_pointer[-(oparg == 3 ? 1 : 0)]; } + stop = stack_pointer[-1 - (oparg == 3 ? 1 : 0)]; + start = stack_pointer[-2 - (oparg == 3 ? 1 : 0)]; + slice = PySlice_New(start, stop, step); + Py_DECREF(start); + Py_DECREF(stop); + Py_XDECREF(step); + if (slice == NULL) { STACK_SHRINK(((oparg == 3) ? 1 : 0)); goto pop_2_error; } + STACK_SHRINK(((oparg == 3) ? 1 : 0)); STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-1] = slice; DISPATCH(); } - TARGET(BINARY_OP_SUBTRACT_INT) { + TARGET(BUILD_STRING) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_OP_SUBTRACT_INT); - PyObject *right; - PyObject *left; - PyObject *res; - // _GUARD_BOTH_INT - right = stack_pointer[-1]; - left = stack_pointer[-2]; - { - DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); - DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP); - } - // _BINARY_OP_SUBTRACT_INT - { - STAT_INC(BINARY_OP, hit); - res = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right); - _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); - _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - if (res == NULL) goto pop_2_error; + next_instr += 1; + INSTRUCTION_STATS(BUILD_STRING); + PyObject **pieces; + PyObject *str; + pieces = stack_pointer - oparg; + str = _PyUnicode_JoinArray(&_Py_STR(empty), pieces, oparg); + for (int _i = oparg; --_i >= 0;) { + Py_DECREF(pieces[_i]); } - STACK_SHRINK(1); - stack_pointer[-1] = res; + if (str == NULL) { STACK_SHRINK(oparg); goto error; } + STACK_SHRINK(oparg); + STACK_GROW(1); + stack_pointer[-1] = str; DISPATCH(); } - TARGET(BINARY_OP_MULTIPLY_FLOAT) { + TARGET(BUILD_TUPLE) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_OP_MULTIPLY_FLOAT); - PyObject *right; - PyObject *left; - PyObject *res; - // _GUARD_BOTH_FLOAT - right = stack_pointer[-1]; - left = stack_pointer[-2]; - { - DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); - DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP); - } - // _BINARY_OP_MULTIPLY_FLOAT - { - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left)->ob_fval * - ((PyFloatObject *)right)->ob_fval; - DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); - } - STACK_SHRINK(1); - stack_pointer[-1] = res; + next_instr += 1; + INSTRUCTION_STATS(BUILD_TUPLE); + PyObject **values; + PyObject *tup; + values = stack_pointer - oparg; + tup = _PyTuple_FromArraySteal(values, oparg); + if (tup == NULL) { STACK_SHRINK(oparg); goto error; } + STACK_SHRINK(oparg); + STACK_GROW(1); + stack_pointer[-1] = tup; DISPATCH(); } - TARGET(BINARY_OP_ADD_FLOAT) { + TARGET(CACHE) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_OP_ADD_FLOAT); - PyObject *right; - PyObject *left; - PyObject *res; - // _GUARD_BOTH_FLOAT - right = stack_pointer[-1]; - left = stack_pointer[-2]; - { - DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); - DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP); - } - // _BINARY_OP_ADD_FLOAT - { - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left)->ob_fval + - ((PyFloatObject *)right)->ob_fval; - DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); - } - STACK_SHRINK(1); - stack_pointer[-1] = res; - DISPATCH(); - } - - TARGET(BINARY_OP_SUBTRACT_FLOAT) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_OP_SUBTRACT_FLOAT); - PyObject *right; - PyObject *left; - PyObject *res; - // _GUARD_BOTH_FLOAT - right = stack_pointer[-1]; - left = stack_pointer[-2]; - { - DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); - DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP); - } - // _BINARY_OP_SUBTRACT_FLOAT - { - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left)->ob_fval - - ((PyFloatObject *)right)->ob_fval; - DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); - } - STACK_SHRINK(1); - stack_pointer[-1] = res; - DISPATCH(); - } - - TARGET(BINARY_OP_ADD_UNICODE) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_OP_ADD_UNICODE); - PyObject *right; - PyObject *left; - PyObject *res; - // _GUARD_BOTH_UNICODE - right = stack_pointer[-1]; - left = stack_pointer[-2]; - { - DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); - DEOPT_IF(!PyUnicode_CheckExact(right), BINARY_OP); - } - // _BINARY_OP_ADD_UNICODE - { - STAT_INC(BINARY_OP, hit); - res = PyUnicode_Concat(left, right); - _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); - _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); - if (res == NULL) goto pop_2_error; - } - STACK_SHRINK(1); - stack_pointer[-1] = res; - DISPATCH(); - } - - TARGET(BINARY_OP_INPLACE_ADD_UNICODE) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_OP_INPLACE_ADD_UNICODE); - PyObject *right; - PyObject *left; - // _GUARD_BOTH_UNICODE - right = stack_pointer[-1]; - left = stack_pointer[-2]; - { - DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); - DEOPT_IF(!PyUnicode_CheckExact(right), BINARY_OP); - } - // _BINARY_OP_INPLACE_ADD_UNICODE - { - assert(next_instr->op.code == STORE_FAST); - PyObject **target_local = &GETLOCAL(next_instr->op.arg); - DEOPT_IF(*target_local != left, BINARY_OP); - STAT_INC(BINARY_OP, hit); - /* Handle `left = left + right` or `left += right` for str. - * - * When possible, extend `left` in place rather than - * allocating a new PyUnicodeObject. This attempts to avoid - * quadratic behavior when one neglects to use str.join(). - * - * If `left` has only two references remaining (one from - * the stack, one in the locals), DECREFing `left` leaves - * only the locals reference, so PyUnicode_Append knows - * that the string is safe to mutate. - */ - assert(Py_REFCNT(left) >= 2); - _Py_DECREF_NO_DEALLOC(left); - PyUnicode_Append(target_local, right); - _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); - if (*target_local == NULL) goto pop_2_error; - // The STORE_FAST is already done. - assert(next_instr->op.code == STORE_FAST); - SKIP_OVER(1); - } - STACK_SHRINK(2); - DISPATCH(); + next_instr += 1; + INSTRUCTION_STATS(CACHE); + assert(0 && "Executing a cache."); + Py_UNREACHABLE(); } - TARGET(BINARY_SUBSCR) { + TARGET(CALL) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR); - PREDICTED(BINARY_SUBSCR); - _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); - PyObject *sub; - PyObject *container; + next_instr += 4; + INSTRUCTION_STATS(CALL); + PREDICTED(CALL); + _Py_CODEUNIT *this_instr = next_instr - 4; + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; PyObject *res; - // _SPECIALIZE_BINARY_SUBSCR - sub = stack_pointer[-1]; - container = stack_pointer[-2]; + // _SPECIALIZE_CALL + args = stack_pointer - oparg; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; { uint16_t counter = read_u16(&this_instr[1].cache); TIER_ONE_ONLY #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { next_instr = this_instr; - _Py_Specialize_BinarySubscr(container, sub, next_instr); + _Py_Specialize_Call(callable, next_instr, oparg + (self_or_null != NULL)); DISPATCH_SAME_OPARG(); } - STAT_INC(BINARY_SUBSCR, deferred); + STAT_INC(CALL, deferred); DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); #endif /* ENABLE_SPECIALIZATION */ } - // _BINARY_SUBSCR + // _CALL { - res = PyObject_GetItem(container, sub); - Py_DECREF(container); - Py_DECREF(sub); - if (res == NULL) goto pop_2_error; + // oparg counts all of the args, but *not* self: + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + else if (Py_TYPE(callable) == &PyMethod_Type) { + args--; + total_args++; + PyObject *self = ((PyMethodObject *)callable)->im_self; + args[0] = Py_NewRef(self); + PyObject *method = ((PyMethodObject *)callable)->im_func; + args[-1] = Py_NewRef(method); + Py_DECREF(callable); + callable = method; + } + // Check if the call can be inlined or not + if (Py_TYPE(callable) == &PyFunction_Type && + tstate->interp->eval_frame == NULL && + ((PyFunctionObject *)callable)->vectorcall == _PyFunction_Vectorcall) + { + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable)); + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( + tstate, (PyFunctionObject *)callable, locals, + args, total_args, NULL + ); + // Manipulate stack directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 2); + // The frame has stolen all the arguments from the stack, + // so there is no need to clean them up. + if (new_frame == NULL) { + GOTO_ERROR(error); + } + frame->return_offset = (uint16_t)(next_instr - this_instr); + DISPATCH_INLINED(new_frame); + } + /* Callable is not a normal Python function */ + res = PyObject_Vectorcall( + callable, args, + total_args | PY_VECTORCALL_ARGUMENTS_OFFSET, + NULL); + if (opcode == INSTRUMENTED_CALL) { + PyObject *arg = total_args == 0 ? + &_PyInstrumentation_MISSING : args[0]; + if (res == NULL) { + _Py_call_instrumentation_exc2( + tstate, PY_MONITORING_EVENT_C_RAISE, + frame, this_instr, callable, arg); + } + else { + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_C_RETURN, + frame, this_instr, callable, arg); + if (err < 0) { + Py_CLEAR(res); + } + } + } + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + Py_DECREF(callable); + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); + } + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } } + STACK_SHRINK(oparg); STACK_SHRINK(1); stack_pointer[-1] = res; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(BINARY_SLICE) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(BINARY_SLICE); - PyObject *stop; - PyObject *start; - PyObject *container; - PyObject *res; - stop = stack_pointer[-1]; - start = stack_pointer[-2]; - container = stack_pointer[-3]; - PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop); - // Can't use ERROR_IF() here, because we haven't - // DECREF'ed container yet, and we still own slice. - if (slice == NULL) { - res = NULL; + TARGET(CALL_ALLOC_AND_ENTER_INIT) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_ALLOC_AND_ENTER_INIT); + PyObject **args; + PyObject *null; + PyObject *callable; + args = stack_pointer - oparg; + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + /* This instruction does the following: + * 1. Creates the object (by calling ``object.__new__``) + * 2. Pushes a shim frame to the frame stack (to cleanup after ``__init__``) + * 3. Pushes the frame for ``__init__`` to the frame stack + * */ + _PyCallCache *cache = (_PyCallCache *)&this_instr[1]; + DEOPT_IF(null != NULL, CALL); + DEOPT_IF(!PyType_Check(callable), CALL); + PyTypeObject *tp = (PyTypeObject *)callable; + DEOPT_IF(tp->tp_version_tag != read_u32(cache->func_version), CALL); + PyHeapTypeObject *cls = (PyHeapTypeObject *)callable; + PyFunctionObject *init = (PyFunctionObject *)cls->_spec_cache.init; + PyCodeObject *code = (PyCodeObject *)init->func_code; + DEOPT_IF(code->co_argcount != oparg+1, CALL); + DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize + _Py_InitCleanup.co_framesize), CALL); + STAT_INC(CALL, hit); + PyObject *self = _PyType_NewManagedObject(tp); + if (self == NULL) { + GOTO_ERROR(error); } - else { - res = PyObject_GetItem(container, slice); - Py_DECREF(slice); + Py_DECREF(tp); + _PyInterpreterFrame *shim = _PyFrame_PushTrampolineUnchecked( + tstate, (PyCodeObject *)&_Py_InitCleanup, 1); + assert(_PyCode_CODE((PyCodeObject *)shim->f_executable)[0].op.code == EXIT_INIT_CHECK); + /* Push self onto stack of shim */ + Py_INCREF(self); + shim->localsplus[0] = self; + Py_INCREF(init); + _PyInterpreterFrame *init_frame = _PyFrame_PushUnchecked(tstate, init, oparg+1); + /* Copy self followed by args to __init__ frame */ + init_frame->localsplus[0] = self; + for (int i = 0; i < oparg; i++) { + init_frame->localsplus[i+1] = args[i]; } - Py_DECREF(container); - if (res == NULL) goto pop_3_error; - STACK_SHRINK(2); - stack_pointer[-1] = res; - DISPATCH(); + frame->return_offset = (uint16_t)(next_instr - this_instr); + STACK_SHRINK(oparg+2); + _PyFrame_SetStackPointer(frame, stack_pointer); + /* Link frames */ + init_frame->previous = shim; + shim->previous = frame; + frame = tstate->current_frame = init_frame; + CALL_STAT_INC(inlined_py_calls); + /* Account for pushing the extra frame. + * We don't check recursion depth here, + * as it will be checked after start_frame */ + tstate->py_recursion_remaining--; + goto start_frame; } - TARGET(STORE_SLICE) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(STORE_SLICE); - PyObject *stop; - PyObject *start; - PyObject *container; - PyObject *v; - stop = stack_pointer[-1]; - start = stack_pointer[-2]; - container = stack_pointer[-3]; - v = stack_pointer[-4]; - PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop); - int err; - if (slice == NULL) { - err = 1; + TARGET(CALL_BOUND_METHOD_EXACT_ARGS) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_BOUND_METHOD_EXACT_ARGS); + PyObject *null; + PyObject *callable; + PyObject *self; + PyObject *self_or_null; + PyObject *func; + PyObject **args; + _PyInterpreterFrame *new_frame; + // _CHECK_PEP_523 + { + DEOPT_IF(tstate->interp->eval_frame, CALL); } - else { - err = PyObject_SetItem(container, slice, v); - Py_DECREF(slice); + // _CHECK_CALL_BOUND_METHOD_EXACT_ARGS + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + { + DEOPT_IF(null != NULL, CALL); + DEOPT_IF(Py_TYPE(callable) != &PyMethod_Type, CALL); + } + // _INIT_CALL_BOUND_METHOD_EXACT_ARGS + { + STAT_INC(CALL, hit); + self = Py_NewRef(((PyMethodObject *)callable)->im_self); + stack_pointer[-1 - oparg] = self; // Patch stack as it is used by _INIT_CALL_PY_EXACT_ARGS + func = Py_NewRef(((PyMethodObject *)callable)->im_func); + stack_pointer[-2 - oparg] = func; // This is used by CALL, upon deoptimization + Py_DECREF(callable); + } + // _CHECK_FUNCTION_EXACT_ARGS + self_or_null = self; + callable = func; + { + uint32_t func_version = read_u32(&this_instr[2].cache); + DEOPT_IF(!PyFunction_Check(callable), CALL); + PyFunctionObject *func = (PyFunctionObject *)callable; + DEOPT_IF(func->func_version != func_version, CALL); + PyCodeObject *code = (PyCodeObject *)func->func_code; + DEOPT_IF(code->co_argcount != oparg + (self_or_null != NULL), CALL); + } + // _CHECK_STACK_SPACE + { + PyFunctionObject *func = (PyFunctionObject *)callable; + PyCodeObject *code = (PyCodeObject *)func->func_code; + DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); + DEOPT_IF(tstate->py_recursion_remaining <= 1, CALL); + } + // _INIT_CALL_PY_EXACT_ARGS + args = stack_pointer - oparg; + { + int argcount = oparg; + if (self_or_null != NULL) { + args--; + argcount++; + } + STAT_INC(CALL, hit); + PyFunctionObject *func = (PyFunctionObject *)callable; + new_frame = _PyFrame_PushUnchecked(tstate, func, argcount); + for (int i = 0; i < argcount; i++) { + new_frame->localsplus[i] = args[i]; + } + } + // _SAVE_RETURN_OFFSET + { + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif + } + // _PUSH_FRAME + STACK_SHRINK(oparg); + STACK_SHRINK(2); + { + // Write it out explicitly because it's subtly different. + // Eventually this should be the only occurrence of this code. + assert(tstate->interp->eval_frame == NULL); + STORE_SP(); + new_frame->previous = frame; + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = new_frame; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + #if LLTRACE && TIER_ONE + lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); + if (lltrace < 0) { + goto exit_unwind; + } + #endif } - Py_DECREF(v); - Py_DECREF(container); - if (err) goto pop_4_error; - STACK_SHRINK(4); DISPATCH(); } - TARGET(BINARY_SUBSCR_LIST_INT) { + TARGET(CALL_BUILTIN_CLASS) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR_LIST_INT); - PyObject *sub; - PyObject *list; + next_instr += 4; + INSTRUCTION_STATS(CALL_BUILTIN_CLASS); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; PyObject *res; - sub = stack_pointer[-1]; - list = stack_pointer[-2]; - DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); - DEOPT_IF(!PyList_CheckExact(list), BINARY_SUBSCR); - - // Deopt unless 0 <= sub < PyList_Size(list) - DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR); - Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; - DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR); - STAT_INC(BINARY_SUBSCR, hit); - res = PyList_GET_ITEM(list, index); - assert(res != NULL); - Py_INCREF(res); - _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); - Py_DECREF(list); + args = stack_pointer - oparg; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + DEOPT_IF(!PyType_Check(callable), CALL); + PyTypeObject *tp = (PyTypeObject *)callable; + DEOPT_IF(tp->tp_vectorcall == NULL, CALL); + STAT_INC(CALL, hit); + res = tp->tp_vectorcall((PyObject *)tp, args, total_args, NULL); + /* Free the arguments. */ + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); + } + Py_DECREF(tp); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); STACK_SHRINK(1); stack_pointer[-1] = res; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(BINARY_SUBSCR_STR_INT) { + TARGET(CALL_BUILTIN_FAST) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR_STR_INT); - PyObject *sub; - PyObject *str; + next_instr += 4; + INSTRUCTION_STATS(CALL_BUILTIN_FAST); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; PyObject *res; - sub = stack_pointer[-1]; - str = stack_pointer[-2]; - DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); - DEOPT_IF(!PyUnicode_CheckExact(str), BINARY_SUBSCR); - DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR); - Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; - DEOPT_IF(PyUnicode_GET_LENGTH(str) <= index, BINARY_SUBSCR); - // Specialize for reading an ASCII character from any string: - Py_UCS4 c = PyUnicode_READ_CHAR(str, index); - DEOPT_IF(Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c, BINARY_SUBSCR); - STAT_INC(BINARY_SUBSCR, hit); - res = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; - _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); - Py_DECREF(str); + args = stack_pointer - oparg; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + /* Builtin METH_FASTCALL functions, without keywords */ + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); + DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_FASTCALL, CALL); + STAT_INC(CALL, hit); + PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); + /* res = func(self, args, nargs) */ + res = ((_PyCFunctionFast)(void(*)(void))cfunc)( + PyCFunction_GET_SELF(callable), + args, + total_args); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + + /* Free the arguments. */ + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); + } + Py_DECREF(callable); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + /* Not deopting because this doesn't mean our optimization was + wrong. `res` can be NULL for valid reasons. Eg. getattr(x, + 'invalid'). In those cases an exception is set, so we must + handle it. + */ + STACK_SHRINK(oparg); STACK_SHRINK(1); stack_pointer[-1] = res; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(BINARY_SUBSCR_TUPLE_INT) { + TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR_TUPLE_INT); - PyObject *sub; - PyObject *tuple; + next_instr += 4; + INSTRUCTION_STATS(CALL_BUILTIN_FAST_WITH_KEYWORDS); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; PyObject *res; - sub = stack_pointer[-1]; - tuple = stack_pointer[-2]; - DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); - DEOPT_IF(!PyTuple_CheckExact(tuple), BINARY_SUBSCR); + args = stack_pointer - oparg; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); + DEOPT_IF(PyCFunction_GET_FLAGS(callable) != (METH_FASTCALL | METH_KEYWORDS), CALL); + STAT_INC(CALL, hit); + /* res = func(self, args, nargs, kwnames) */ + _PyCFunctionFastWithKeywords cfunc = + (_PyCFunctionFastWithKeywords)(void(*)(void)) + PyCFunction_GET_FUNCTION(callable); + res = cfunc(PyCFunction_GET_SELF(callable), args, total_args, NULL); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - // Deopt unless 0 <= sub < PyTuple_Size(list) - DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR); - Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; - DEOPT_IF(index >= PyTuple_GET_SIZE(tuple), BINARY_SUBSCR); - STAT_INC(BINARY_SUBSCR, hit); - res = PyTuple_GET_ITEM(tuple, index); - assert(res != NULL); - Py_INCREF(res); - _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); - Py_DECREF(tuple); + /* Free the arguments. */ + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); + } + Py_DECREF(callable); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); STACK_SHRINK(1); stack_pointer[-1] = res; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(BINARY_SUBSCR_DICT) { + TARGET(CALL_BUILTIN_O) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR_DICT); - PyObject *sub; - PyObject *dict; + next_instr += 4; + INSTRUCTION_STATS(CALL_BUILTIN_O); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; PyObject *res; - sub = stack_pointer[-1]; - dict = stack_pointer[-2]; - DEOPT_IF(!PyDict_CheckExact(dict), BINARY_SUBSCR); - STAT_INC(BINARY_SUBSCR, hit); - int rc = PyDict_GetItemRef(dict, sub, &res); - if (rc == 0) { - _PyErr_SetKeyError(sub); + args = stack_pointer - oparg; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + /* Builtin METH_O functions */ + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; } - Py_DECREF(dict); - Py_DECREF(sub); - if (rc <= 0) goto pop_2_error; + DEOPT_IF(total_args != 1, CALL); + DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); + DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_O, CALL); + STAT_INC(CALL, hit); + PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); + // This is slower but CPython promises to check all non-vectorcall + // function calls. + if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { + GOTO_ERROR(error); + } + PyObject *arg = args[0]; + res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg); + _Py_LeaveRecursiveCallTstate(tstate); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + + Py_DECREF(arg); + Py_DECREF(callable); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); STACK_SHRINK(1); stack_pointer[-1] = res; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(BINARY_SUBSCR_GETITEM) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR_GETITEM); - PyObject *sub; - PyObject *container; - sub = stack_pointer[-1]; - container = stack_pointer[-2]; - DEOPT_IF(tstate->interp->eval_frame, BINARY_SUBSCR); - PyTypeObject *tp = Py_TYPE(container); - DEOPT_IF(!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE), BINARY_SUBSCR); - PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; - PyObject *cached = ht->_spec_cache.getitem; - DEOPT_IF(cached == NULL, BINARY_SUBSCR); - assert(PyFunction_Check(cached)); - PyFunctionObject *getitem = (PyFunctionObject *)cached; - uint32_t cached_version = ht->_spec_cache.getitem_version; - DEOPT_IF(getitem->func_version != cached_version, BINARY_SUBSCR); - PyCodeObject *code = (PyCodeObject *)getitem->func_code; - assert(code->co_argcount == 2); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), BINARY_SUBSCR); - STAT_INC(BINARY_SUBSCR, hit); - Py_INCREF(getitem); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, getitem, 2); + TARGET(CALL_FUNCTION_EX) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(CALL_FUNCTION_EX); + PREDICTED(CALL_FUNCTION_EX); + _Py_CODEUNIT *this_instr = next_instr - 1; + PyObject *kwargs = NULL; + PyObject *callargs; + PyObject *func; + PyObject *result; + if (oparg & 1) { kwargs = stack_pointer[-(oparg & 1 ? 1 : 0)]; } + callargs = stack_pointer[-1 - (oparg & 1 ? 1 : 0)]; + func = stack_pointer[-3 - (oparg & 1 ? 1 : 0)]; + // DICT_MERGE is called before this opcode if there are kwargs. + // It converts all dict subtypes in kwargs into regular dicts. + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + if (!PyTuple_CheckExact(callargs)) { + if (check_args_iterable(tstate, func, callargs) < 0) { + GOTO_ERROR(error); + } + PyObject *tuple = PySequence_Tuple(callargs); + if (tuple == NULL) { + GOTO_ERROR(error); + } + Py_SETREF(callargs, tuple); + } + assert(PyTuple_CheckExact(callargs)); + EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_FUNCTION_EX, func); + if (opcode == INSTRUMENTED_CALL_FUNCTION_EX && + !PyFunction_Check(func) && !PyMethod_Check(func) + ) { + PyObject *arg = PyTuple_GET_SIZE(callargs) > 0 ? + PyTuple_GET_ITEM(callargs, 0) : Py_None; + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, func, arg); + if (err) GOTO_ERROR(error); + result = PyObject_Call(func, callargs, kwargs); + if (result == NULL) { + _Py_call_instrumentation_exc2( + tstate, PY_MONITORING_EVENT_C_RAISE, + frame, this_instr, func, arg); + } + else { + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_C_RETURN, + frame, this_instr, func, arg); + if (err < 0) { + Py_CLEAR(result); + } + } + } + else { + if (Py_TYPE(func) == &PyFunction_Type && + tstate->interp->eval_frame == NULL && + ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { + assert(PyTuple_CheckExact(callargs)); + Py_ssize_t nargs = PyTuple_GET_SIZE(callargs); + int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func)); + + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex(tstate, + (PyFunctionObject *)func, locals, + nargs, callargs, kwargs); + // Need to manually shrink the stack since we exit with DISPATCH_INLINED. + STACK_SHRINK(oparg + 3); + if (new_frame == NULL) { + GOTO_ERROR(error); + } + assert(next_instr - this_instr == 1); + frame->return_offset = 1; + DISPATCH_INLINED(new_frame); + } + result = PyObject_Call(func, callargs, kwargs); + } + Py_DECREF(func); + Py_DECREF(callargs); + Py_XDECREF(kwargs); + assert(PEEK(2 + (oparg & 1)) == NULL); + if (result == NULL) { STACK_SHRINK(((oparg & 1) ? 1 : 0)); goto pop_3_error; } + STACK_SHRINK(((oparg & 1) ? 1 : 0)); STACK_SHRINK(2); - new_frame->localsplus[0] = container; - new_frame->localsplus[1] = sub; - frame->return_offset = (uint16_t)(next_instr - this_instr); - DISPATCH_INLINED(new_frame); + stack_pointer[-1] = result; + CHECK_EVAL_BREAKER(); + DISPATCH(); } - TARGET(LIST_APPEND) { + TARGET(CALL_INTRINSIC_1) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(LIST_APPEND); - PyObject *v; - PyObject *list; - v = stack_pointer[-1]; - list = stack_pointer[-2 - (oparg-1)]; - if (_PyList_AppendTakeRef((PyListObject *)list, v) < 0) goto pop_1_error; - STACK_SHRINK(1); + INSTRUCTION_STATS(CALL_INTRINSIC_1); + PyObject *value; + PyObject *res; + value = stack_pointer[-1]; + assert(oparg <= MAX_INTRINSIC_1); + res = _PyIntrinsics_UnaryFunctions[oparg].func(tstate, value); + Py_DECREF(value); + if (res == NULL) goto pop_1_error; + stack_pointer[-1] = res; DISPATCH(); } - TARGET(SET_ADD) { + TARGET(CALL_INTRINSIC_2) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(SET_ADD); - PyObject *v; - PyObject *set; - v = stack_pointer[-1]; - set = stack_pointer[-2 - (oparg-1)]; - int err = PySet_Add(set, v); - Py_DECREF(v); - if (err) goto pop_1_error; + INSTRUCTION_STATS(CALL_INTRINSIC_2); + PyObject *value1; + PyObject *value2; + PyObject *res; + value1 = stack_pointer[-1]; + value2 = stack_pointer[-2]; + assert(oparg <= MAX_INTRINSIC_2); + res = _PyIntrinsics_BinaryFunctions[oparg].func(tstate, value2, value1); + Py_DECREF(value2); + Py_DECREF(value1); + if (res == NULL) goto pop_2_error; STACK_SHRINK(1); + stack_pointer[-1] = res; DISPATCH(); } - TARGET(STORE_SUBSCR) { + TARGET(CALL_ISINSTANCE) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(STORE_SUBSCR); - PREDICTED(STORE_SUBSCR); - _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_STORE_SUBSCR == 1, "incorrect cache size"); - PyObject *sub; - PyObject *container; - PyObject *v; - // _SPECIALIZE_STORE_SUBSCR - sub = stack_pointer[-1]; - container = stack_pointer[-2]; - { - uint16_t counter = read_u16(&this_instr[1].cache); - TIER_ONE_ONLY - #if ENABLE_SPECIALIZATION - if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - next_instr = this_instr; - _Py_Specialize_StoreSubscr(container, sub, next_instr); - DISPATCH_SAME_OPARG(); - } - STAT_INC(STORE_SUBSCR, deferred); - DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - #endif /* ENABLE_SPECIALIZATION */ + next_instr += 4; + INSTRUCTION_STATS(CALL_ISINSTANCE); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; + PyObject *res; + args = stack_pointer - oparg; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + /* isinstance(o, o2) */ + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; } - // _STORE_SUBSCR - v = stack_pointer[-3]; - { - /* container[sub] = v */ - int err = PyObject_SetItem(container, sub, v); - Py_DECREF(v); - Py_DECREF(container); - Py_DECREF(sub); - if (err) goto pop_3_error; + DEOPT_IF(total_args != 2, CALL); + PyInterpreterState *interp = tstate->interp; + DEOPT_IF(callable != interp->callable_cache.isinstance, CALL); + STAT_INC(CALL, hit); + PyObject *cls = args[1]; + PyObject *inst = args[0]; + int retval = PyObject_IsInstance(inst, cls); + if (retval < 0) { + GOTO_ERROR(error); } - STACK_SHRINK(3); - DISPATCH(); - } - - TARGET(STORE_SUBSCR_LIST_INT) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(STORE_SUBSCR_LIST_INT); - PyObject *sub; - PyObject *list; - PyObject *value; - sub = stack_pointer[-1]; - list = stack_pointer[-2]; - value = stack_pointer[-3]; - DEOPT_IF(!PyLong_CheckExact(sub), STORE_SUBSCR); - DEOPT_IF(!PyList_CheckExact(list), STORE_SUBSCR); - - // Ensure nonnegative, zero-or-one-digit ints. - DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), STORE_SUBSCR); - Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; - // Ensure index < len(list) - DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR); - STAT_INC(STORE_SUBSCR, hit); - - PyObject *old_value = PyList_GET_ITEM(list, index); - PyList_SET_ITEM(list, index, value); - assert(old_value != NULL); - Py_DECREF(old_value); - _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); - Py_DECREF(list); - STACK_SHRINK(3); - DISPATCH(); - } + res = PyBool_FromLong(retval); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - TARGET(STORE_SUBSCR_DICT) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(STORE_SUBSCR_DICT); - PyObject *sub; - PyObject *dict; - PyObject *value; - sub = stack_pointer[-1]; - dict = stack_pointer[-2]; - value = stack_pointer[-3]; - DEOPT_IF(!PyDict_CheckExact(dict), STORE_SUBSCR); - STAT_INC(STORE_SUBSCR, hit); - int err = _PyDict_SetItem_Take2((PyDictObject *)dict, sub, value); - Py_DECREF(dict); - if (err) goto pop_3_error; - STACK_SHRINK(3); + Py_DECREF(inst); + Py_DECREF(cls); + Py_DECREF(callable); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; DISPATCH(); } - TARGET(DELETE_SUBSCR) { + TARGET(CALL_KW) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(DELETE_SUBSCR); - PyObject *sub; - PyObject *container; - sub = stack_pointer[-1]; - container = stack_pointer[-2]; - /* del container[sub] */ - int err = PyObject_DelItem(container, sub); - Py_DECREF(container); - Py_DECREF(sub); - if (err) goto pop_2_error; + INSTRUCTION_STATS(CALL_KW); + PREDICTED(CALL_KW); + _Py_CODEUNIT *this_instr = next_instr - 1; + PyObject *kwnames; + PyObject **args; + PyObject *self_or_null; + PyObject *callable; + PyObject *res; + kwnames = stack_pointer[-1]; + args = stack_pointer - 1 - oparg; + self_or_null = stack_pointer[-2 - oparg]; + callable = stack_pointer[-3 - oparg]; + // oparg counts all of the args, but *not* self: + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + if (self_or_null == NULL && Py_TYPE(callable) == &PyMethod_Type) { + args--; + total_args++; + PyObject *self = ((PyMethodObject *)callable)->im_self; + args[0] = Py_NewRef(self); + PyObject *method = ((PyMethodObject *)callable)->im_func; + args[-1] = Py_NewRef(method); + Py_DECREF(callable); + callable = method; + } + int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames); + // Check if the call can be inlined or not + if (Py_TYPE(callable) == &PyFunction_Type && + tstate->interp->eval_frame == NULL && + ((PyFunctionObject *)callable)->vectorcall == _PyFunction_Vectorcall) + { + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable)); + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( + tstate, (PyFunctionObject *)callable, locals, + args, positional_args, kwnames + ); + Py_DECREF(kwnames); + // Manipulate stack directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 3); + // The frame has stolen all the arguments from the stack, + // so there is no need to clean them up. + if (new_frame == NULL) { + GOTO_ERROR(error); + } + assert(next_instr - this_instr == 1); + frame->return_offset = 1; + DISPATCH_INLINED(new_frame); + } + /* Callable is not a normal Python function */ + res = PyObject_Vectorcall( + callable, args, + positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, + kwnames); + if (opcode == INSTRUMENTED_CALL_KW) { + PyObject *arg = total_args == 0 ? + &_PyInstrumentation_MISSING : args[0]; + if (res == NULL) { + _Py_call_instrumentation_exc2( + tstate, PY_MONITORING_EVENT_C_RAISE, + frame, this_instr, callable, arg); + } + else { + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_C_RETURN, + frame, this_instr, callable, arg); + if (err < 0) { + Py_CLEAR(res); + } + } + } + Py_DECREF(kwnames); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + Py_DECREF(callable); + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); + } + if (res == NULL) { STACK_SHRINK(oparg); goto pop_3_error; } + STACK_SHRINK(oparg); STACK_SHRINK(2); + stack_pointer[-1] = res; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(CALL_INTRINSIC_1) { + TARGET(CALL_LEN) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(CALL_INTRINSIC_1); - PyObject *value; + next_instr += 4; + INSTRUCTION_STATS(CALL_LEN); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; PyObject *res; - value = stack_pointer[-1]; - assert(oparg <= MAX_INTRINSIC_1); - res = _PyIntrinsics_UnaryFunctions[oparg].func(tstate, value); - Py_DECREF(value); - if (res == NULL) goto pop_1_error; + args = stack_pointer - oparg; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + /* len(o) */ + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + DEOPT_IF(total_args != 1, CALL); + PyInterpreterState *interp = tstate->interp; + DEOPT_IF(callable != interp->callable_cache.len, CALL); + STAT_INC(CALL, hit); + PyObject *arg = args[0]; + Py_ssize_t len_i = PyObject_Length(arg); + if (len_i < 0) { + GOTO_ERROR(error); + } + res = PyLong_FromSsize_t(len_i); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + + Py_DECREF(callable); + Py_DECREF(arg); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); stack_pointer[-1] = res; DISPATCH(); } - TARGET(CALL_INTRINSIC_2) { + TARGET(CALL_LIST_APPEND) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(CALL_INTRINSIC_2); - PyObject *value1; - PyObject *value2; - PyObject *res; - value1 = stack_pointer[-1]; - value2 = stack_pointer[-2]; - assert(oparg <= MAX_INTRINSIC_2); - res = _PyIntrinsics_BinaryFunctions[oparg].func(tstate, value2, value1); - Py_DECREF(value2); - Py_DECREF(value1); - if (res == NULL) goto pop_2_error; - STACK_SHRINK(1); - stack_pointer[-1] = res; + next_instr += 4; + INSTRUCTION_STATS(CALL_LIST_APPEND); + PyObject **args; + PyObject *self; + PyObject *callable; + args = stack_pointer - oparg; + self = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + assert(oparg == 1); + PyInterpreterState *interp = tstate->interp; + DEOPT_IF(callable != interp->callable_cache.list_append, CALL); + assert(self != NULL); + DEOPT_IF(!PyList_Check(self), CALL); + STAT_INC(CALL, hit); + if (_PyList_AppendTakeRef((PyListObject *)self, args[0]) < 0) { + goto pop_1_error; // Since arg is DECREF'ed already + } + Py_DECREF(self); + Py_DECREF(callable); + STACK_SHRINK(3); + // Skip POP_TOP + assert(next_instr->op.code == POP_TOP); + SKIP_OVER(1); DISPATCH(); } - TARGET(RAISE_VARARGS) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(RAISE_VARARGS); + TARGET(CALL_METHOD_DESCRIPTOR_FAST) { + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_FAST); PyObject **args; + PyObject *self_or_null; + PyObject *callable; + PyObject *res; args = stack_pointer - oparg; - TIER_ONE_ONLY - PyObject *cause = NULL, *exc = NULL; - switch (oparg) { - case 2: - cause = args[1]; - /* fall through */ - case 1: - exc = args[0]; - /* fall through */ - case 0: - if (do_raise(tstate, exc, cause)) { - assert(oparg == 0); - monitor_reraise(tstate, frame, this_instr); - goto exception_unwind; - } - break; - default: - _PyErr_SetString(tstate, PyExc_SystemError, - "bad RAISE_VARARGS oparg"); - break; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; } - if (true) { STACK_SHRINK(oparg); goto error; } + PyMethodDescrObject *method = (PyMethodDescrObject *)callable; + /* Builtin METH_FASTCALL methods, without keywords */ + DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); + PyMethodDef *meth = method->d_method; + DEOPT_IF(meth->ml_flags != METH_FASTCALL, CALL); + PyObject *self = args[0]; + DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); + STAT_INC(CALL, hit); + _PyCFunctionFast cfunc = + (_PyCFunctionFast)(void(*)(void))meth->ml_meth; + int nargs = total_args - 1; + res = cfunc(self, args + 1, nargs); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + /* Clear the stack of the arguments. */ + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); + } + Py_DECREF(callable); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + CHECK_EVAL_BREAKER(); + DISPATCH(); } - TARGET(INTERPRETER_EXIT) { + TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INTERPRETER_EXIT); - PyObject *retval; - retval = stack_pointer[-1]; - assert(frame == &entry_frame); - assert(_PyFrame_IsIncomplete(frame)); - /* Restore previous frame and return. */ - tstate->current_frame = frame->previous; - assert(!_PyErr_Occurred(tstate)); - tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS; - return retval; - } + next_instr += 4; + INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; + PyObject *res; + args = stack_pointer - oparg; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + PyMethodDescrObject *method = (PyMethodDescrObject *)callable; + DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); + PyMethodDef *meth = method->d_method; + DEOPT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS), CALL); + PyTypeObject *d_type = method->d_common.d_type; + PyObject *self = args[0]; + DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL); + STAT_INC(CALL, hit); + int nargs = total_args - 1; + _PyCFunctionFastWithKeywords cfunc = + (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; + res = cfunc(self, args + 1, nargs, NULL); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - TARGET(RETURN_VALUE) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(RETURN_VALUE); - PyObject *retval; - retval = stack_pointer[-1]; - STACK_SHRINK(1); - assert(EMPTY()); - #if TIER_ONE - assert(frame != &entry_frame); - #endif - STORE_SP(); - _Py_LeaveRecursiveCallPy(tstate); - // GH-99729: We need to unlink the frame *before* clearing it: - _PyInterpreterFrame *dying = frame; - frame = tstate->current_frame = dying->previous; - _PyEval_FrameClearAndPop(tstate, dying); - _PyFrame_StackPush(frame, retval); - LOAD_SP(); - LOAD_IP(frame->return_offset); -#if LLTRACE && TIER_ONE - lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); - if (lltrace < 0) { - goto exit_unwind; + /* Free the arguments. */ + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); } -#endif + Py_DECREF(callable); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(INSTRUMENTED_RETURN_VALUE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_RETURN_VALUE); - PyObject *retval; - retval = stack_pointer[-1]; - int err = _Py_call_instrumentation_arg( - tstate, PY_MONITORING_EVENT_PY_RETURN, - frame, this_instr, retval); - if (err) GOTO_ERROR(error); + TARGET(CALL_METHOD_DESCRIPTOR_NOARGS) { + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_NOARGS); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; + PyObject *res; + args = stack_pointer - oparg; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + assert(oparg == 0 || oparg == 1); + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + DEOPT_IF(total_args != 1, CALL); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable; + DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); + PyMethodDef *meth = method->d_method; + PyObject *self = args[0]; + DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); + DEOPT_IF(meth->ml_flags != METH_NOARGS, CALL); + STAT_INC(CALL, hit); + PyCFunction cfunc = meth->ml_meth; + // This is slower but CPython promises to check all non-vectorcall + // function calls. + if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { + GOTO_ERROR(error); + } + res = _PyCFunction_TrampolineCall(cfunc, self, NULL); + _Py_LeaveRecursiveCallTstate(tstate); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + Py_DECREF(self); + Py_DECREF(callable); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); STACK_SHRINK(1); - assert(EMPTY()); - _PyFrame_SetStackPointer(frame, stack_pointer); - _Py_LeaveRecursiveCallPy(tstate); - assert(frame != &entry_frame); - // GH-99729: We need to unlink the frame *before* clearing it: - _PyInterpreterFrame *dying = frame; - frame = tstate->current_frame = dying->previous; - _PyEval_FrameClearAndPop(tstate, dying); - _PyFrame_StackPush(frame, retval); - LOAD_IP(frame->return_offset); - goto resume_frame; + stack_pointer[-1] = res; + CHECK_EVAL_BREAKER(); + DISPATCH(); } - TARGET(RETURN_CONST) { + TARGET(CALL_METHOD_DESCRIPTOR_O) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(RETURN_CONST); - PyObject *value; - PyObject *retval; - // LOAD_CONST - { - value = GETITEM(FRAME_CO_CONSTS, oparg); - Py_INCREF(value); + next_instr += 4; + INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_O); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; + PyObject *res; + args = stack_pointer - oparg; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; } - // _POP_FRAME - retval = value; - { - assert(EMPTY()); - #if TIER_ONE - assert(frame != &entry_frame); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable; + DEOPT_IF(total_args != 2, CALL); + DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); + PyMethodDef *meth = method->d_method; + DEOPT_IF(meth->ml_flags != METH_O, CALL); + PyObject *arg = args[1]; + PyObject *self = args[0]; + DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); + STAT_INC(CALL, hit); + PyCFunction cfunc = meth->ml_meth; + // This is slower but CPython promises to check all non-vectorcall + // function calls. + if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { + GOTO_ERROR(error); + } + res = _PyCFunction_TrampolineCall(cfunc, self, arg); + _Py_LeaveRecursiveCallTstate(tstate); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + Py_DECREF(self); + Py_DECREF(arg); + Py_DECREF(callable); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + CHECK_EVAL_BREAKER(); + DISPATCH(); + } + + TARGET(CALL_PY_EXACT_ARGS) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_PY_EXACT_ARGS); + PyObject *self_or_null; + PyObject *callable; + PyObject **args; + _PyInterpreterFrame *new_frame; + // _CHECK_PEP_523 + { + DEOPT_IF(tstate->interp->eval_frame, CALL); + } + // _CHECK_FUNCTION_EXACT_ARGS + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + { + uint32_t func_version = read_u32(&this_instr[2].cache); + DEOPT_IF(!PyFunction_Check(callable), CALL); + PyFunctionObject *func = (PyFunctionObject *)callable; + DEOPT_IF(func->func_version != func_version, CALL); + PyCodeObject *code = (PyCodeObject *)func->func_code; + DEOPT_IF(code->co_argcount != oparg + (self_or_null != NULL), CALL); + } + // _CHECK_STACK_SPACE + { + PyFunctionObject *func = (PyFunctionObject *)callable; + PyCodeObject *code = (PyCodeObject *)func->func_code; + DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); + DEOPT_IF(tstate->py_recursion_remaining <= 1, CALL); + } + // _INIT_CALL_PY_EXACT_ARGS + args = stack_pointer - oparg; + { + int argcount = oparg; + if (self_or_null != NULL) { + args--; + argcount++; + } + STAT_INC(CALL, hit); + PyFunctionObject *func = (PyFunctionObject *)callable; + new_frame = _PyFrame_PushUnchecked(tstate, func, argcount); + for (int i = 0; i < argcount; i++) { + new_frame->localsplus[i] = args[i]; + } + } + // _SAVE_RETURN_OFFSET + { + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; #endif + } + // _PUSH_FRAME + STACK_SHRINK(oparg); + STACK_SHRINK(2); + { + // Write it out explicitly because it's subtly different. + // Eventually this should be the only occurrence of this code. + assert(tstate->interp->eval_frame == NULL); STORE_SP(); - _Py_LeaveRecursiveCallPy(tstate); - // GH-99729: We need to unlink the frame *before* clearing it: - _PyInterpreterFrame *dying = frame; - frame = tstate->current_frame = dying->previous; - _PyEval_FrameClearAndPop(tstate, dying); - _PyFrame_StackPush(frame, retval); + new_frame->previous = frame; + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = new_frame; + tstate->py_recursion_remaining--; LOAD_SP(); - LOAD_IP(frame->return_offset); + LOAD_IP(0); #if LLTRACE && TIER_ONE lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); if (lltrace < 0) { @@ -1242,457 +1715,467 @@ DISPATCH(); } - TARGET(INSTRUMENTED_RETURN_CONST) { + TARGET(CALL_PY_WITH_DEFAULTS) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_RETURN_CONST); - PyObject *retval = GETITEM(FRAME_CO_CONSTS, oparg); - int err = _Py_call_instrumentation_arg( - tstate, PY_MONITORING_EVENT_PY_RETURN, - frame, this_instr, retval); - if (err) GOTO_ERROR(error); - Py_INCREF(retval); - assert(EMPTY()); - _PyFrame_SetStackPointer(frame, stack_pointer); - _Py_LeaveRecursiveCallPy(tstate); - assert(frame != &entry_frame); - // GH-99729: We need to unlink the frame *before* clearing it: - _PyInterpreterFrame *dying = frame; - frame = tstate->current_frame = dying->previous; - _PyEval_FrameClearAndPop(tstate, dying); - _PyFrame_StackPush(frame, retval); - LOAD_IP(frame->return_offset); - goto resume_frame; - } - - TARGET(GET_AITER) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(GET_AITER); - PyObject *obj; - PyObject *iter; - obj = stack_pointer[-1]; - unaryfunc getter = NULL; - PyTypeObject *type = Py_TYPE(obj); - - if (type->tp_as_async != NULL) { - getter = type->tp_as_async->am_aiter; + next_instr += 4; + INSTRUCTION_STATS(CALL_PY_WITH_DEFAULTS); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; + args = stack_pointer - oparg; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + uint32_t func_version = read_u32(&this_instr[2].cache); + DEOPT_IF(tstate->interp->eval_frame, CALL); + int argcount = oparg; + if (self_or_null != NULL) { + args--; + argcount++; } - - if (getter == NULL) { - _PyErr_Format(tstate, PyExc_TypeError, - "'async for' requires an object with " - "__aiter__ method, got %.100s", - type->tp_name); - Py_DECREF(obj); - if (true) goto pop_1_error; + DEOPT_IF(!PyFunction_Check(callable), CALL); + PyFunctionObject *func = (PyFunctionObject *)callable; + DEOPT_IF(func->func_version != func_version, CALL); + PyCodeObject *code = (PyCodeObject *)func->func_code; + assert(func->func_defaults); + assert(PyTuple_CheckExact(func->func_defaults)); + int defcount = (int)PyTuple_GET_SIZE(func->func_defaults); + assert(defcount <= code->co_argcount); + int min_args = code->co_argcount - defcount; + DEOPT_IF(argcount > code->co_argcount, CALL); + DEOPT_IF(argcount < min_args, CALL); + DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); + STAT_INC(CALL, hit); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, code->co_argcount); + for (int i = 0; i < argcount; i++) { + new_frame->localsplus[i] = args[i]; } - - iter = (*getter)(obj); - Py_DECREF(obj); - if (iter == NULL) goto pop_1_error; - - if (Py_TYPE(iter)->tp_as_async == NULL || - Py_TYPE(iter)->tp_as_async->am_anext == NULL) { - - _PyErr_Format(tstate, PyExc_TypeError, - "'async for' received an object from __aiter__ " - "that does not implement __anext__: %.100s", - Py_TYPE(iter)->tp_name); - Py_DECREF(iter); - if (true) goto pop_1_error; + for (int i = argcount; i < code->co_argcount; i++) { + PyObject *def = PyTuple_GET_ITEM(func->func_defaults, i - min_args); + new_frame->localsplus[i] = Py_NewRef(def); } - stack_pointer[-1] = iter; + // Manipulate stack and cache directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 2); + frame->return_offset = (uint16_t)(next_instr - this_instr); + DISPATCH_INLINED(new_frame); + } + + TARGET(CALL_STR_1) { + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_STR_1); + PyObject **args; + PyObject *null; + PyObject *callable; + PyObject *res; + args = stack_pointer - oparg; + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + assert(oparg == 1); + DEOPT_IF(null != NULL, CALL); + DEOPT_IF(callable != (PyObject *)&PyUnicode_Type, CALL); + STAT_INC(CALL, hit); + PyObject *arg = args[0]; + res = PyObject_Str(arg); + Py_DECREF(arg); + Py_DECREF(&PyUnicode_Type); // I.e., callable + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(GET_ANEXT) { + TARGET(CALL_TUPLE_1) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(GET_ANEXT); - PyObject *aiter; - PyObject *awaitable; - aiter = stack_pointer[-1]; - unaryfunc getter = NULL; - PyObject *next_iter = NULL; - PyTypeObject *type = Py_TYPE(aiter); + next_instr += 4; + INSTRUCTION_STATS(CALL_TUPLE_1); + PyObject **args; + PyObject *null; + PyObject *callable; + PyObject *res; + args = stack_pointer - oparg; + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + assert(oparg == 1); + DEOPT_IF(null != NULL, CALL); + DEOPT_IF(callable != (PyObject *)&PyTuple_Type, CALL); + STAT_INC(CALL, hit); + PyObject *arg = args[0]; + res = PySequence_Tuple(arg); + Py_DECREF(arg); + Py_DECREF(&PyTuple_Type); // I.e., tuple + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + CHECK_EVAL_BREAKER(); + DISPATCH(); + } - if (PyAsyncGen_CheckExact(aiter)) { - awaitable = type->tp_as_async->am_anext(aiter); - if (awaitable == NULL) { - GOTO_ERROR(error); - } - } else { - if (type->tp_as_async != NULL){ - getter = type->tp_as_async->am_anext; - } + TARGET(CALL_TYPE_1) { + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_TYPE_1); + PyObject **args; + PyObject *null; + PyObject *callable; + PyObject *res; + args = stack_pointer - oparg; + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + assert(oparg == 1); + DEOPT_IF(null != NULL, CALL); + PyObject *obj = args[0]; + DEOPT_IF(callable != (PyObject *)&PyType_Type, CALL); + STAT_INC(CALL, hit); + res = Py_NewRef(Py_TYPE(obj)); + Py_DECREF(obj); + Py_DECREF(&PyType_Type); // I.e., callable + STACK_SHRINK(oparg); + STACK_SHRINK(1); + stack_pointer[-1] = res; + DISPATCH(); + } - if (getter != NULL) { - next_iter = (*getter)(aiter); - if (next_iter == NULL) { - GOTO_ERROR(error); - } - } - else { - _PyErr_Format(tstate, PyExc_TypeError, - "'async for' requires an iterator with " - "__anext__ method, got %.100s", - type->tp_name); - GOTO_ERROR(error); - } + TARGET(CHECK_EG_MATCH) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(CHECK_EG_MATCH); + PyObject *match_type; + PyObject *exc_value; + PyObject *rest; + PyObject *match; + match_type = stack_pointer[-1]; + exc_value = stack_pointer[-2]; + if (_PyEval_CheckExceptStarTypeValid(tstate, match_type) < 0) { + Py_DECREF(exc_value); + Py_DECREF(match_type); + if (true) goto pop_2_error; + } - awaitable = _PyCoro_GetAwaitableIter(next_iter); - if (awaitable == NULL) { - _PyErr_FormatFromCause( - PyExc_TypeError, - "'async for' received an invalid object " - "from __anext__: %.100s", - Py_TYPE(next_iter)->tp_name); + match = NULL; + rest = NULL; + int res = _PyEval_ExceptionGroupMatch(exc_value, match_type, + &match, &rest); + Py_DECREF(exc_value); + Py_DECREF(match_type); + if (res < 0) goto pop_2_error; - Py_DECREF(next_iter); - GOTO_ERROR(error); - } else { - Py_DECREF(next_iter); - } + assert((match == NULL) == (rest == NULL)); + if (match == NULL) goto pop_2_error; + + if (!Py_IsNone(match)) { + PyErr_SetHandledException(match); } - STACK_GROW(1); - stack_pointer[-1] = awaitable; + stack_pointer[-2] = rest; + stack_pointer[-1] = match; DISPATCH(); } - TARGET(GET_AWAITABLE) { + TARGET(CHECK_EXC_MATCH) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(GET_AWAITABLE); - PyObject *iterable; - PyObject *iter; - iterable = stack_pointer[-1]; - iter = _PyCoro_GetAwaitableIter(iterable); - - if (iter == NULL) { - _PyEval_FormatAwaitableError(tstate, Py_TYPE(iterable), oparg); + INSTRUCTION_STATS(CHECK_EXC_MATCH); + PyObject *right; + PyObject *left; + PyObject *b; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + assert(PyExceptionInstance_Check(left)); + if (_PyEval_CheckExceptTypeValid(tstate, right) < 0) { + Py_DECREF(right); + if (true) goto pop_1_error; } - Py_DECREF(iterable); + int res = PyErr_GivenExceptionMatches(left, right); + Py_DECREF(right); + b = res ? Py_True : Py_False; + stack_pointer[-1] = b; + DISPATCH(); + } - if (iter != NULL && PyCoro_CheckExact(iter)) { - PyObject *yf = _PyGen_yf((PyGenObject*)iter); - if (yf != NULL) { - /* `iter` is a coroutine object that is being - awaited, `yf` is a pointer to the current awaitable - being awaited on. */ - Py_DECREF(yf); - Py_CLEAR(iter); - _PyErr_SetString(tstate, PyExc_RuntimeError, - "coroutine is being awaited already"); - /* The code below jumps to `error` if `iter` is NULL. */ - } + TARGET(CLEANUP_THROW) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(CLEANUP_THROW); + PyObject *exc_value; + PyObject *last_sent_val; + PyObject *sub_iter; + PyObject *none; + PyObject *value; + exc_value = stack_pointer[-1]; + last_sent_val = stack_pointer[-2]; + sub_iter = stack_pointer[-3]; + TIER_ONE_ONLY + assert(throwflag); + assert(exc_value && PyExceptionInstance_Check(exc_value)); + if (PyErr_GivenExceptionMatches(exc_value, PyExc_StopIteration)) { + value = Py_NewRef(((PyStopIterationObject *)exc_value)->value); + Py_DECREF(sub_iter); + Py_DECREF(last_sent_val); + Py_DECREF(exc_value); + none = Py_None; } - - if (iter == NULL) goto pop_1_error; - stack_pointer[-1] = iter; + else { + _PyErr_SetRaisedException(tstate, Py_NewRef(exc_value)); + monitor_reraise(tstate, frame, this_instr); + goto exception_unwind; + } + STACK_SHRINK(1); + stack_pointer[-2] = none; + stack_pointer[-1] = value; DISPATCH(); } - TARGET(SEND) { + TARGET(COMPARE_OP) { frame->instr_ptr = next_instr; next_instr += 2; - INSTRUCTION_STATS(SEND); - PREDICTED(SEND); + INSTRUCTION_STATS(COMPARE_OP); + PREDICTED(COMPARE_OP); _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_SEND == 1, "incorrect cache size"); - PyObject *receiver; - PyObject *v; - PyObject *retval; - // _SPECIALIZE_SEND - receiver = stack_pointer[-2]; + static_assert(INLINE_CACHE_ENTRIES_COMPARE_OP == 1, "incorrect cache size"); + PyObject *right; + PyObject *left; + PyObject *res; + // _SPECIALIZE_COMPARE_OP + right = stack_pointer[-1]; + left = stack_pointer[-2]; { uint16_t counter = read_u16(&this_instr[1].cache); TIER_ONE_ONLY #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { next_instr = this_instr; - _Py_Specialize_Send(receiver, next_instr); + _Py_Specialize_CompareOp(left, right, next_instr, oparg); DISPATCH_SAME_OPARG(); } - STAT_INC(SEND, deferred); + STAT_INC(COMPARE_OP, deferred); DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); #endif /* ENABLE_SPECIALIZATION */ } - // _SEND - v = stack_pointer[-1]; + // _COMPARE_OP { - assert(frame != &entry_frame); - if ((tstate->interp->eval_frame == NULL) && - (Py_TYPE(receiver) == &PyGen_Type || Py_TYPE(receiver) == &PyCoro_Type) && - ((PyGenObject *)receiver)->gi_frame_state < FRAME_EXECUTING) - { - PyGenObject *gen = (PyGenObject *)receiver; - _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; - STACK_SHRINK(1); - _PyFrame_StackPush(gen_frame, v); - gen->gi_frame_state = FRAME_EXECUTING; - gen->gi_exc_state.previous_item = tstate->exc_info; - tstate->exc_info = &gen->gi_exc_state; - assert(next_instr - this_instr + oparg <= UINT16_MAX); - frame->return_offset = (uint16_t)(next_instr - this_instr + oparg); - DISPATCH_INLINED(gen_frame); - } - if (Py_IsNone(v) && PyIter_Check(receiver)) { - retval = Py_TYPE(receiver)->tp_iternext(receiver); - } - else { - retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v); - } - if (retval == NULL) { - if (_PyErr_ExceptionMatches(tstate, PyExc_StopIteration) - ) { - monitor_raise(tstate, frame, this_instr); - } - if (_PyGen_FetchStopIterationValue(&retval) == 0) { - assert(retval != NULL); - JUMPBY(oparg); - } - else { - GOTO_ERROR(error); - } + assert((oparg >> 5) <= Py_GE); + res = PyObject_RichCompare(left, right, oparg >> 5); + Py_DECREF(left); + Py_DECREF(right); + if (res == NULL) goto pop_2_error; + if (oparg & 16) { + int res_bool = PyObject_IsTrue(res); + Py_DECREF(res); + if (res_bool < 0) goto pop_2_error; + res = res_bool ? Py_True : Py_False; } - Py_DECREF(v); } - stack_pointer[-1] = retval; + STACK_SHRINK(1); + stack_pointer[-1] = res; DISPATCH(); } - TARGET(SEND_GEN) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + TARGET(COMPARE_OP_FLOAT) { + frame->instr_ptr = next_instr; next_instr += 2; - INSTRUCTION_STATS(SEND_GEN); - PyObject *v; - PyObject *receiver; - v = stack_pointer[-1]; - receiver = stack_pointer[-2]; - DEOPT_IF(tstate->interp->eval_frame, SEND); - PyGenObject *gen = (PyGenObject *)receiver; - DEOPT_IF(Py_TYPE(gen) != &PyGen_Type && Py_TYPE(gen) != &PyCoro_Type, SEND); - DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, SEND); - STAT_INC(SEND, hit); - _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; - STACK_SHRINK(1); - _PyFrame_StackPush(gen_frame, v); - gen->gi_frame_state = FRAME_EXECUTING; - gen->gi_exc_state.previous_item = tstate->exc_info; - tstate->exc_info = &gen->gi_exc_state; - assert(next_instr - this_instr + oparg <= UINT16_MAX); - frame->return_offset = (uint16_t)(next_instr - this_instr + oparg); - DISPATCH_INLINED(gen_frame); + INSTRUCTION_STATS(COMPARE_OP_FLOAT); + PyObject *right; + PyObject *left; + PyObject *res; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_OP); + DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_OP); + STAT_INC(COMPARE_OP, hit); + double dleft = PyFloat_AS_DOUBLE(left); + double dright = PyFloat_AS_DOUBLE(right); + // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg + int sign_ish = COMPARISON_BIT(dleft, dright); + _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); + _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); + res = (sign_ish & oparg) ? Py_True : Py_False; + // It's always a bool, so we don't care about oparg & 16. + STACK_SHRINK(1); + stack_pointer[-1] = res; + DISPATCH(); } - TARGET(INSTRUMENTED_YIELD_VALUE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_YIELD_VALUE); - PyObject *retval; - retval = stack_pointer[-1]; - assert(frame != &entry_frame); + TARGET(COMPARE_OP_INT) { frame->instr_ptr = next_instr; - PyGenObject *gen = _PyFrame_GetGenerator(frame); - assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); - assert(oparg == 0 || oparg == 1); - gen->gi_frame_state = FRAME_SUSPENDED + oparg; - _PyFrame_SetStackPointer(frame, stack_pointer - 1); - int err = _Py_call_instrumentation_arg( - tstate, PY_MONITORING_EVENT_PY_YIELD, - frame, this_instr, retval); - if (err) GOTO_ERROR(error); - tstate->exc_info = gen->gi_exc_state.previous_item; - gen->gi_exc_state.previous_item = NULL; - _Py_LeaveRecursiveCallPy(tstate); - _PyInterpreterFrame *gen_frame = frame; - frame = tstate->current_frame = frame->previous; - gen_frame->previous = NULL; - _PyFrame_StackPush(frame, retval); - /* We don't know which of these is relevant here, so keep them equal */ - assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); - LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); - goto resume_frame; + next_instr += 2; + INSTRUCTION_STATS(COMPARE_OP_INT); + PyObject *right; + PyObject *left; + PyObject *res; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + DEOPT_IF(!PyLong_CheckExact(left), COMPARE_OP); + DEOPT_IF(!PyLong_CheckExact(right), COMPARE_OP); + DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)left), COMPARE_OP); + DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)right), COMPARE_OP); + STAT_INC(COMPARE_OP, hit); + assert(_PyLong_DigitCount((PyLongObject *)left) <= 1 && + _PyLong_DigitCount((PyLongObject *)right) <= 1); + Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left); + Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right); + // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg + int sign_ish = COMPARISON_BIT(ileft, iright); + _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); + _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); + res = (sign_ish & oparg) ? Py_True : Py_False; + // It's always a bool, so we don't care about oparg & 16. + STACK_SHRINK(1); + stack_pointer[-1] = res; + DISPATCH(); } - TARGET(YIELD_VALUE) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(YIELD_VALUE); - PyObject *retval; - retval = stack_pointer[-1]; - // NOTE: It's important that YIELD_VALUE never raises an exception! - // The compiler treats any exception raised here as a failed close() - // or throw() call. - assert(frame != &entry_frame); + TARGET(COMPARE_OP_STR) { frame->instr_ptr = next_instr; - PyGenObject *gen = _PyFrame_GetGenerator(frame); - assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); - assert(oparg == 0 || oparg == 1); - gen->gi_frame_state = FRAME_SUSPENDED + oparg; - _PyFrame_SetStackPointer(frame, stack_pointer - 1); - tstate->exc_info = gen->gi_exc_state.previous_item; - gen->gi_exc_state.previous_item = NULL; - _Py_LeaveRecursiveCallPy(tstate); - _PyInterpreterFrame *gen_frame = frame; - frame = tstate->current_frame = frame->previous; - gen_frame->previous = NULL; - _PyFrame_StackPush(frame, retval); - /* We don't know which of these is relevant here, so keep them equal */ - assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); - LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); - goto resume_frame; + next_instr += 2; + INSTRUCTION_STATS(COMPARE_OP_STR); + PyObject *right; + PyObject *left; + PyObject *res; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_OP); + DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_OP); + STAT_INC(COMPARE_OP, hit); + int eq = _PyUnicode_Equal(left, right); + assert((oparg >> 5) == Py_EQ || (oparg >> 5) == Py_NE); + _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); + _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); + assert(eq == 0 || eq == 1); + assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); + assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); + res = ((COMPARISON_NOT_EQUALS + eq) & oparg) ? Py_True : Py_False; + // It's always a bool, so we don't care about oparg & 16. + STACK_SHRINK(1); + stack_pointer[-1] = res; + DISPATCH(); } - TARGET(POP_EXCEPT) { + TARGET(CONTAINS_OP) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(POP_EXCEPT); - PyObject *exc_value; - exc_value = stack_pointer[-1]; - _PyErr_StackItem *exc_info = tstate->exc_info; - Py_XSETREF(exc_info->exc_value, exc_value); + INSTRUCTION_STATS(CONTAINS_OP); + PyObject *right; + PyObject *left; + PyObject *b; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + int res = PySequence_Contains(right, left); + Py_DECREF(left); + Py_DECREF(right); + if (res < 0) goto pop_2_error; + b = (res ^ oparg) ? Py_True : Py_False; STACK_SHRINK(1); + stack_pointer[-1] = b; DISPATCH(); } - TARGET(RERAISE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + TARGET(CONVERT_VALUE) { + frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(RERAISE); - PyObject *exc; - PyObject **values; - exc = stack_pointer[-1]; - values = stack_pointer - 1 - oparg; - TIER_ONE_ONLY - assert(oparg >= 0 && oparg <= 2); - if (oparg) { - PyObject *lasti = values[0]; - if (PyLong_Check(lasti)) { - frame->instr_ptr = _PyCode_CODE(_PyFrame_GetCode(frame)) + PyLong_AsLong(lasti); - assert(!_PyErr_Occurred(tstate)); - } - else { - assert(PyLong_Check(lasti)); - _PyErr_SetString(tstate, PyExc_SystemError, "lasti is not an int"); - GOTO_ERROR(error); - } - } - assert(exc && PyExceptionInstance_Check(exc)); - Py_INCREF(exc); - _PyErr_SetRaisedException(tstate, exc); - monitor_reraise(tstate, frame, this_instr); - goto exception_unwind; + INSTRUCTION_STATS(CONVERT_VALUE); + PyObject *value; + PyObject *result; + value = stack_pointer[-1]; + convertion_func_ptr conv_fn; + assert(oparg >= FVC_STR && oparg <= FVC_ASCII); + conv_fn = CONVERSION_FUNCTIONS[oparg]; + result = conv_fn(value); + Py_DECREF(value); + if (result == NULL) goto pop_1_error; + stack_pointer[-1] = result; + DISPATCH(); } - TARGET(END_ASYNC_FOR) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + TARGET(COPY) { + frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(END_ASYNC_FOR); - PyObject *exc; - PyObject *awaitable; - exc = stack_pointer[-1]; - awaitable = stack_pointer[-2]; - TIER_ONE_ONLY - assert(exc && PyExceptionInstance_Check(exc)); - if (PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration)) { - Py_DECREF(awaitable); - Py_DECREF(exc); - } - else { - Py_INCREF(exc); - _PyErr_SetRaisedException(tstate, exc); - monitor_reraise(tstate, frame, this_instr); - goto exception_unwind; - } - STACK_SHRINK(2); + INSTRUCTION_STATS(COPY); + PyObject *bottom; + PyObject *top; + bottom = stack_pointer[-1 - (oparg-1)]; + assert(oparg > 0); + top = Py_NewRef(bottom); + STACK_GROW(1); + stack_pointer[-1] = top; DISPATCH(); } - TARGET(CLEANUP_THROW) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + TARGET(COPY_FREE_VARS) { + frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(CLEANUP_THROW); - PyObject *exc_value; - PyObject *last_sent_val; - PyObject *sub_iter; - PyObject *none; - PyObject *value; - exc_value = stack_pointer[-1]; - last_sent_val = stack_pointer[-2]; - sub_iter = stack_pointer[-3]; - TIER_ONE_ONLY - assert(throwflag); - assert(exc_value && PyExceptionInstance_Check(exc_value)); - if (PyErr_GivenExceptionMatches(exc_value, PyExc_StopIteration)) { - value = Py_NewRef(((PyStopIterationObject *)exc_value)->value); - Py_DECREF(sub_iter); - Py_DECREF(last_sent_val); - Py_DECREF(exc_value); - none = Py_None; - } - else { - _PyErr_SetRaisedException(tstate, Py_NewRef(exc_value)); - monitor_reraise(tstate, frame, this_instr); - goto exception_unwind; + INSTRUCTION_STATS(COPY_FREE_VARS); + /* Copy closure variables to free variables */ + PyCodeObject *co = _PyFrame_GetCode(frame); + assert(PyFunction_Check(frame->f_funcobj)); + PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure; + assert(oparg == co->co_nfreevars); + int offset = co->co_nlocalsplus - oparg; + for (int i = 0; i < oparg; ++i) { + PyObject *o = PyTuple_GET_ITEM(closure, i); + frame->localsplus[offset + i] = Py_NewRef(o); } - STACK_SHRINK(1); - stack_pointer[-2] = none; - stack_pointer[-1] = value; DISPATCH(); } - TARGET(LOAD_ASSERTION_ERROR) { + TARGET(DELETE_ATTR) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(LOAD_ASSERTION_ERROR); - PyObject *value; - value = Py_NewRef(PyExc_AssertionError); - STACK_GROW(1); - stack_pointer[-1] = value; + INSTRUCTION_STATS(DELETE_ATTR); + PyObject *owner; + owner = stack_pointer[-1]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + int err = PyObject_DelAttr(owner, name); + Py_DECREF(owner); + if (err) goto pop_1_error; + STACK_SHRINK(1); DISPATCH(); } - TARGET(LOAD_BUILD_CLASS) { + TARGET(DELETE_DEREF) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(LOAD_BUILD_CLASS); - PyObject *bc; - if (PyMapping_GetOptionalItem(BUILTINS(), &_Py_ID(__build_class__), &bc) < 0) goto error; - if (bc == NULL) { - _PyErr_SetString(tstate, PyExc_NameError, - "__build_class__ not found"); - if (true) goto error; + INSTRUCTION_STATS(DELETE_DEREF); + PyObject *cell = GETLOCAL(oparg); + PyObject *oldobj = PyCell_GET(cell); + // Can't use ERROR_IF here. + // Fortunately we don't need its superpower. + if (oldobj == NULL) { + _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); + GOTO_ERROR(error); } - STACK_GROW(1); - stack_pointer[-1] = bc; + PyCell_SET(cell, NULL); + Py_DECREF(oldobj); DISPATCH(); } - TARGET(STORE_NAME) { + TARGET(DELETE_FAST) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(STORE_NAME); - PyObject *v; - v = stack_pointer[-1]; + INSTRUCTION_STATS(DELETE_FAST); + PyObject *v = GETLOCAL(oparg); + if (v == NULL) goto unbound_local_error; + SETLOCAL(oparg, NULL); + DISPATCH(); + } + + TARGET(DELETE_GLOBAL) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(DELETE_GLOBAL); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - PyObject *ns = LOCALS(); int err; - if (ns == NULL) { - _PyErr_Format(tstate, PyExc_SystemError, - "no locals found when storing %R", name); - Py_DECREF(v); - if (true) goto pop_1_error; + err = PyDict_DelItem(GLOBALS(), name); + // Can't use ERROR_IF here. + if (err != 0) { + if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + } + GOTO_ERROR(error); } - if (PyDict_CheckExact(ns)) - err = PyDict_SetItem(ns, name, v); - else - err = PyObject_SetItem(ns, name, v); - Py_DECREF(v); - if (err) goto pop_1_error; - STACK_SHRINK(1); DISPATCH(); } @@ -1719,982 +2202,1162 @@ DISPATCH(); } - TARGET(UNPACK_SEQUENCE) { + TARGET(DELETE_SUBSCR) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(UNPACK_SEQUENCE); - PREDICTED(UNPACK_SEQUENCE); - _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE == 1, "incorrect cache size"); - PyObject *seq; - // _SPECIALIZE_UNPACK_SEQUENCE - seq = stack_pointer[-1]; - { - uint16_t counter = read_u16(&this_instr[1].cache); - TIER_ONE_ONLY - #if ENABLE_SPECIALIZATION - if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - next_instr = this_instr; - _Py_Specialize_UnpackSequence(seq, next_instr, oparg); - DISPATCH_SAME_OPARG(); - } - STAT_INC(UNPACK_SEQUENCE, deferred); - DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - #endif /* ENABLE_SPECIALIZATION */ - (void)seq; - (void)counter; - } - // _UNPACK_SEQUENCE - { - PyObject **top = stack_pointer + oparg - 1; - int res = _PyEval_UnpackIterable(tstate, seq, oparg, -1, top); - Py_DECREF(seq); - if (res == 0) goto pop_1_error; - } - STACK_SHRINK(1); - STACK_GROW(oparg); + next_instr += 1; + INSTRUCTION_STATS(DELETE_SUBSCR); + PyObject *sub; + PyObject *container; + sub = stack_pointer[-1]; + container = stack_pointer[-2]; + /* del container[sub] */ + int err = PyObject_DelItem(container, sub); + Py_DECREF(container); + Py_DECREF(sub); + if (err) goto pop_2_error; + STACK_SHRINK(2); DISPATCH(); } - TARGET(UNPACK_SEQUENCE_TWO_TUPLE) { + TARGET(DICT_MERGE) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(UNPACK_SEQUENCE_TWO_TUPLE); - PyObject *seq; - PyObject **values; - seq = stack_pointer[-1]; - values = stack_pointer - 1; - DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); - DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE); - assert(oparg == 2); - STAT_INC(UNPACK_SEQUENCE, hit); - values[0] = Py_NewRef(PyTuple_GET_ITEM(seq, 1)); - values[1] = Py_NewRef(PyTuple_GET_ITEM(seq, 0)); - Py_DECREF(seq); + next_instr += 1; + INSTRUCTION_STATS(DICT_MERGE); + PyObject *update; + PyObject *dict; + PyObject *callable; + update = stack_pointer[-1]; + dict = stack_pointer[-2 - (oparg - 1)]; + callable = stack_pointer[-5 - (oparg - 1)]; + if (_PyDict_MergeEx(dict, update, 2) < 0) { + _PyEval_FormatKwargsError(tstate, callable, update); + Py_DECREF(update); + if (true) goto pop_1_error; + } + Py_DECREF(update); STACK_SHRINK(1); - STACK_GROW(oparg); DISPATCH(); } - TARGET(UNPACK_SEQUENCE_TUPLE) { + TARGET(DICT_UPDATE) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(UNPACK_SEQUENCE_TUPLE); - PyObject *seq; - PyObject **values; - seq = stack_pointer[-1]; - values = stack_pointer - 1; - DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); - DEOPT_IF(PyTuple_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); - STAT_INC(UNPACK_SEQUENCE, hit); - PyObject **items = _PyTuple_ITEMS(seq); - for (int i = oparg; --i >= 0; ) { - *values++ = Py_NewRef(items[i]); + next_instr += 1; + INSTRUCTION_STATS(DICT_UPDATE); + PyObject *update; + PyObject *dict; + update = stack_pointer[-1]; + dict = stack_pointer[-2 - (oparg - 1)]; + if (PyDict_Update(dict, update) < 0) { + if (_PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) { + _PyErr_Format(tstate, PyExc_TypeError, + "'%.200s' object is not a mapping", + Py_TYPE(update)->tp_name); + } + Py_DECREF(update); + if (true) goto pop_1_error; } - Py_DECREF(seq); + Py_DECREF(update); STACK_SHRINK(1); - STACK_GROW(oparg); DISPATCH(); } - TARGET(UNPACK_SEQUENCE_LIST) { + TARGET(END_ASYNC_FOR) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(END_ASYNC_FOR); + PyObject *exc; + PyObject *awaitable; + exc = stack_pointer[-1]; + awaitable = stack_pointer[-2]; + TIER_ONE_ONLY + assert(exc && PyExceptionInstance_Check(exc)); + if (PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration)) { + Py_DECREF(awaitable); + Py_DECREF(exc); + } + else { + Py_INCREF(exc); + _PyErr_SetRaisedException(tstate, exc); + monitor_reraise(tstate, frame, this_instr); + goto exception_unwind; + } + STACK_SHRINK(2); + DISPATCH(); + } + + TARGET(END_FOR) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(UNPACK_SEQUENCE_LIST); - PyObject *seq; - PyObject **values; - seq = stack_pointer[-1]; - values = stack_pointer - 1; - DEOPT_IF(!PyList_CheckExact(seq), UNPACK_SEQUENCE); - DEOPT_IF(PyList_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); - STAT_INC(UNPACK_SEQUENCE, hit); - PyObject **items = _PyList_ITEMS(seq); - for (int i = oparg; --i >= 0; ) { - *values++ = Py_NewRef(items[i]); + next_instr += 1; + INSTRUCTION_STATS(END_FOR); + PyObject *value; + // POP_TOP + value = stack_pointer[-1]; + { + Py_DECREF(value); } - Py_DECREF(seq); - STACK_SHRINK(1); - STACK_GROW(oparg); + // POP_TOP + value = stack_pointer[-2]; + { + Py_DECREF(value); + } + STACK_SHRINK(2); DISPATCH(); } - TARGET(UNPACK_EX) { + TARGET(END_SEND) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(UNPACK_EX); - PyObject *seq; - seq = stack_pointer[-1]; - int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8); - PyObject **top = stack_pointer + totalargs - 1; - int res = _PyEval_UnpackIterable(tstate, seq, oparg & 0xFF, oparg >> 8, top); - Py_DECREF(seq); - if (res == 0) goto pop_1_error; - STACK_GROW((oparg & 0xFF) + (oparg >> 8)); + INSTRUCTION_STATS(END_SEND); + PyObject *value; + PyObject *receiver; + value = stack_pointer[-1]; + receiver = stack_pointer[-2]; + Py_DECREF(receiver); + STACK_SHRINK(1); + stack_pointer[-1] = value; DISPATCH(); } - TARGET(STORE_ATTR) { - frame->instr_ptr = next_instr; - next_instr += 5; - INSTRUCTION_STATS(STORE_ATTR); - PREDICTED(STORE_ATTR); - _Py_CODEUNIT *this_instr = next_instr - 5; - static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); - PyObject *owner; - PyObject *v; - // _SPECIALIZE_STORE_ATTR - owner = stack_pointer[-1]; - { - uint16_t counter = read_u16(&this_instr[1].cache); - TIER_ONE_ONLY - #if ENABLE_SPECIALIZATION - if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - next_instr = this_instr; - _Py_Specialize_StoreAttr(owner, next_instr, name); - DISPATCH_SAME_OPARG(); - } - STAT_INC(STORE_ATTR, deferred); - DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - #endif /* ENABLE_SPECIALIZATION */ - } - // _STORE_ATTR - v = stack_pointer[-2]; - { - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyObject_SetAttr(owner, name, v); - Py_DECREF(v); - Py_DECREF(owner); - if (err) goto pop_2_error; - } - STACK_SHRINK(2); - DISPATCH(); - } - - TARGET(DELETE_ATTR) { + TARGET(ENTER_EXECUTOR) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(DELETE_ATTR); - PyObject *owner; - owner = stack_pointer[-1]; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyObject_DelAttr(owner, name); - Py_DECREF(owner); - if (err) goto pop_1_error; - STACK_SHRINK(1); - DISPATCH(); - } + INSTRUCTION_STATS(ENTER_EXECUTOR); + TIER_ONE_ONLY + CHECK_EVAL_BREAKER(); - TARGET(STORE_GLOBAL) { + PyCodeObject *code = _PyFrame_GetCode(frame); + _PyExecutorObject *executor = (_PyExecutorObject *)code->co_executors->executors[oparg&255]; + int original_oparg = executor->vm_data.oparg | (oparg & 0xfffff00); + JUMPBY(1-original_oparg); frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(STORE_GLOBAL); - PyObject *v; - v = stack_pointer[-1]; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyDict_SetItem(GLOBALS(), name, v); - Py_DECREF(v); - if (err) goto pop_1_error; - STACK_SHRINK(1); - DISPATCH(); + Py_INCREF(executor); + if (executor->execute == _PyUopExecute) { + current_executor = (_PyUOpExecutorObject *)executor; + GOTO_TIER_TWO(); + } + frame = executor->execute(executor, frame, stack_pointer); + if (frame == NULL) { + frame = tstate->current_frame; + goto resume_with_error; + } + goto enter_tier_one; } - TARGET(DELETE_GLOBAL) { + TARGET(EXIT_INIT_CHECK) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(DELETE_GLOBAL); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err; - err = PyDict_DelItem(GLOBALS(), name); - // Can't use ERROR_IF here. - if (err != 0) { - if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { - _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, name); - } + INSTRUCTION_STATS(EXIT_INIT_CHECK); + PyObject *should_be_none; + should_be_none = stack_pointer[-1]; + assert(STACK_LEVEL() == 2); + if (should_be_none != Py_None) { + PyErr_Format(PyExc_TypeError, + "__init__() should return None, not '%.200s'", + Py_TYPE(should_be_none)->tp_name); GOTO_ERROR(error); } + STACK_SHRINK(1); DISPATCH(); } - TARGET(LOAD_LOCALS) { + TARGET(EXTENDED_ARG) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(LOAD_LOCALS); - PyObject *locals; - locals = LOCALS(); - if (locals == NULL) { - _PyErr_SetString(tstate, PyExc_SystemError, - "no locals found"); - if (true) goto error; - } - Py_INCREF(locals); - STACK_GROW(1); - stack_pointer[-1] = locals; - DISPATCH(); + INSTRUCTION_STATS(EXTENDED_ARG); + assert(oparg); + opcode = next_instr->op.code; + oparg = oparg << 8 | next_instr->op.arg; + PRE_DISPATCH_GOTO(); + DISPATCH_GOTO(); } - TARGET(LOAD_FROM_DICT_OR_GLOBALS) { + TARGET(FORMAT_SIMPLE) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(LOAD_FROM_DICT_OR_GLOBALS); - PyObject *mod_or_class_dict; - PyObject *v; - mod_or_class_dict = stack_pointer[-1]; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) { - GOTO_ERROR(error); + INSTRUCTION_STATS(FORMAT_SIMPLE); + PyObject *value; + PyObject *res; + value = stack_pointer[-1]; + /* If value is a unicode object, then we know the result + * of format(value) is value itself. */ + if (!PyUnicode_CheckExact(value)) { + res = PyObject_Format(value, NULL); + Py_DECREF(value); + if (res == NULL) goto pop_1_error; } - if (v == NULL) { - if (PyDict_GetItemRef(GLOBALS(), name, &v) < 0) { - GOTO_ERROR(error); - } - if (v == NULL) { - if (PyMapping_GetOptionalItem(BUILTINS(), name, &v) < 0) { - GOTO_ERROR(error); - } - if (v == NULL) { - _PyEval_FormatExcCheckArg( - tstate, PyExc_NameError, - NAME_ERROR_MSG, name); - GOTO_ERROR(error); - } - } + else { + res = value; } - Py_DECREF(mod_or_class_dict); - stack_pointer[-1] = v; + stack_pointer[-1] = res; DISPATCH(); } - TARGET(LOAD_NAME) { + TARGET(FORMAT_WITH_SPEC) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(LOAD_NAME); - PyObject *v; - PyObject *mod_or_class_dict = LOCALS(); - if (mod_or_class_dict == NULL) { - _PyErr_SetString(tstate, PyExc_SystemError, - "no locals found"); - if (true) goto error; - } - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) { - GOTO_ERROR(error); - } - if (v == NULL) { - if (PyDict_GetItemRef(GLOBALS(), name, &v) < 0) { - GOTO_ERROR(error); - } - if (v == NULL) { - if (PyMapping_GetOptionalItem(BUILTINS(), name, &v) < 0) { - GOTO_ERROR(error); - } - if (v == NULL) { - _PyEval_FormatExcCheckArg( - tstate, PyExc_NameError, - NAME_ERROR_MSG, name); - GOTO_ERROR(error); - } - } - } - STACK_GROW(1); - stack_pointer[-1] = v; + INSTRUCTION_STATS(FORMAT_WITH_SPEC); + PyObject *fmt_spec; + PyObject *value; + PyObject *res; + fmt_spec = stack_pointer[-1]; + value = stack_pointer[-2]; + res = PyObject_Format(value, fmt_spec); + Py_DECREF(value); + Py_DECREF(fmt_spec); + if (res == NULL) goto pop_2_error; + STACK_SHRINK(1); + stack_pointer[-1] = res; DISPATCH(); } - TARGET(LOAD_GLOBAL) { + TARGET(FOR_ITER) { frame->instr_ptr = next_instr; - next_instr += 5; - INSTRUCTION_STATS(LOAD_GLOBAL); - PREDICTED(LOAD_GLOBAL); - _Py_CODEUNIT *this_instr = next_instr - 5; - static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size"); - PyObject *res; - PyObject *null = NULL; - // _SPECIALIZE_LOAD_GLOBAL + next_instr += 2; + INSTRUCTION_STATS(FOR_ITER); + PREDICTED(FOR_ITER); + _Py_CODEUNIT *this_instr = next_instr - 2; + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); + PyObject *iter; + PyObject *next; + // _SPECIALIZE_FOR_ITER + iter = stack_pointer[-1]; { uint16_t counter = read_u16(&this_instr[1].cache); TIER_ONE_ONLY #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); next_instr = this_instr; - _Py_Specialize_LoadGlobal(GLOBALS(), BUILTINS(), next_instr, name); + _Py_Specialize_ForIter(iter, next_instr, oparg); DISPATCH_SAME_OPARG(); } - STAT_INC(LOAD_GLOBAL, deferred); + STAT_INC(FOR_ITER, deferred); DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); #endif /* ENABLE_SPECIALIZATION */ } - // _LOAD_GLOBAL + // _FOR_ITER { - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); - if (PyDict_CheckExact(GLOBALS()) - && PyDict_CheckExact(BUILTINS())) - { - res = _PyDict_LoadGlobal((PyDictObject *)GLOBALS(), - (PyDictObject *)BUILTINS(), - name); - if (res == NULL) { - if (!_PyErr_Occurred(tstate)) { - /* _PyDict_LoadGlobal() returns NULL without raising - * an exception if the key doesn't exist */ - _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, name); + /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */ + next = (*Py_TYPE(iter)->tp_iternext)(iter); + if (next == NULL) { + if (_PyErr_Occurred(tstate)) { + if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { + GOTO_ERROR(error); } - if (true) goto error; + monitor_raise(tstate, frame, this_instr); + _PyErr_Clear(tstate); } - Py_INCREF(res); + /* iterator ended normally */ + assert(next_instr[oparg].op.code == END_FOR || + next_instr[oparg].op.code == INSTRUMENTED_END_FOR); + Py_DECREF(iter); + STACK_SHRINK(1); + /* Jump forward oparg, then skip following END_FOR instruction */ + JUMPBY(oparg + 1); + DISPATCH(); } - else { - /* Slow-path if globals or builtins is not a dict */ - /* namespace 1: globals */ - if (PyMapping_GetOptionalItem(GLOBALS(), name, &res) < 0) goto error; - if (res == NULL) { - /* namespace 2: builtins */ - if (PyMapping_GetOptionalItem(BUILTINS(), name, &res) < 0) goto error; - if (res == NULL) { - _PyEval_FormatExcCheckArg( - tstate, PyExc_NameError, - NAME_ERROR_MSG, name); - if (true) goto error; - } - } - } - null = NULL; + // Common case: no jump, leave it to the code generator } STACK_GROW(1); - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = res; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + stack_pointer[-1] = next; DISPATCH(); } - TARGET(LOAD_GLOBAL_MODULE) { + TARGET(FOR_ITER_GEN) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 5; - INSTRUCTION_STATS(LOAD_GLOBAL_MODULE); - PyObject *res; - PyObject *null = NULL; - // _GUARD_GLOBALS_VERSION + next_instr += 2; + INSTRUCTION_STATS(FOR_ITER_GEN); + PyObject *iter; + iter = stack_pointer[-1]; + DEOPT_IF(tstate->interp->eval_frame, FOR_ITER); + PyGenObject *gen = (PyGenObject *)iter; + DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER); + DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER); + STAT_INC(FOR_ITER, hit); + _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; + _PyFrame_StackPush(gen_frame, Py_None); + gen->gi_frame_state = FRAME_EXECUTING; + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + assert(next_instr[oparg].op.code == END_FOR || + next_instr[oparg].op.code == INSTRUMENTED_END_FOR); + assert(next_instr - this_instr + oparg <= UINT16_MAX); + frame->return_offset = (uint16_t)(next_instr - this_instr + oparg); + DISPATCH_INLINED(gen_frame); + } + + TARGET(FOR_ITER_LIST) { + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(FOR_ITER_LIST); + PyObject *iter; + PyObject *next; + // _ITER_CHECK_LIST + iter = stack_pointer[-1]; { - uint16_t version = read_u16(&this_instr[2].cache); - PyDictObject *dict = (PyDictObject *)GLOBALS(); - DEOPT_IF(!PyDict_CheckExact(dict), LOAD_GLOBAL); - DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL); - assert(DK_IS_UNICODE(dict->ma_keys)); + DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type, FOR_ITER); } - // _LOAD_GLOBAL_MODULE + // _ITER_JUMP_LIST { - uint16_t index = read_u16(&this_instr[4].cache); - PyDictObject *dict = (PyDictObject *)GLOBALS(); - PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys); - res = entries[index].me_value; - DEOPT_IF(res == NULL, LOAD_GLOBAL); - Py_INCREF(res); - STAT_INC(LOAD_GLOBAL, hit); - null = NULL; + _PyListIterObject *it = (_PyListIterObject *)iter; + assert(Py_TYPE(iter) == &PyListIter_Type); + STAT_INC(FOR_ITER, hit); + PyListObject *seq = it->it_seq; + if (seq == NULL || it->it_index >= PyList_GET_SIZE(seq)) { + if (seq != NULL) { + it->it_seq = NULL; + Py_DECREF(seq); + } + Py_DECREF(iter); + STACK_SHRINK(1); + /* Jump forward oparg, then skip following END_FOR instruction */ + JUMPBY(oparg + 1); + DISPATCH(); + } + } + // _ITER_NEXT_LIST + { + _PyListIterObject *it = (_PyListIterObject *)iter; + assert(Py_TYPE(iter) == &PyListIter_Type); + PyListObject *seq = it->it_seq; + assert(seq); + assert(it->it_index < PyList_GET_SIZE(seq)); + next = Py_NewRef(PyList_GET_ITEM(seq, it->it_index++)); } STACK_GROW(1); - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = res; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + stack_pointer[-1] = next; DISPATCH(); } - TARGET(LOAD_GLOBAL_BUILTIN) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 5; - INSTRUCTION_STATS(LOAD_GLOBAL_BUILTIN); - PyObject *res; - PyObject *null = NULL; - // _GUARD_GLOBALS_VERSION + TARGET(FOR_ITER_RANGE) { + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(FOR_ITER_RANGE); + PyObject *iter; + PyObject *next; + // _ITER_CHECK_RANGE + iter = stack_pointer[-1]; { - uint16_t version = read_u16(&this_instr[2].cache); - PyDictObject *dict = (PyDictObject *)GLOBALS(); - DEOPT_IF(!PyDict_CheckExact(dict), LOAD_GLOBAL); - DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL); - assert(DK_IS_UNICODE(dict->ma_keys)); + _PyRangeIterObject *r = (_PyRangeIterObject *)iter; + DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); } - // _GUARD_BUILTINS_VERSION + // _ITER_JUMP_RANGE { - uint16_t version = read_u16(&this_instr[3].cache); - PyDictObject *dict = (PyDictObject *)BUILTINS(); - DEOPT_IF(!PyDict_CheckExact(dict), LOAD_GLOBAL); - DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL); - assert(DK_IS_UNICODE(dict->ma_keys)); + _PyRangeIterObject *r = (_PyRangeIterObject *)iter; + assert(Py_TYPE(r) == &PyRangeIter_Type); + STAT_INC(FOR_ITER, hit); + if (r->len <= 0) { + STACK_SHRINK(1); + Py_DECREF(r); + // Jump over END_FOR instruction. + JUMPBY(oparg + 1); + DISPATCH(); + } } - // _LOAD_GLOBAL_BUILTINS + // _ITER_NEXT_RANGE { - uint16_t index = read_u16(&this_instr[4].cache); - PyDictObject *bdict = (PyDictObject *)BUILTINS(); - PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(bdict->ma_keys); - res = entries[index].me_value; - DEOPT_IF(res == NULL, LOAD_GLOBAL); - Py_INCREF(res); - STAT_INC(LOAD_GLOBAL, hit); - null = NULL; + _PyRangeIterObject *r = (_PyRangeIterObject *)iter; + assert(Py_TYPE(r) == &PyRangeIter_Type); + assert(r->len > 0); + long value = r->start; + r->start = value + r->step; + r->len--; + next = PyLong_FromLong(value); + if (next == NULL) goto error; } STACK_GROW(1); - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = res; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + stack_pointer[-1] = next; DISPATCH(); } - TARGET(DELETE_FAST) { + TARGET(FOR_ITER_TUPLE) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(DELETE_FAST); - PyObject *v = GETLOCAL(oparg); - if (v == NULL) goto unbound_local_error; - SETLOCAL(oparg, NULL); + next_instr += 2; + INSTRUCTION_STATS(FOR_ITER_TUPLE); + PyObject *iter; + PyObject *next; + // _ITER_CHECK_TUPLE + iter = stack_pointer[-1]; + { + DEOPT_IF(Py_TYPE(iter) != &PyTupleIter_Type, FOR_ITER); + } + // _ITER_JUMP_TUPLE + { + _PyTupleIterObject *it = (_PyTupleIterObject *)iter; + assert(Py_TYPE(iter) == &PyTupleIter_Type); + STAT_INC(FOR_ITER, hit); + PyTupleObject *seq = it->it_seq; + if (seq == NULL || it->it_index >= PyTuple_GET_SIZE(seq)) { + if (seq != NULL) { + it->it_seq = NULL; + Py_DECREF(seq); + } + Py_DECREF(iter); + STACK_SHRINK(1); + /* Jump forward oparg, then skip following END_FOR instruction */ + JUMPBY(oparg + 1); + DISPATCH(); + } + } + // _ITER_NEXT_TUPLE + { + _PyTupleIterObject *it = (_PyTupleIterObject *)iter; + assert(Py_TYPE(iter) == &PyTupleIter_Type); + PyTupleObject *seq = it->it_seq; + assert(seq); + assert(it->it_index < PyTuple_GET_SIZE(seq)); + next = Py_NewRef(PyTuple_GET_ITEM(seq, it->it_index++)); + } + STACK_GROW(1); + stack_pointer[-1] = next; DISPATCH(); } - TARGET(MAKE_CELL) { + TARGET(GET_AITER) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(MAKE_CELL); - // "initial" is probably NULL but not if it's an arg (or set - // via PyFrame_LocalsToFast() before MAKE_CELL has run). - PyObject *initial = GETLOCAL(oparg); - PyObject *cell = PyCell_New(initial); - if (cell == NULL) { - GOTO_ERROR(error); + INSTRUCTION_STATS(GET_AITER); + PyObject *obj; + PyObject *iter; + obj = stack_pointer[-1]; + unaryfunc getter = NULL; + PyTypeObject *type = Py_TYPE(obj); + + if (type->tp_as_async != NULL) { + getter = type->tp_as_async->am_aiter; } - SETLOCAL(oparg, cell); + + if (getter == NULL) { + _PyErr_Format(tstate, PyExc_TypeError, + "'async for' requires an object with " + "__aiter__ method, got %.100s", + type->tp_name); + Py_DECREF(obj); + if (true) goto pop_1_error; + } + + iter = (*getter)(obj); + Py_DECREF(obj); + if (iter == NULL) goto pop_1_error; + + if (Py_TYPE(iter)->tp_as_async == NULL || + Py_TYPE(iter)->tp_as_async->am_anext == NULL) { + + _PyErr_Format(tstate, PyExc_TypeError, + "'async for' received an object from __aiter__ " + "that does not implement __anext__: %.100s", + Py_TYPE(iter)->tp_name); + Py_DECREF(iter); + if (true) goto pop_1_error; + } + stack_pointer[-1] = iter; DISPATCH(); } - TARGET(DELETE_DEREF) { + TARGET(GET_ANEXT) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(DELETE_DEREF); - PyObject *cell = GETLOCAL(oparg); - PyObject *oldobj = PyCell_GET(cell); - // Can't use ERROR_IF here. - // Fortunately we don't need its superpower. - if (oldobj == NULL) { - _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); - GOTO_ERROR(error); - } - PyCell_SET(cell, NULL); - Py_DECREF(oldobj); - DISPATCH(); - } + INSTRUCTION_STATS(GET_ANEXT); + PyObject *aiter; + PyObject *awaitable; + aiter = stack_pointer[-1]; + unaryfunc getter = NULL; + PyObject *next_iter = NULL; + PyTypeObject *type = Py_TYPE(aiter); - TARGET(LOAD_FROM_DICT_OR_DEREF) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(LOAD_FROM_DICT_OR_DEREF); - PyObject *class_dict; - PyObject *value; - class_dict = stack_pointer[-1]; - PyObject *name; - assert(class_dict); - assert(oparg >= 0 && oparg < _PyFrame_GetCode(frame)->co_nlocalsplus); - name = PyTuple_GET_ITEM(_PyFrame_GetCode(frame)->co_localsplusnames, oparg); - if (PyMapping_GetOptionalItem(class_dict, name, &value) < 0) { - GOTO_ERROR(error); - } - if (!value) { - PyObject *cell = GETLOCAL(oparg); - value = PyCell_GET(cell); - if (value == NULL) { - _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); + if (PyAsyncGen_CheckExact(aiter)) { + awaitable = type->tp_as_async->am_anext(aiter); + if (awaitable == NULL) { GOTO_ERROR(error); } - Py_INCREF(value); - } - Py_DECREF(class_dict); - stack_pointer[-1] = value; - DISPATCH(); - } + } else { + if (type->tp_as_async != NULL){ + getter = type->tp_as_async->am_anext; + } - TARGET(LOAD_DEREF) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(LOAD_DEREF); - PyObject *value; - PyObject *cell = GETLOCAL(oparg); - value = PyCell_GET(cell); - if (value == NULL) { - _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); - if (true) goto error; + if (getter != NULL) { + next_iter = (*getter)(aiter); + if (next_iter == NULL) { + GOTO_ERROR(error); + } + } + else { + _PyErr_Format(tstate, PyExc_TypeError, + "'async for' requires an iterator with " + "__anext__ method, got %.100s", + type->tp_name); + GOTO_ERROR(error); + } + + awaitable = _PyCoro_GetAwaitableIter(next_iter); + if (awaitable == NULL) { + _PyErr_FormatFromCause( + PyExc_TypeError, + "'async for' received an invalid object " + "from __anext__: %.100s", + Py_TYPE(next_iter)->tp_name); + + Py_DECREF(next_iter); + GOTO_ERROR(error); + } else { + Py_DECREF(next_iter); + } } - Py_INCREF(value); STACK_GROW(1); - stack_pointer[-1] = value; + stack_pointer[-1] = awaitable; DISPATCH(); } - TARGET(STORE_DEREF) { + TARGET(GET_AWAITABLE) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(STORE_DEREF); - PyObject *v; - v = stack_pointer[-1]; - PyObject *cell = GETLOCAL(oparg); - PyObject *oldobj = PyCell_GET(cell); - PyCell_SET(cell, v); - Py_XDECREF(oldobj); - STACK_SHRINK(1); - DISPATCH(); - } + INSTRUCTION_STATS(GET_AWAITABLE); + PyObject *iterable; + PyObject *iter; + iterable = stack_pointer[-1]; + iter = _PyCoro_GetAwaitableIter(iterable); - TARGET(COPY_FREE_VARS) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(COPY_FREE_VARS); - /* Copy closure variables to free variables */ - PyCodeObject *co = _PyFrame_GetCode(frame); - assert(PyFunction_Check(frame->f_funcobj)); - PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure; - assert(oparg == co->co_nfreevars); - int offset = co->co_nlocalsplus - oparg; - for (int i = 0; i < oparg; ++i) { - PyObject *o = PyTuple_GET_ITEM(closure, i); - frame->localsplus[offset + i] = Py_NewRef(o); + if (iter == NULL) { + _PyEval_FormatAwaitableError(tstate, Py_TYPE(iterable), oparg); } - DISPATCH(); - } - TARGET(BUILD_STRING) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(BUILD_STRING); - PyObject **pieces; - PyObject *str; - pieces = stack_pointer - oparg; - str = _PyUnicode_JoinArray(&_Py_STR(empty), pieces, oparg); - for (int _i = oparg; --_i >= 0;) { - Py_DECREF(pieces[_i]); + Py_DECREF(iterable); + + if (iter != NULL && PyCoro_CheckExact(iter)) { + PyObject *yf = _PyGen_yf((PyGenObject*)iter); + if (yf != NULL) { + /* `iter` is a coroutine object that is being + awaited, `yf` is a pointer to the current awaitable + being awaited on. */ + Py_DECREF(yf); + Py_CLEAR(iter); + _PyErr_SetString(tstate, PyExc_RuntimeError, + "coroutine is being awaited already"); + /* The code below jumps to `error` if `iter` is NULL. */ + } } - if (str == NULL) { STACK_SHRINK(oparg); goto error; } - STACK_SHRINK(oparg); - STACK_GROW(1); - stack_pointer[-1] = str; + + if (iter == NULL) goto pop_1_error; + stack_pointer[-1] = iter; DISPATCH(); } - TARGET(BUILD_TUPLE) { + TARGET(GET_ITER) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(BUILD_TUPLE); - PyObject **values; - PyObject *tup; - values = stack_pointer - oparg; - tup = _PyTuple_FromArraySteal(values, oparg); - if (tup == NULL) { STACK_SHRINK(oparg); goto error; } - STACK_SHRINK(oparg); - STACK_GROW(1); - stack_pointer[-1] = tup; + INSTRUCTION_STATS(GET_ITER); + PyObject *iterable; + PyObject *iter; + iterable = stack_pointer[-1]; + /* before: [obj]; after [getiter(obj)] */ + iter = PyObject_GetIter(iterable); + Py_DECREF(iterable); + if (iter == NULL) goto pop_1_error; + stack_pointer[-1] = iter; DISPATCH(); } - TARGET(BUILD_LIST) { + TARGET(GET_LEN) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(BUILD_LIST); - PyObject **values; - PyObject *list; - values = stack_pointer - oparg; - list = _PyList_FromArraySteal(values, oparg); - if (list == NULL) { STACK_SHRINK(oparg); goto error; } - STACK_SHRINK(oparg); + INSTRUCTION_STATS(GET_LEN); + PyObject *obj; + PyObject *len_o; + obj = stack_pointer[-1]; + // PUSH(len(TOS)) + Py_ssize_t len_i = PyObject_Length(obj); + if (len_i < 0) goto error; + len_o = PyLong_FromSsize_t(len_i); + if (len_o == NULL) goto error; STACK_GROW(1); - stack_pointer[-1] = list; + stack_pointer[-1] = len_o; DISPATCH(); } - TARGET(LIST_EXTEND) { + TARGET(GET_YIELD_FROM_ITER) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(LIST_EXTEND); + INSTRUCTION_STATS(GET_YIELD_FROM_ITER); PyObject *iterable; - PyObject *list; + PyObject *iter; iterable = stack_pointer[-1]; - list = stack_pointer[-2 - (oparg-1)]; - PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable); - if (none_val == NULL) { - if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError) && - (Py_TYPE(iterable)->tp_iter == NULL && !PySequence_Check(iterable))) - { - _PyErr_Clear(tstate); - _PyErr_Format(tstate, PyExc_TypeError, - "Value after * must be an iterable, not %.200s", - Py_TYPE(iterable)->tp_name); + /* before: [obj]; after [getiter(obj)] */ + if (PyCoro_CheckExact(iterable)) { + /* `iterable` is a coroutine */ + if (!(_PyFrame_GetCode(frame)->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) { + /* and it is used in a 'yield from' expression of a + regular generator. */ + _PyErr_SetString(tstate, PyExc_TypeError, + "cannot 'yield from' a coroutine object " + "in a non-coroutine generator"); + GOTO_ERROR(error); + } + iter = iterable; + } + else if (PyGen_CheckExact(iterable)) { + iter = iterable; + } + else { + /* `iterable` is not a generator. */ + iter = PyObject_GetIter(iterable); + if (iter == NULL) { + GOTO_ERROR(error); } Py_DECREF(iterable); - if (true) goto pop_1_error; } - assert(Py_IsNone(none_val)); - Py_DECREF(iterable); - STACK_SHRINK(1); + stack_pointer[-1] = iter; DISPATCH(); } - TARGET(SET_UPDATE) { + TARGET(IMPORT_FROM) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(SET_UPDATE); - PyObject *iterable; - PyObject *set; - iterable = stack_pointer[-1]; - set = stack_pointer[-2 - (oparg-1)]; - int err = _PySet_Update(set, iterable); - Py_DECREF(iterable); - if (err < 0) goto pop_1_error; - STACK_SHRINK(1); + INSTRUCTION_STATS(IMPORT_FROM); + PyObject *from; + PyObject *res; + from = stack_pointer[-1]; + TIER_ONE_ONLY + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + res = import_from(tstate, from, name); + if (res == NULL) goto error; + STACK_GROW(1); + stack_pointer[-1] = res; DISPATCH(); } - TARGET(BUILD_SET) { + TARGET(IMPORT_NAME) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(BUILD_SET); - PyObject **values; - PyObject *set; - values = stack_pointer - oparg; - set = PySet_New(NULL); - if (set == NULL) - GOTO_ERROR(error); - int err = 0; - for (int i = 0; i < oparg; i++) { - PyObject *item = values[i]; - if (err == 0) - err = PySet_Add(set, item); - Py_DECREF(item); - } - if (err != 0) { - Py_DECREF(set); - if (true) { STACK_SHRINK(oparg); goto error; } - } - STACK_SHRINK(oparg); - STACK_GROW(1); - stack_pointer[-1] = set; + INSTRUCTION_STATS(IMPORT_NAME); + PyObject *fromlist; + PyObject *level; + PyObject *res; + fromlist = stack_pointer[-1]; + level = stack_pointer[-2]; + TIER_ONE_ONLY + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + res = import_name(tstate, frame, name, fromlist, level); + Py_DECREF(level); + Py_DECREF(fromlist); + if (res == NULL) goto pop_2_error; + STACK_SHRINK(1); + stack_pointer[-1] = res; DISPATCH(); } - TARGET(BUILD_MAP) { + TARGET(INSTRUMENTED_CALL) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(INSTRUMENTED_CALL); + int is_meth = PEEK(oparg + 1) != NULL; + int total_args = oparg + is_meth; + PyObject *function = PEEK(oparg + 2); + PyObject *arg = total_args == 0 ? + &_PyInstrumentation_MISSING : PEEK(total_args); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, function, arg); + if (err) goto error; + INCREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + GO_TO_INSTRUCTION(CALL); + } + + TARGET(INSTRUMENTED_CALL_FUNCTION_EX) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(BUILD_MAP); - PyObject **values; - PyObject *map; - values = stack_pointer - oparg*2; - map = _PyDict_FromItems( - values, 2, - values+1, 2, - oparg); - for (int _i = oparg*2; --_i >= 0;) { - Py_DECREF(values[_i]); - } - if (map == NULL) { STACK_SHRINK(oparg*2); goto error; } - STACK_SHRINK(oparg*2); - STACK_GROW(1); - stack_pointer[-1] = map; - DISPATCH(); + INSTRUCTION_STATS(INSTRUMENTED_CALL_FUNCTION_EX); + GO_TO_INSTRUCTION(CALL_FUNCTION_EX); } - TARGET(SETUP_ANNOTATIONS) { - frame->instr_ptr = next_instr; + TARGET(INSTRUMENTED_CALL_KW) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(SETUP_ANNOTATIONS); - int err; - PyObject *ann_dict; - if (LOCALS() == NULL) { - _PyErr_Format(tstate, PyExc_SystemError, - "no locals found when setting up annotations"); - if (true) goto error; - } - /* check if __annotations__ in locals()... */ - if (PyMapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict) < 0) goto error; - if (ann_dict == NULL) { - ann_dict = PyDict_New(); - if (ann_dict == NULL) goto error; - err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__), - ann_dict); - Py_DECREF(ann_dict); - if (err) goto error; - } - else { - Py_DECREF(ann_dict); - } - DISPATCH(); + INSTRUCTION_STATS(INSTRUMENTED_CALL_KW); + int is_meth = PEEK(oparg + 2) != NULL; + int total_args = oparg + is_meth; + PyObject *function = PEEK(oparg + 3); + PyObject *arg = total_args == 0 ? &_PyInstrumentation_MISSING + : PEEK(total_args + 1); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, function, arg); + if (err) goto error; + GO_TO_INSTRUCTION(CALL_KW); } - TARGET(BUILD_CONST_KEY_MAP) { - frame->instr_ptr = next_instr; + TARGET(INSTRUMENTED_END_FOR) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(BUILD_CONST_KEY_MAP); - PyObject *keys; - PyObject **values; - PyObject *map; - keys = stack_pointer[-1]; - values = stack_pointer - 1 - oparg; - if (!PyTuple_CheckExact(keys) || - PyTuple_GET_SIZE(keys) != (Py_ssize_t)oparg) { - _PyErr_SetString(tstate, PyExc_SystemError, - "bad BUILD_CONST_KEY_MAP keys argument"); - GOTO_ERROR(error); // Pop the keys and values. - } - map = _PyDict_FromItems( - &PyTuple_GET_ITEM(keys, 0), 1, - values, 1, oparg); - for (int _i = oparg; --_i >= 0;) { - Py_DECREF(values[_i]); + INSTRUCTION_STATS(INSTRUMENTED_END_FOR); + PyObject *value; + PyObject *receiver; + value = stack_pointer[-1]; + receiver = stack_pointer[-2]; + TIER_ONE_ONLY + /* Need to create a fake StopIteration error here, + * to conform to PEP 380 */ + if (PyGen_Check(receiver)) { + PyErr_SetObject(PyExc_StopIteration, value); + if (monitor_stop_iteration(tstate, frame, this_instr)) { + GOTO_ERROR(error); + } + PyErr_SetRaisedException(NULL); } - Py_DECREF(keys); - if (map == NULL) { STACK_SHRINK(oparg); goto pop_1_error; } - STACK_SHRINK(oparg); - stack_pointer[-1] = map; + Py_DECREF(receiver); + Py_DECREF(value); + STACK_SHRINK(2); DISPATCH(); } - TARGET(DICT_UPDATE) { - frame->instr_ptr = next_instr; + TARGET(INSTRUMENTED_END_SEND) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(DICT_UPDATE); - PyObject *update; - PyObject *dict; - update = stack_pointer[-1]; - dict = stack_pointer[-2 - (oparg - 1)]; - if (PyDict_Update(dict, update) < 0) { - if (_PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) { - _PyErr_Format(tstate, PyExc_TypeError, - "'%.200s' object is not a mapping", - Py_TYPE(update)->tp_name); + INSTRUCTION_STATS(INSTRUMENTED_END_SEND); + PyObject *value; + PyObject *receiver; + value = stack_pointer[-1]; + receiver = stack_pointer[-2]; + TIER_ONE_ONLY + if (PyGen_Check(receiver) || PyCoro_CheckExact(receiver)) { + PyErr_SetObject(PyExc_StopIteration, value); + if (monitor_stop_iteration(tstate, frame, this_instr)) { + GOTO_ERROR(error); } - Py_DECREF(update); - if (true) goto pop_1_error; + PyErr_SetRaisedException(NULL); } - Py_DECREF(update); + Py_DECREF(receiver); STACK_SHRINK(1); + stack_pointer[-1] = value; DISPATCH(); } - TARGET(DICT_MERGE) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(DICT_MERGE); - PyObject *update; - PyObject *dict; - PyObject *callable; - update = stack_pointer[-1]; - dict = stack_pointer[-2 - (oparg - 1)]; - callable = stack_pointer[-5 - (oparg - 1)]; - if (_PyDict_MergeEx(dict, update, 2) < 0) { - _PyEval_FormatKwargsError(tstate, callable, update); - Py_DECREF(update); - if (true) goto pop_1_error; + TARGET(INSTRUMENTED_FOR_ITER) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_FOR_ITER); + _Py_CODEUNIT *target; + PyObject *iter = TOP(); + PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter); + if (next != NULL) { + PUSH(next); + target = next_instr; } - Py_DECREF(update); - STACK_SHRINK(1); + else { + if (_PyErr_Occurred(tstate)) { + if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { + GOTO_ERROR(error); + } + monitor_raise(tstate, frame, this_instr); + _PyErr_Clear(tstate); + } + /* iterator ended normally */ + assert(next_instr[oparg].op.code == END_FOR || + next_instr[oparg].op.code == INSTRUMENTED_END_FOR); + STACK_SHRINK(1); + Py_DECREF(iter); + /* Skip END_FOR */ + target = next_instr + oparg + 1; + } + INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH); DISPATCH(); } - TARGET(MAP_ADD) { - frame->instr_ptr = next_instr; + TARGET(INSTRUMENTED_INSTRUCTION) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(MAP_ADD); - PyObject *value; - PyObject *key; - PyObject *dict; - value = stack_pointer[-1]; - key = stack_pointer[-2]; - dict = stack_pointer[-3 - (oparg - 1)]; - assert(PyDict_CheckExact(dict)); - /* dict[key] = value */ - // Do not DECREF INPUTS because the function steals the references - if (_PyDict_SetItem_Take2((PyDictObject *)dict, key, value) != 0) goto pop_2_error; - STACK_SHRINK(2); - DISPATCH(); + INSTRUCTION_STATS(INSTRUMENTED_INSTRUCTION); + int next_opcode = _Py_call_instrumentation_instruction( + tstate, frame, this_instr); + if (next_opcode < 0) goto error; + next_instr = this_instr; + if (_PyOpcode_Caches[next_opcode]) { + INCREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + } + assert(next_opcode > 0 && next_opcode < 256); + opcode = next_opcode; + DISPATCH_GOTO(); } - TARGET(INSTRUMENTED_LOAD_SUPER_ATTR) { + TARGET(INSTRUMENTED_JUMP_BACKWARD) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 2; - INSTRUCTION_STATS(INSTRUMENTED_LOAD_SUPER_ATTR); - // cancel out the decrement that will happen in LOAD_SUPER_ATTR; we + INSTRUCTION_STATS(INSTRUMENTED_JUMP_BACKWARD); + CHECK_EVAL_BREAKER(); + INSTRUMENTED_JUMP(this_instr, next_instr - oparg, PY_MONITORING_EVENT_JUMP); + DISPATCH(); + } + + TARGET(INSTRUMENTED_JUMP_FORWARD) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_JUMP_FORWARD); + INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_JUMP); + DISPATCH(); + } + + TARGET(INSTRUMENTED_LOAD_SUPER_ATTR) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_LOAD_SUPER_ATTR); + // cancel out the decrement that will happen in LOAD_SUPER_ATTR; we // don't want to specialize instrumented instructions INCREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); GO_TO_INSTRUCTION(LOAD_SUPER_ATTR); } - TARGET(LOAD_SUPER_ATTR) { - frame->instr_ptr = next_instr; + TARGET(INSTRUMENTED_POP_JUMP_IF_FALSE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 2; - INSTRUCTION_STATS(LOAD_SUPER_ATTR); - PREDICTED(LOAD_SUPER_ATTR); - _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR == 1, "incorrect cache size"); - PyObject *class; - PyObject *global_super; - PyObject *self; - PyObject *attr; - PyObject *null = NULL; - // _SPECIALIZE_LOAD_SUPER_ATTR - class = stack_pointer[-2]; - global_super = stack_pointer[-3]; - { - uint16_t counter = read_u16(&this_instr[1].cache); - TIER_ONE_ONLY - #if ENABLE_SPECIALIZATION - int load_method = oparg & 1; - if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - next_instr = this_instr; - _Py_Specialize_LoadSuperAttr(global_super, class, next_instr, load_method); - DISPATCH_SAME_OPARG(); - } - STAT_INC(LOAD_SUPER_ATTR, deferred); - DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - #endif /* ENABLE_SPECIALIZATION */ - } - // _LOAD_SUPER_ATTR - self = stack_pointer[-1]; - { - TIER_ONE_ONLY - if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { - PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_CALL, - frame, this_instr, global_super, arg); - if (err) goto pop_3_error; - } - // we make no attempt to optimize here; specializations should - // handle any case whose performance we care about - PyObject *stack[] = {class, self}; - PyObject *super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL); - if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { - PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; - if (super == NULL) { - _Py_call_instrumentation_exc2( - tstate, PY_MONITORING_EVENT_C_RAISE, - frame, this_instr, global_super, arg); - } - else { - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_C_RETURN, - frame, this_instr, global_super, arg); - if (err < 0) { - Py_CLEAR(super); - } - } - } - Py_DECREF(global_super); - Py_DECREF(class); - Py_DECREF(self); - if (super == NULL) goto pop_3_error; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); - attr = PyObject_GetAttr(super, name); - Py_DECREF(super); - if (attr == NULL) goto pop_3_error; - null = NULL; - } - STACK_SHRINK(2); - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_FALSE); + PyObject *cond = POP(); + assert(PyBool_Check(cond)); + int flag = Py_IsFalse(cond); + int offset = flag * oparg; + #if ENABLE_SPECIALIZATION + this_instr[1].cache = (this_instr[1].cache << 1) | flag; + #endif + INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); DISPATCH(); } - TARGET(LOAD_SUPER_ATTR_ATTR) { - frame->instr_ptr = next_instr; + TARGET(INSTRUMENTED_POP_JUMP_IF_NONE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 2; - INSTRUCTION_STATS(LOAD_SUPER_ATTR_ATTR); - PyObject *self; - PyObject *class; - PyObject *global_super; - PyObject *attr; - self = stack_pointer[-1]; - class = stack_pointer[-2]; - global_super = stack_pointer[-3]; - assert(!(oparg & 1)); - DEOPT_IF(global_super != (PyObject *)&PySuper_Type, LOAD_SUPER_ATTR); - DEOPT_IF(!PyType_Check(class), LOAD_SUPER_ATTR); - STAT_INC(LOAD_SUPER_ATTR, hit); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); - attr = _PySuper_Lookup((PyTypeObject *)class, self, name, NULL); - Py_DECREF(global_super); - Py_DECREF(class); - Py_DECREF(self); - if (attr == NULL) goto pop_3_error; - STACK_SHRINK(2); - stack_pointer[-1] = attr; + INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NONE); + PyObject *value = POP(); + int flag = Py_IsNone(value); + int offset; + if (flag) { + offset = oparg; + } + else { + Py_DECREF(value); + offset = 0; + } + #if ENABLE_SPECIALIZATION + this_instr[1].cache = (this_instr[1].cache << 1) | flag; + #endif + INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); DISPATCH(); } - TARGET(LOAD_SUPER_ATTR_METHOD) { - frame->instr_ptr = next_instr; + TARGET(INSTRUMENTED_POP_JUMP_IF_NOT_NONE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 2; - INSTRUCTION_STATS(LOAD_SUPER_ATTR_METHOD); - PyObject *self; - PyObject *class; - PyObject *global_super; - PyObject *attr; - PyObject *self_or_null; - self = stack_pointer[-1]; - class = stack_pointer[-2]; - global_super = stack_pointer[-3]; - assert(oparg & 1); - DEOPT_IF(global_super != (PyObject *)&PySuper_Type, LOAD_SUPER_ATTR); - DEOPT_IF(!PyType_Check(class), LOAD_SUPER_ATTR); - STAT_INC(LOAD_SUPER_ATTR, hit); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); - PyTypeObject *cls = (PyTypeObject *)class; - int method_found = 0; - attr = _PySuper_Lookup(cls, self, name, - Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL); - Py_DECREF(global_super); - Py_DECREF(class); - if (attr == NULL) { - Py_DECREF(self); - if (true) goto pop_3_error; + INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NOT_NONE); + PyObject *value = POP(); + int offset; + int nflag = Py_IsNone(value); + if (nflag) { + offset = 0; } - if (method_found) { - self_or_null = self; // transfer ownership - } else { - Py_DECREF(self); - self_or_null = NULL; + else { + Py_DECREF(value); + offset = oparg; } - STACK_SHRINK(1); - stack_pointer[-2] = attr; - stack_pointer[-1] = self_or_null; + #if ENABLE_SPECIALIZATION + this_instr[1].cache = (this_instr[1].cache << 1) | !nflag; + #endif + INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); DISPATCH(); } - TARGET(LOAD_ATTR) { - frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR); - PREDICTED(LOAD_ATTR); - _Py_CODEUNIT *this_instr = next_instr - 10; - static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); - PyObject *owner; - PyObject *attr; - PyObject *self_or_null = NULL; - // _SPECIALIZE_LOAD_ATTR - owner = stack_pointer[-1]; - { - uint16_t counter = read_u16(&this_instr[1].cache); - TIER_ONE_ONLY - #if ENABLE_SPECIALIZATION - if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); + TARGET(INSTRUMENTED_POP_JUMP_IF_TRUE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_TRUE); + PyObject *cond = POP(); + assert(PyBool_Check(cond)); + int flag = Py_IsTrue(cond); + int offset = flag * oparg; + #if ENABLE_SPECIALIZATION + this_instr[1].cache = (this_instr[1].cache << 1) | flag; + #endif + INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + DISPATCH(); + } + + TARGET(INSTRUMENTED_RESUME) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_RESUME); + uintptr_t global_version = _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker) & ~_PY_EVAL_EVENTS_MASK; + uintptr_t code_version = _PyFrame_GetCode(frame)->_co_instrumentation_version; + if (code_version != global_version) { + if (_Py_Instrument(_PyFrame_GetCode(frame), tstate->interp)) { + GOTO_ERROR(error); + } + next_instr = this_instr; + } + else { + if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { + CHECK_EVAL_BREAKER(); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation( + tstate, oparg > 0, frame, this_instr); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) goto error; + if (frame->instr_ptr != this_instr) { + /* Instrumentation has jumped */ next_instr = this_instr; - _Py_Specialize_LoadAttr(owner, next_instr, name); - DISPATCH_SAME_OPARG(); + DISPATCH(); } - STAT_INC(LOAD_ATTR, deferred); - DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - #endif /* ENABLE_SPECIALIZATION */ } - // _LOAD_ATTR - { - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); - if (oparg & 1) { + DISPATCH(); + } + + TARGET(INSTRUMENTED_RETURN_CONST) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_RETURN_CONST); + PyObject *retval = GETITEM(FRAME_CO_CONSTS, oparg); + int err = _Py_call_instrumentation_arg( + tstate, PY_MONITORING_EVENT_PY_RETURN, + frame, this_instr, retval); + if (err) GOTO_ERROR(error); + Py_INCREF(retval); + assert(EMPTY()); + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_LeaveRecursiveCallPy(tstate); + assert(frame != &entry_frame); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = tstate->current_frame = dying->previous; + _PyEval_FrameClearAndPop(tstate, dying); + _PyFrame_StackPush(frame, retval); + LOAD_IP(frame->return_offset); + goto resume_frame; + } + + TARGET(INSTRUMENTED_RETURN_VALUE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_RETURN_VALUE); + PyObject *retval; + retval = stack_pointer[-1]; + int err = _Py_call_instrumentation_arg( + tstate, PY_MONITORING_EVENT_PY_RETURN, + frame, this_instr, retval); + if (err) GOTO_ERROR(error); + STACK_SHRINK(1); + assert(EMPTY()); + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_LeaveRecursiveCallPy(tstate); + assert(frame != &entry_frame); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = tstate->current_frame = dying->previous; + _PyEval_FrameClearAndPop(tstate, dying); + _PyFrame_StackPush(frame, retval); + LOAD_IP(frame->return_offset); + goto resume_frame; + } + + TARGET(INSTRUMENTED_YIELD_VALUE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_YIELD_VALUE); + PyObject *retval; + retval = stack_pointer[-1]; + assert(frame != &entry_frame); + frame->instr_ptr = next_instr; + PyGenObject *gen = _PyFrame_GetGenerator(frame); + assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); + assert(oparg == 0 || oparg == 1); + gen->gi_frame_state = FRAME_SUSPENDED + oparg; + _PyFrame_SetStackPointer(frame, stack_pointer - 1); + int err = _Py_call_instrumentation_arg( + tstate, PY_MONITORING_EVENT_PY_YIELD, + frame, this_instr, retval); + if (err) GOTO_ERROR(error); + tstate->exc_info = gen->gi_exc_state.previous_item; + gen->gi_exc_state.previous_item = NULL; + _Py_LeaveRecursiveCallPy(tstate); + _PyInterpreterFrame *gen_frame = frame; + frame = tstate->current_frame = frame->previous; + gen_frame->previous = NULL; + _PyFrame_StackPush(frame, retval); + /* We don't know which of these is relevant here, so keep them equal */ + assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); + LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); + goto resume_frame; + } + + TARGET(INTERPRETER_EXIT) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INTERPRETER_EXIT); + PyObject *retval; + retval = stack_pointer[-1]; + assert(frame == &entry_frame); + assert(_PyFrame_IsIncomplete(frame)); + /* Restore previous frame and return. */ + tstate->current_frame = frame->previous; + assert(!_PyErr_Occurred(tstate)); + tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS; + return retval; + } + + TARGET(IS_OP) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(IS_OP); + PyObject *right; + PyObject *left; + PyObject *b; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + int res = Py_Is(left, right) ^ oparg; + Py_DECREF(left); + Py_DECREF(right); + b = res ? Py_True : Py_False; + STACK_SHRINK(1); + stack_pointer[-1] = b; + DISPATCH(); + } + + TARGET(JUMP_BACKWARD) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(JUMP_BACKWARD); + CHECK_EVAL_BREAKER(); + assert(oparg <= INSTR_OFFSET()); + JUMPBY(-oparg); + #if ENABLE_SPECIALIZATION + this_instr[1].cache += (1 << OPTIMIZER_BITS_IN_COUNTER); + /* We are using unsigned values, but we really want signed values, so + * do the 2s complement comparison manually */ + uint16_t ucounter = this_instr[1].cache + (1 << 15); + uint16_t threshold = tstate->interp->optimizer_backedge_threshold + (1 << 15); + // Double-check that the opcode isn't instrumented or something: + if (ucounter > threshold && this_instr->op.code == JUMP_BACKWARD) { + OPT_STAT_INC(attempts); + int optimized = _PyOptimizer_BackEdge(frame, this_instr, next_instr, stack_pointer); + if (optimized < 0) goto error; + if (optimized) { + // Rewind and enter the executor: + assert(this_instr->op.code == ENTER_EXECUTOR); + next_instr = this_instr; + this_instr[1].cache &= ((1 << OPTIMIZER_BITS_IN_COUNTER) - 1); + } + else { + int backoff = this_instr[1].cache & ((1 << OPTIMIZER_BITS_IN_COUNTER) - 1); + if (backoff < MINIMUM_TIER2_BACKOFF) { + backoff = MINIMUM_TIER2_BACKOFF; + } + else if (backoff < 15 - OPTIMIZER_BITS_IN_COUNTER) { + backoff++; + } + assert(backoff <= 15 - OPTIMIZER_BITS_IN_COUNTER); + this_instr[1].cache = ((1 << 16) - ((1 << OPTIMIZER_BITS_IN_COUNTER) << backoff)) | backoff; + } + } + #endif /* ENABLE_SPECIALIZATION */ + DISPATCH(); + } + + TARGET(JUMP_BACKWARD_NO_INTERRUPT) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(JUMP_BACKWARD_NO_INTERRUPT); + /* This bytecode is used in the `yield from` or `await` loop. + * If there is an interrupt, we want it handled in the innermost + * generator or coroutine, so we deliberately do not check it here. + * (see bpo-30039). + */ + JUMPBY(-oparg); + DISPATCH(); + } + + TARGET(JUMP_FORWARD) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(JUMP_FORWARD); + JUMPBY(oparg); + DISPATCH(); + } + + TARGET(LIST_APPEND) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LIST_APPEND); + PyObject *v; + PyObject *list; + v = stack_pointer[-1]; + list = stack_pointer[-2 - (oparg-1)]; + if (_PyList_AppendTakeRef((PyListObject *)list, v) < 0) goto pop_1_error; + STACK_SHRINK(1); + DISPATCH(); + } + + TARGET(LIST_EXTEND) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LIST_EXTEND); + PyObject *iterable; + PyObject *list; + iterable = stack_pointer[-1]; + list = stack_pointer[-2 - (oparg-1)]; + PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable); + if (none_val == NULL) { + if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError) && + (Py_TYPE(iterable)->tp_iter == NULL && !PySequence_Check(iterable))) + { + _PyErr_Clear(tstate); + _PyErr_Format(tstate, PyExc_TypeError, + "Value after * must be an iterable, not %.200s", + Py_TYPE(iterable)->tp_name); + } + Py_DECREF(iterable); + if (true) goto pop_1_error; + } + assert(Py_IsNone(none_val)); + Py_DECREF(iterable); + STACK_SHRINK(1); + DISPATCH(); + } + + TARGET(LOAD_ASSERTION_ERROR) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_ASSERTION_ERROR); + PyObject *value; + value = Py_NewRef(PyExc_AssertionError); + STACK_GROW(1); + stack_pointer[-1] = value; + DISPATCH(); + } + + TARGET(LOAD_ATTR) { + frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR); + PREDICTED(LOAD_ATTR); + _Py_CODEUNIT *this_instr = next_instr - 10; + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + PyObject *owner; + PyObject *attr; + PyObject *self_or_null = NULL; + // _SPECIALIZE_LOAD_ATTR + owner = stack_pointer[-1]; + { + uint16_t counter = read_u16(&this_instr[1].cache); + TIER_ONE_ONLY + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); + next_instr = this_instr; + _Py_Specialize_LoadAttr(owner, next_instr, name); + DISPATCH_SAME_OPARG(); + } + STAT_INC(LOAD_ATTR, deferred); + DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + #endif /* ENABLE_SPECIALIZATION */ + } + // _LOAD_ATTR + { + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); + if (oparg & 1) { /* Designed to work in tandem with CALL, pushes two values. */ attr = NULL; if (_PyObject_GetMethod(owner, name, &attr)) { @@ -2730,7 +3393,71 @@ DISPATCH(); } - TARGET(LOAD_ATTR_INSTANCE_VALUE) { + TARGET(LOAD_ATTR_CLASS) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_CLASS); + PyObject *owner; + PyObject *attr; + PyObject *null = NULL; + // _CHECK_ATTR_CLASS + owner = stack_pointer[-1]; + { + uint32_t type_version = read_u32(&this_instr[2].cache); + DEOPT_IF(!PyType_Check(owner), LOAD_ATTR); + assert(type_version != 0); + DEOPT_IF(((PyTypeObject *)owner)->tp_version_tag != type_version, LOAD_ATTR); + } + // _LOAD_ATTR_CLASS + { + PyObject *descr = read_obj(&this_instr[6].cache); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + attr = Py_NewRef(descr); + null = NULL; + Py_DECREF(owner); + } + STACK_GROW(((oparg & 1) ? 1 : 0)); + stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; + if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + DISPATCH(); + } + + TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN); + PyObject *owner; + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + uint32_t func_version = read_u32(&this_instr[4].cache); + PyObject *getattribute = read_obj(&this_instr[6].cache); + assert((oparg & 1) == 0); + DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); + PyTypeObject *cls = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR); + assert(Py_IS_TYPE(getattribute, &PyFunction_Type)); + PyFunctionObject *f = (PyFunctionObject *)getattribute; + assert(func_version != 0); + DEOPT_IF(f->func_version != func_version, LOAD_ATTR); + PyCodeObject *code = (PyCodeObject *)f->func_code; + assert(code->co_argcount == 2); + DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR); + STAT_INC(LOAD_ATTR, hit); + + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); + Py_INCREF(f); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 2); + // Manipulate stack directly because we exit with DISPATCH_INLINED(). + STACK_SHRINK(1); + new_frame->localsplus[0] = owner; + new_frame->localsplus[1] = Py_NewRef(name); + frame->return_offset = (uint16_t)(next_instr - this_instr); + DISPATCH_INLINED(new_frame); + } + + TARGET(LOAD_ATTR_INSTANCE_VALUE) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_INSTANCE_VALUE); @@ -2769,49 +3496,52 @@ DISPATCH(); } - TARGET(LOAD_ATTR_MODULE) { + TARGET(LOAD_ATTR_METHOD_LAZY_DICT) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_MODULE); + INSTRUCTION_STATS(LOAD_ATTR_METHOD_LAZY_DICT); PyObject *owner; PyObject *attr; - PyObject *null = NULL; - // _CHECK_ATTR_MODULE + PyObject *self; + // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; { uint32_t type_version = read_u32(&this_instr[2].cache); - DEOPT_IF(!PyModule_CheckExact(owner), LOAD_ATTR); - PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; - assert(dict != NULL); - DEOPT_IF(dict->ma_keys->dk_version != type_version, LOAD_ATTR); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); } - // _LOAD_ATTR_MODULE + // _CHECK_ATTR_METHOD_LAZY_DICT { - uint16_t index = read_u16(&this_instr[4].cache); - PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; - assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); - assert(index < dict->ma_keys->dk_nentries); - PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index; - attr = ep->me_value; - DEOPT_IF(attr == NULL, LOAD_ATTR); + Py_ssize_t dictoffset = Py_TYPE(owner)->tp_dictoffset; + assert(dictoffset > 0); + PyObject *dict = *(PyObject **)((char *)owner + dictoffset); + /* This object has a __dict__, just not yet created */ + DEOPT_IF(dict != NULL, LOAD_ATTR); + } + // _LOAD_ATTR_METHOD_LAZY_DICT + { + PyObject *descr = read_obj(&this_instr[6].cache); + assert(oparg & 1); STAT_INC(LOAD_ATTR, hit); - Py_INCREF(attr); - null = NULL; - Py_DECREF(owner); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = Py_NewRef(descr); + self = owner; } - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + STACK_GROW(1); + stack_pointer[-2] = attr; + stack_pointer[-1] = self; DISPATCH(); } - TARGET(LOAD_ATTR_WITH_HINT) { + TARGET(LOAD_ATTR_METHOD_NO_DICT) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_WITH_HINT); + INSTRUCTION_STATS(LOAD_ATTR_METHOD_NO_DICT); PyObject *owner; PyObject *attr; - PyObject *null = NULL; + PyObject *self; // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; { @@ -2820,51 +3550,30 @@ assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); } - // _CHECK_ATTR_WITH_HINT - { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(_PyDictOrValues_IsValues(dorv), LOAD_ATTR); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); - DEOPT_IF(dict == NULL, LOAD_ATTR); - assert(PyDict_CheckExact((PyObject *)dict)); - } - // _LOAD_ATTR_WITH_HINT + // _LOAD_ATTR_METHOD_NO_DICT { - uint16_t hint = read_u16(&this_instr[4].cache); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); - DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); - if (DK_IS_UNICODE(dict->ma_keys)) { - PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; - DEOPT_IF(ep->me_key != name, LOAD_ATTR); - attr = ep->me_value; - } - else { - PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + hint; - DEOPT_IF(ep->me_key != name, LOAD_ATTR); - attr = ep->me_value; - } - DEOPT_IF(attr == NULL, LOAD_ATTR); + PyObject *descr = read_obj(&this_instr[6].cache); + assert(oparg & 1); + assert(Py_TYPE(owner)->tp_dictoffset == 0); STAT_INC(LOAD_ATTR, hit); - Py_INCREF(attr); - null = NULL; - Py_DECREF(owner); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = Py_NewRef(descr); + self = owner; } - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + STACK_GROW(1); + stack_pointer[-2] = attr; + stack_pointer[-1] = self; DISPATCH(); } - TARGET(LOAD_ATTR_SLOT) { + TARGET(LOAD_ATTR_METHOD_WITH_VALUES) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_SLOT); + INSTRUCTION_STATS(LOAD_ATTR_METHOD_WITH_VALUES); PyObject *owner; PyObject *attr; - PyObject *null = NULL; + PyObject *self; // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; { @@ -2873,11 +3582,60 @@ assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); } - // _LOAD_ATTR_SLOT + // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT + { + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); + DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), LOAD_ATTR); + } + // _GUARD_KEYS_VERSION + { + uint32_t keys_version = read_u32(&this_instr[4].cache); + PyTypeObject *owner_cls = Py_TYPE(owner); + PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; + DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version, LOAD_ATTR); + } + // _LOAD_ATTR_METHOD_WITH_VALUES + { + PyObject *descr = read_obj(&this_instr[6].cache); + assert(oparg & 1); + /* Cached method object */ + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + attr = Py_NewRef(descr); + assert(_PyType_HasFeature(Py_TYPE(attr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + self = owner; + } + STACK_GROW(1); + stack_pointer[-2] = attr; + stack_pointer[-1] = self; + DISPATCH(); + } + + TARGET(LOAD_ATTR_MODULE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_MODULE); + PyObject *owner; + PyObject *attr; + PyObject *null = NULL; + // _CHECK_ATTR_MODULE + owner = stack_pointer[-1]; + { + uint32_t type_version = read_u32(&this_instr[2].cache); + DEOPT_IF(!PyModule_CheckExact(owner), LOAD_ATTR); + PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; + assert(dict != NULL); + DEOPT_IF(dict->ma_keys->dk_version != type_version, LOAD_ATTR); + } + // _LOAD_ATTR_MODULE { uint16_t index = read_u16(&this_instr[4].cache); - char *addr = (char *)owner + index; - attr = *(PyObject **)addr; + PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; + assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); + assert(index < dict->ma_keys->dk_nentries); + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index; + attr = ep->me_value; DEOPT_IF(attr == NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(attr); @@ -2890,2874 +3648,2116 @@ DISPATCH(); } - TARGET(LOAD_ATTR_CLASS) { + TARGET(LOAD_ATTR_NONDESCRIPTOR_NO_DICT) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_CLASS); + INSTRUCTION_STATS(LOAD_ATTR_NONDESCRIPTOR_NO_DICT); PyObject *owner; PyObject *attr; - PyObject *null = NULL; - // _CHECK_ATTR_CLASS + // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; { uint32_t type_version = read_u32(&this_instr[2].cache); - DEOPT_IF(!PyType_Check(owner), LOAD_ATTR); + PyTypeObject *tp = Py_TYPE(owner); assert(type_version != 0); - DEOPT_IF(((PyTypeObject *)owner)->tp_version_tag != type_version, LOAD_ATTR); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); } - // _LOAD_ATTR_CLASS + // _LOAD_ATTR_NONDESCRIPTOR_NO_DICT { PyObject *descr = read_obj(&this_instr[6].cache); + assert((oparg & 1) == 0); + assert(Py_TYPE(owner)->tp_dictoffset == 0); STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); - attr = Py_NewRef(descr); - null = NULL; Py_DECREF(owner); + attr = Py_NewRef(descr); } - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + stack_pointer[-1] = attr; DISPATCH(); } - TARGET(LOAD_ATTR_PROPERTY) { + TARGET(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_PROPERTY); + INSTRUCTION_STATS(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES); PyObject *owner; + PyObject *attr; + // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; - uint32_t type_version = read_u32(&this_instr[2].cache); - uint32_t func_version = read_u32(&this_instr[4].cache); - PyObject *fget = read_obj(&this_instr[6].cache); - assert((oparg & 1) == 0); - DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); - - PyTypeObject *cls = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR); - assert(Py_IS_TYPE(fget, &PyFunction_Type)); - PyFunctionObject *f = (PyFunctionObject *)fget; - assert(func_version != 0); - DEOPT_IF(f->func_version != func_version, LOAD_ATTR); - PyCodeObject *code = (PyCodeObject *)f->func_code; - assert(code->co_argcount == 1); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR); - STAT_INC(LOAD_ATTR, hit); - Py_INCREF(fget); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 1); - // Manipulate stack directly because we exit with DISPATCH_INLINED(). - STACK_SHRINK(1); - new_frame->localsplus[0] = owner; - frame->return_offset = (uint16_t)(next_instr - this_instr); - DISPATCH_INLINED(new_frame); + { + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + } + // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT + { + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); + DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), LOAD_ATTR); + } + // _GUARD_KEYS_VERSION + { + uint32_t keys_version = read_u32(&this_instr[4].cache); + PyTypeObject *owner_cls = Py_TYPE(owner); + PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; + DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version, LOAD_ATTR); + } + // _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES + { + PyObject *descr = read_obj(&this_instr[6].cache); + assert((oparg & 1) == 0); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + Py_DECREF(owner); + attr = Py_NewRef(descr); + } + stack_pointer[-1] = attr; + DISPATCH(); } - TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) { + TARGET(LOAD_ATTR_PROPERTY) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN); + INSTRUCTION_STATS(LOAD_ATTR_PROPERTY); PyObject *owner; owner = stack_pointer[-1]; uint32_t type_version = read_u32(&this_instr[2].cache); uint32_t func_version = read_u32(&this_instr[4].cache); - PyObject *getattribute = read_obj(&this_instr[6].cache); + PyObject *fget = read_obj(&this_instr[6].cache); assert((oparg & 1) == 0); DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); + PyTypeObject *cls = Py_TYPE(owner); assert(type_version != 0); DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR); - assert(Py_IS_TYPE(getattribute, &PyFunction_Type)); - PyFunctionObject *f = (PyFunctionObject *)getattribute; + assert(Py_IS_TYPE(fget, &PyFunction_Type)); + PyFunctionObject *f = (PyFunctionObject *)fget; assert(func_version != 0); DEOPT_IF(f->func_version != func_version, LOAD_ATTR); PyCodeObject *code = (PyCodeObject *)f->func_code; - assert(code->co_argcount == 2); + assert(code->co_argcount == 1); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); - - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); - Py_INCREF(f); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 2); + Py_INCREF(fget); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 1); // Manipulate stack directly because we exit with DISPATCH_INLINED(). STACK_SHRINK(1); new_frame->localsplus[0] = owner; - new_frame->localsplus[1] = Py_NewRef(name); frame->return_offset = (uint16_t)(next_instr - this_instr); DISPATCH_INLINED(new_frame); } - TARGET(STORE_ATTR_INSTANCE_VALUE) { + TARGET(LOAD_ATTR_SLOT) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 5; - INSTRUCTION_STATS(STORE_ATTR_INSTANCE_VALUE); + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_SLOT); PyObject *owner; - PyObject *value; + PyObject *attr; + PyObject *null = NULL; // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; { uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(owner); assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); - } - // _GUARD_DORV_VALUES - { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(dorv), STORE_ATTR); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); } - // _STORE_ATTR_INSTANCE_VALUE - value = stack_pointer[-2]; + // _LOAD_ATTR_SLOT { uint16_t index = read_u16(&this_instr[4].cache); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - STAT_INC(STORE_ATTR, hit); - PyDictValues *values = _PyDictOrValues_GetValues(dorv); - PyObject *old_value = values->values[index]; - values->values[index] = value; - if (old_value == NULL) { - _PyDictValues_AddToInsertionOrder(values, index); - } - else { - Py_DECREF(old_value); - } + char *addr = (char *)owner + index; + attr = *(PyObject **)addr; + DEOPT_IF(attr == NULL, LOAD_ATTR); + STAT_INC(LOAD_ATTR, hit); + Py_INCREF(attr); + null = NULL; Py_DECREF(owner); } - STACK_SHRINK(2); - DISPATCH(); - } - - TARGET(STORE_ATTR_WITH_HINT) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 5; - INSTRUCTION_STATS(STORE_ATTR_WITH_HINT); - PyObject *owner; - PyObject *value; - owner = stack_pointer[-1]; - value = stack_pointer[-2]; - uint32_t type_version = read_u32(&this_instr[2].cache); - uint16_t hint = read_u16(&this_instr[4].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); - assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(_PyDictOrValues_IsValues(dorv), STORE_ATTR); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); - DEOPT_IF(dict == NULL, STORE_ATTR); - assert(PyDict_CheckExact((PyObject *)dict)); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR); - PyObject *old_value; - uint64_t new_version; - if (DK_IS_UNICODE(dict->ma_keys)) { - PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; - DEOPT_IF(ep->me_key != name, STORE_ATTR); - old_value = ep->me_value; - DEOPT_IF(old_value == NULL, STORE_ATTR); - new_version = _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, value); - ep->me_value = value; - } - else { - PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + hint; - DEOPT_IF(ep->me_key != name, STORE_ATTR); - old_value = ep->me_value; - DEOPT_IF(old_value == NULL, STORE_ATTR); - new_version = _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, value); - ep->me_value = value; - } - Py_DECREF(old_value); - STAT_INC(STORE_ATTR, hit); - /* Ensure dict is GC tracked if it needs to be */ - if (!_PyObject_GC_IS_TRACKED(dict) && _PyObject_GC_MAY_BE_TRACKED(value)) { - _PyObject_GC_TRACK(dict); - } - /* PEP 509 */ - dict->ma_version_tag = new_version; - Py_DECREF(owner); - STACK_SHRINK(2); + STACK_GROW(((oparg & 1) ? 1 : 0)); + stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; + if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } DISPATCH(); } - TARGET(STORE_ATTR_SLOT) { + TARGET(LOAD_ATTR_WITH_HINT) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 5; - INSTRUCTION_STATS(STORE_ATTR_SLOT); + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_WITH_HINT); PyObject *owner; - PyObject *value; + PyObject *attr; + PyObject *null = NULL; // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; { uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(owner); assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); } - // _STORE_ATTR_SLOT - value = stack_pointer[-2]; + // _CHECK_ATTR_WITH_HINT { - uint16_t index = read_u16(&this_instr[4].cache); - char *addr = (char *)owner + index; - STAT_INC(STORE_ATTR, hit); - PyObject *old_value = *(PyObject **)addr; - *(PyObject **)addr = value; - Py_XDECREF(old_value); - Py_DECREF(owner); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + DEOPT_IF(_PyDictOrValues_IsValues(dorv), LOAD_ATTR); + PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + DEOPT_IF(dict == NULL, LOAD_ATTR); + assert(PyDict_CheckExact((PyObject *)dict)); } - STACK_SHRINK(2); - DISPATCH(); - } - - TARGET(COMPARE_OP) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(COMPARE_OP); - PREDICTED(COMPARE_OP); - _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_COMPARE_OP == 1, "incorrect cache size"); - PyObject *right; - PyObject *left; - PyObject *res; - // _SPECIALIZE_COMPARE_OP - right = stack_pointer[-1]; - left = stack_pointer[-2]; + // _LOAD_ATTR_WITH_HINT { - uint16_t counter = read_u16(&this_instr[1].cache); - TIER_ONE_ONLY - #if ENABLE_SPECIALIZATION - if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - next_instr = this_instr; - _Py_Specialize_CompareOp(left, right, next_instr, oparg); - DISPATCH_SAME_OPARG(); + uint16_t hint = read_u16(&this_instr[4].cache); + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); + if (DK_IS_UNICODE(dict->ma_keys)) { + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; + DEOPT_IF(ep->me_key != name, LOAD_ATTR); + attr = ep->me_value; } - STAT_INC(COMPARE_OP, deferred); - DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - #endif /* ENABLE_SPECIALIZATION */ - } - // _COMPARE_OP - { - assert((oparg >> 5) <= Py_GE); - res = PyObject_RichCompare(left, right, oparg >> 5); - Py_DECREF(left); - Py_DECREF(right); - if (res == NULL) goto pop_2_error; - if (oparg & 16) { - int res_bool = PyObject_IsTrue(res); - Py_DECREF(res); - if (res_bool < 0) goto pop_2_error; - res = res_bool ? Py_True : Py_False; + else { + PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + hint; + DEOPT_IF(ep->me_key != name, LOAD_ATTR); + attr = ep->me_value; } + DEOPT_IF(attr == NULL, LOAD_ATTR); + STAT_INC(LOAD_ATTR, hit); + Py_INCREF(attr); + null = NULL; + Py_DECREF(owner); } - STACK_SHRINK(1); - stack_pointer[-1] = res; + STACK_GROW(((oparg & 1) ? 1 : 0)); + stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; + if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } DISPATCH(); } - TARGET(COMPARE_OP_FLOAT) { + TARGET(LOAD_BUILD_CLASS) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(COMPARE_OP_FLOAT); - PyObject *right; - PyObject *left; - PyObject *res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_OP); - DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_OP); - STAT_INC(COMPARE_OP, hit); - double dleft = PyFloat_AS_DOUBLE(left); - double dright = PyFloat_AS_DOUBLE(right); - // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg - int sign_ish = COMPARISON_BIT(dleft, dright); - _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); - _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); - res = (sign_ish & oparg) ? Py_True : Py_False; - // It's always a bool, so we don't care about oparg & 16. - STACK_SHRINK(1); - stack_pointer[-1] = res; + next_instr += 1; + INSTRUCTION_STATS(LOAD_BUILD_CLASS); + PyObject *bc; + if (PyMapping_GetOptionalItem(BUILTINS(), &_Py_ID(__build_class__), &bc) < 0) goto error; + if (bc == NULL) { + _PyErr_SetString(tstate, PyExc_NameError, + "__build_class__ not found"); + if (true) goto error; + } + STACK_GROW(1); + stack_pointer[-1] = bc; DISPATCH(); } - TARGET(COMPARE_OP_INT) { + TARGET(LOAD_CONST) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(COMPARE_OP_INT); - PyObject *right; - PyObject *left; - PyObject *res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - DEOPT_IF(!PyLong_CheckExact(left), COMPARE_OP); - DEOPT_IF(!PyLong_CheckExact(right), COMPARE_OP); - DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)left), COMPARE_OP); - DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)right), COMPARE_OP); - STAT_INC(COMPARE_OP, hit); - assert(_PyLong_DigitCount((PyLongObject *)left) <= 1 && - _PyLong_DigitCount((PyLongObject *)right) <= 1); - Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left); - Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right); - // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg - int sign_ish = COMPARISON_BIT(ileft, iright); - _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); - res = (sign_ish & oparg) ? Py_True : Py_False; - // It's always a bool, so we don't care about oparg & 16. - STACK_SHRINK(1); - stack_pointer[-1] = res; + next_instr += 1; + INSTRUCTION_STATS(LOAD_CONST); + PyObject *value; + value = GETITEM(FRAME_CO_CONSTS, oparg); + Py_INCREF(value); + STACK_GROW(1); + stack_pointer[-1] = value; DISPATCH(); } - TARGET(COMPARE_OP_STR) { + TARGET(LOAD_DEREF) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(COMPARE_OP_STR); - PyObject *right; - PyObject *left; - PyObject *res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_OP); - DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_OP); - STAT_INC(COMPARE_OP, hit); - int eq = _PyUnicode_Equal(left, right); - assert((oparg >> 5) == Py_EQ || (oparg >> 5) == Py_NE); - _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); - _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); - assert(eq == 0 || eq == 1); - assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); - assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); - res = ((COMPARISON_NOT_EQUALS + eq) & oparg) ? Py_True : Py_False; - // It's always a bool, so we don't care about oparg & 16. - STACK_SHRINK(1); - stack_pointer[-1] = res; + next_instr += 1; + INSTRUCTION_STATS(LOAD_DEREF); + PyObject *value; + PyObject *cell = GETLOCAL(oparg); + value = PyCell_GET(cell); + if (value == NULL) { + _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); + if (true) goto error; + } + Py_INCREF(value); + STACK_GROW(1); + stack_pointer[-1] = value; DISPATCH(); } - TARGET(IS_OP) { + TARGET(LOAD_FAST) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(IS_OP); - PyObject *right; - PyObject *left; - PyObject *b; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - int res = Py_Is(left, right) ^ oparg; - Py_DECREF(left); - Py_DECREF(right); - b = res ? Py_True : Py_False; - STACK_SHRINK(1); - stack_pointer[-1] = b; + INSTRUCTION_STATS(LOAD_FAST); + PyObject *value; + value = GETLOCAL(oparg); + assert(value != NULL); + Py_INCREF(value); + STACK_GROW(1); + stack_pointer[-1] = value; DISPATCH(); } - TARGET(CONTAINS_OP) { + TARGET(LOAD_FAST_AND_CLEAR) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(CONTAINS_OP); - PyObject *right; - PyObject *left; - PyObject *b; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - int res = PySequence_Contains(right, left); - Py_DECREF(left); - Py_DECREF(right); - if (res < 0) goto pop_2_error; - b = (res ^ oparg) ? Py_True : Py_False; - STACK_SHRINK(1); - stack_pointer[-1] = b; + INSTRUCTION_STATS(LOAD_FAST_AND_CLEAR); + PyObject *value; + value = GETLOCAL(oparg); + // do not use SETLOCAL here, it decrefs the old value + GETLOCAL(oparg) = NULL; + STACK_GROW(1); + stack_pointer[-1] = value; DISPATCH(); } - TARGET(CHECK_EG_MATCH) { + TARGET(LOAD_FAST_CHECK) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(CHECK_EG_MATCH); - PyObject *match_type; - PyObject *exc_value; - PyObject *rest; - PyObject *match; - match_type = stack_pointer[-1]; - exc_value = stack_pointer[-2]; - if (_PyEval_CheckExceptStarTypeValid(tstate, match_type) < 0) { - Py_DECREF(exc_value); - Py_DECREF(match_type); - if (true) goto pop_2_error; - } - - match = NULL; - rest = NULL; - int res = _PyEval_ExceptionGroupMatch(exc_value, match_type, - &match, &rest); - Py_DECREF(exc_value); - Py_DECREF(match_type); - if (res < 0) goto pop_2_error; - - assert((match == NULL) == (rest == NULL)); - if (match == NULL) goto pop_2_error; - - if (!Py_IsNone(match)) { - PyErr_SetHandledException(match); - } - stack_pointer[-2] = rest; - stack_pointer[-1] = match; + INSTRUCTION_STATS(LOAD_FAST_CHECK); + PyObject *value; + value = GETLOCAL(oparg); + if (value == NULL) goto unbound_local_error; + Py_INCREF(value); + STACK_GROW(1); + stack_pointer[-1] = value; DISPATCH(); } - TARGET(CHECK_EXC_MATCH) { + TARGET(LOAD_FAST_LOAD_FAST) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(CHECK_EXC_MATCH); - PyObject *right; - PyObject *left; - PyObject *b; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - assert(PyExceptionInstance_Check(left)); - if (_PyEval_CheckExceptTypeValid(tstate, right) < 0) { - Py_DECREF(right); - if (true) goto pop_1_error; - } - - int res = PyErr_GivenExceptionMatches(left, right); - Py_DECREF(right); - b = res ? Py_True : Py_False; - stack_pointer[-1] = b; + INSTRUCTION_STATS(LOAD_FAST_LOAD_FAST); + PyObject *value1; + PyObject *value2; + uint32_t oparg1 = oparg >> 4; + uint32_t oparg2 = oparg & 15; + value1 = GETLOCAL(oparg1); + value2 = GETLOCAL(oparg2); + Py_INCREF(value1); + Py_INCREF(value2); + STACK_GROW(2); + stack_pointer[-2] = value1; + stack_pointer[-1] = value2; DISPATCH(); } - TARGET(IMPORT_NAME) { + TARGET(LOAD_FROM_DICT_OR_DEREF) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(IMPORT_NAME); - PyObject *fromlist; - PyObject *level; - PyObject *res; - fromlist = stack_pointer[-1]; - level = stack_pointer[-2]; - TIER_ONE_ONLY - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - res = import_name(tstate, frame, name, fromlist, level); - Py_DECREF(level); - Py_DECREF(fromlist); - if (res == NULL) goto pop_2_error; - STACK_SHRINK(1); - stack_pointer[-1] = res; + INSTRUCTION_STATS(LOAD_FROM_DICT_OR_DEREF); + PyObject *class_dict; + PyObject *value; + class_dict = stack_pointer[-1]; + PyObject *name; + assert(class_dict); + assert(oparg >= 0 && oparg < _PyFrame_GetCode(frame)->co_nlocalsplus); + name = PyTuple_GET_ITEM(_PyFrame_GetCode(frame)->co_localsplusnames, oparg); + if (PyMapping_GetOptionalItem(class_dict, name, &value) < 0) { + GOTO_ERROR(error); + } + if (!value) { + PyObject *cell = GETLOCAL(oparg); + value = PyCell_GET(cell); + if (value == NULL) { + _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); + GOTO_ERROR(error); + } + Py_INCREF(value); + } + Py_DECREF(class_dict); + stack_pointer[-1] = value; DISPATCH(); } - TARGET(IMPORT_FROM) { + TARGET(LOAD_FROM_DICT_OR_GLOBALS) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(IMPORT_FROM); - PyObject *from; - PyObject *res; - from = stack_pointer[-1]; - TIER_ONE_ONLY - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - res = import_from(tstate, from, name); - if (res == NULL) goto error; - STACK_GROW(1); - stack_pointer[-1] = res; - DISPATCH(); - } - - TARGET(JUMP_FORWARD) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(JUMP_FORWARD); - JUMPBY(oparg); - DISPATCH(); - } - - TARGET(JUMP_BACKWARD) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(JUMP_BACKWARD); - CHECK_EVAL_BREAKER(); - assert(oparg <= INSTR_OFFSET()); - JUMPBY(-oparg); - #if ENABLE_SPECIALIZATION - this_instr[1].cache += (1 << OPTIMIZER_BITS_IN_COUNTER); - /* We are using unsigned values, but we really want signed values, so - * do the 2s complement comparison manually */ - uint16_t ucounter = this_instr[1].cache + (1 << 15); - uint16_t threshold = tstate->interp->optimizer_backedge_threshold + (1 << 15); - // Double-check that the opcode isn't instrumented or something: - if (ucounter > threshold && this_instr->op.code == JUMP_BACKWARD) { - OPT_STAT_INC(attempts); - int optimized = _PyOptimizer_BackEdge(frame, this_instr, next_instr, stack_pointer); - if (optimized < 0) goto error; - if (optimized) { - // Rewind and enter the executor: - assert(this_instr->op.code == ENTER_EXECUTOR); - next_instr = this_instr; - this_instr[1].cache &= ((1 << OPTIMIZER_BITS_IN_COUNTER) - 1); + INSTRUCTION_STATS(LOAD_FROM_DICT_OR_GLOBALS); + PyObject *mod_or_class_dict; + PyObject *v; + mod_or_class_dict = stack_pointer[-1]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) { + GOTO_ERROR(error); + } + if (v == NULL) { + if (PyDict_GetItemRef(GLOBALS(), name, &v) < 0) { + GOTO_ERROR(error); } - else { - int backoff = this_instr[1].cache & ((1 << OPTIMIZER_BITS_IN_COUNTER) - 1); - if (backoff < MINIMUM_TIER2_BACKOFF) { - backoff = MINIMUM_TIER2_BACKOFF; + if (v == NULL) { + if (PyMapping_GetOptionalItem(BUILTINS(), name, &v) < 0) { + GOTO_ERROR(error); } - else if (backoff < 15 - OPTIMIZER_BITS_IN_COUNTER) { - backoff++; + if (v == NULL) { + _PyEval_FormatExcCheckArg( + tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + GOTO_ERROR(error); } - assert(backoff <= 15 - OPTIMIZER_BITS_IN_COUNTER); - this_instr[1].cache = ((1 << 16) - ((1 << OPTIMIZER_BITS_IN_COUNTER) << backoff)) | backoff; } } - #endif /* ENABLE_SPECIALIZATION */ + Py_DECREF(mod_or_class_dict); + stack_pointer[-1] = v; DISPATCH(); } - TARGET(ENTER_EXECUTOR) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(ENTER_EXECUTOR); - TIER_ONE_ONLY - CHECK_EVAL_BREAKER(); - - PyCodeObject *code = _PyFrame_GetCode(frame); - _PyExecutorObject *executor = (_PyExecutorObject *)code->co_executors->executors[oparg&255]; - int original_oparg = executor->vm_data.oparg | (oparg & 0xfffff00); - JUMPBY(1-original_oparg); + TARGET(LOAD_GLOBAL) { frame->instr_ptr = next_instr; - Py_INCREF(executor); - if (executor->execute == _PyUopExecute) { - current_executor = (_PyUOpExecutorObject *)executor; - GOTO_TIER_TWO(); + next_instr += 5; + INSTRUCTION_STATS(LOAD_GLOBAL); + PREDICTED(LOAD_GLOBAL); + _Py_CODEUNIT *this_instr = next_instr - 5; + static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size"); + PyObject *res; + PyObject *null = NULL; + // _SPECIALIZE_LOAD_GLOBAL + { + uint16_t counter = read_u16(&this_instr[1].cache); + TIER_ONE_ONLY + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); + next_instr = this_instr; + _Py_Specialize_LoadGlobal(GLOBALS(), BUILTINS(), next_instr, name); + DISPATCH_SAME_OPARG(); + } + STAT_INC(LOAD_GLOBAL, deferred); + DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + #endif /* ENABLE_SPECIALIZATION */ } - frame = executor->execute(executor, frame, stack_pointer); - if (frame == NULL) { - frame = tstate->current_frame; - goto resume_with_error; + // _LOAD_GLOBAL + { + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); + if (PyDict_CheckExact(GLOBALS()) + && PyDict_CheckExact(BUILTINS())) + { + res = _PyDict_LoadGlobal((PyDictObject *)GLOBALS(), + (PyDictObject *)BUILTINS(), + name); + if (res == NULL) { + if (!_PyErr_Occurred(tstate)) { + /* _PyDict_LoadGlobal() returns NULL without raising + * an exception if the key doesn't exist */ + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + } + if (true) goto error; + } + Py_INCREF(res); + } + else { + /* Slow-path if globals or builtins is not a dict */ + /* namespace 1: globals */ + if (PyMapping_GetOptionalItem(GLOBALS(), name, &res) < 0) goto error; + if (res == NULL) { + /* namespace 2: builtins */ + if (PyMapping_GetOptionalItem(BUILTINS(), name, &res) < 0) goto error; + if (res == NULL) { + _PyEval_FormatExcCheckArg( + tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + if (true) goto error; + } + } + } + null = NULL; } - goto enter_tier_one; - } - - TARGET(POP_JUMP_IF_TRUE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(POP_JUMP_IF_TRUE); - PyObject *cond; - cond = stack_pointer[-1]; - assert(PyBool_Check(cond)); - int flag = Py_IsTrue(cond); - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif - JUMPBY(oparg * flag); - STACK_SHRINK(1); - DISPATCH(); - } - - TARGET(POP_JUMP_IF_FALSE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(POP_JUMP_IF_FALSE); - PyObject *cond; - cond = stack_pointer[-1]; - assert(PyBool_Check(cond)); - int flag = Py_IsFalse(cond); - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif - JUMPBY(oparg * flag); - STACK_SHRINK(1); + STACK_GROW(1); + STACK_GROW(((oparg & 1) ? 1 : 0)); + stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = res; + if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } DISPATCH(); } - TARGET(POP_JUMP_IF_NONE) { + TARGET(LOAD_GLOBAL_BUILTIN) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(POP_JUMP_IF_NONE); - PyObject *value; - PyObject *b; - PyObject *cond; - // _IS_NONE - value = stack_pointer[-1]; + next_instr += 5; + INSTRUCTION_STATS(LOAD_GLOBAL_BUILTIN); + PyObject *res; + PyObject *null = NULL; + // _GUARD_GLOBALS_VERSION { - if (Py_IsNone(value)) { - b = Py_True; - } - else { - b = Py_False; - Py_DECREF(value); - } + uint16_t version = read_u16(&this_instr[2].cache); + PyDictObject *dict = (PyDictObject *)GLOBALS(); + DEOPT_IF(!PyDict_CheckExact(dict), LOAD_GLOBAL); + DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL); + assert(DK_IS_UNICODE(dict->ma_keys)); } - // _POP_JUMP_IF_TRUE - cond = b; + // _GUARD_BUILTINS_VERSION { - assert(PyBool_Check(cond)); - int flag = Py_IsTrue(cond); - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif - JUMPBY(oparg * flag); + uint16_t version = read_u16(&this_instr[3].cache); + PyDictObject *dict = (PyDictObject *)BUILTINS(); + DEOPT_IF(!PyDict_CheckExact(dict), LOAD_GLOBAL); + DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL); + assert(DK_IS_UNICODE(dict->ma_keys)); } - STACK_SHRINK(1); + // _LOAD_GLOBAL_BUILTINS + { + uint16_t index = read_u16(&this_instr[4].cache); + PyDictObject *bdict = (PyDictObject *)BUILTINS(); + PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(bdict->ma_keys); + res = entries[index].me_value; + DEOPT_IF(res == NULL, LOAD_GLOBAL); + Py_INCREF(res); + STAT_INC(LOAD_GLOBAL, hit); + null = NULL; + } + STACK_GROW(1); + STACK_GROW(((oparg & 1) ? 1 : 0)); + stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = res; + if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } DISPATCH(); } - TARGET(POP_JUMP_IF_NOT_NONE) { + TARGET(LOAD_GLOBAL_MODULE) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(POP_JUMP_IF_NOT_NONE); - PyObject *value; - PyObject *b; - PyObject *cond; - // _IS_NONE - value = stack_pointer[-1]; + next_instr += 5; + INSTRUCTION_STATS(LOAD_GLOBAL_MODULE); + PyObject *res; + PyObject *null = NULL; + // _GUARD_GLOBALS_VERSION { - if (Py_IsNone(value)) { - b = Py_True; - } - else { - b = Py_False; - Py_DECREF(value); - } + uint16_t version = read_u16(&this_instr[2].cache); + PyDictObject *dict = (PyDictObject *)GLOBALS(); + DEOPT_IF(!PyDict_CheckExact(dict), LOAD_GLOBAL); + DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL); + assert(DK_IS_UNICODE(dict->ma_keys)); } - // _POP_JUMP_IF_FALSE - cond = b; + // _LOAD_GLOBAL_MODULE { - assert(PyBool_Check(cond)); - int flag = Py_IsFalse(cond); - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif - JUMPBY(oparg * flag); + uint16_t index = read_u16(&this_instr[4].cache); + PyDictObject *dict = (PyDictObject *)GLOBALS(); + PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys); + res = entries[index].me_value; + DEOPT_IF(res == NULL, LOAD_GLOBAL); + Py_INCREF(res); + STAT_INC(LOAD_GLOBAL, hit); + null = NULL; } - STACK_SHRINK(1); - DISPATCH(); - } - - TARGET(JUMP_BACKWARD_NO_INTERRUPT) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(JUMP_BACKWARD_NO_INTERRUPT); - /* This bytecode is used in the `yield from` or `await` loop. - * If there is an interrupt, we want it handled in the innermost - * generator or coroutine, so we deliberately do not check it here. - * (see bpo-30039). - */ - JUMPBY(-oparg); - DISPATCH(); - } - - TARGET(GET_LEN) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(GET_LEN); - PyObject *obj; - PyObject *len_o; - obj = stack_pointer[-1]; - // PUSH(len(TOS)) - Py_ssize_t len_i = PyObject_Length(obj); - if (len_i < 0) goto error; - len_o = PyLong_FromSsize_t(len_i); - if (len_o == NULL) goto error; STACK_GROW(1); - stack_pointer[-1] = len_o; + STACK_GROW(((oparg & 1) ? 1 : 0)); + stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = res; + if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } DISPATCH(); } - TARGET(MATCH_CLASS) { + TARGET(LOAD_LOCALS) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(MATCH_CLASS); - PyObject *names; - PyObject *type; - PyObject *subject; - PyObject *attrs; - names = stack_pointer[-1]; - type = stack_pointer[-2]; - subject = stack_pointer[-3]; - // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or - // None on failure. - assert(PyTuple_CheckExact(names)); - attrs = _PyEval_MatchClass(tstate, subject, type, oparg, names); - Py_DECREF(subject); - Py_DECREF(type); - Py_DECREF(names); - if (attrs) { - assert(PyTuple_CheckExact(attrs)); // Success! - } - else { - if (_PyErr_Occurred(tstate)) goto pop_3_error; - attrs = Py_None; // Failure! + INSTRUCTION_STATS(LOAD_LOCALS); + PyObject *locals; + locals = LOCALS(); + if (locals == NULL) { + _PyErr_SetString(tstate, PyExc_SystemError, + "no locals found"); + if (true) goto error; } - STACK_SHRINK(2); - stack_pointer[-1] = attrs; - DISPATCH(); - } - - TARGET(MATCH_MAPPING) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(MATCH_MAPPING); - PyObject *subject; - PyObject *res; - subject = stack_pointer[-1]; - int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; - res = match ? Py_True : Py_False; - STACK_GROW(1); - stack_pointer[-1] = res; - DISPATCH(); - } - - TARGET(MATCH_SEQUENCE) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(MATCH_SEQUENCE); - PyObject *subject; - PyObject *res; - subject = stack_pointer[-1]; - int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; - res = match ? Py_True : Py_False; - STACK_GROW(1); - stack_pointer[-1] = res; - DISPATCH(); - } - - TARGET(MATCH_KEYS) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(MATCH_KEYS); - PyObject *keys; - PyObject *subject; - PyObject *values_or_none; - keys = stack_pointer[-1]; - subject = stack_pointer[-2]; - // On successful match, PUSH(values). Otherwise, PUSH(None). - values_or_none = _PyEval_MatchKeys(tstate, subject, keys); - if (values_or_none == NULL) goto error; + Py_INCREF(locals); STACK_GROW(1); - stack_pointer[-1] = values_or_none; - DISPATCH(); - } - - TARGET(GET_ITER) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(GET_ITER); - PyObject *iterable; - PyObject *iter; - iterable = stack_pointer[-1]; - /* before: [obj]; after [getiter(obj)] */ - iter = PyObject_GetIter(iterable); - Py_DECREF(iterable); - if (iter == NULL) goto pop_1_error; - stack_pointer[-1] = iter; + stack_pointer[-1] = locals; DISPATCH(); } - TARGET(GET_YIELD_FROM_ITER) { + TARGET(LOAD_NAME) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(GET_YIELD_FROM_ITER); - PyObject *iterable; - PyObject *iter; - iterable = stack_pointer[-1]; - /* before: [obj]; after [getiter(obj)] */ - if (PyCoro_CheckExact(iterable)) { - /* `iterable` is a coroutine */ - if (!(_PyFrame_GetCode(frame)->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) { - /* and it is used in a 'yield from' expression of a - regular generator. */ - _PyErr_SetString(tstate, PyExc_TypeError, - "cannot 'yield from' a coroutine object " - "in a non-coroutine generator"); - GOTO_ERROR(error); - } - iter = iterable; + INSTRUCTION_STATS(LOAD_NAME); + PyObject *v; + PyObject *mod_or_class_dict = LOCALS(); + if (mod_or_class_dict == NULL) { + _PyErr_SetString(tstate, PyExc_SystemError, + "no locals found"); + if (true) goto error; } - else if (PyGen_CheckExact(iterable)) { - iter = iterable; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) { + GOTO_ERROR(error); } - else { - /* `iterable` is not a generator. */ - iter = PyObject_GetIter(iterable); - if (iter == NULL) { + if (v == NULL) { + if (PyDict_GetItemRef(GLOBALS(), name, &v) < 0) { GOTO_ERROR(error); } - Py_DECREF(iterable); + if (v == NULL) { + if (PyMapping_GetOptionalItem(BUILTINS(), name, &v) < 0) { + GOTO_ERROR(error); + } + if (v == NULL) { + _PyEval_FormatExcCheckArg( + tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + GOTO_ERROR(error); + } + } } - stack_pointer[-1] = iter; + STACK_GROW(1); + stack_pointer[-1] = v; DISPATCH(); } - TARGET(FOR_ITER) { + TARGET(LOAD_SUPER_ATTR) { frame->instr_ptr = next_instr; next_instr += 2; - INSTRUCTION_STATS(FOR_ITER); - PREDICTED(FOR_ITER); + INSTRUCTION_STATS(LOAD_SUPER_ATTR); + PREDICTED(LOAD_SUPER_ATTR); _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); - PyObject *iter; - PyObject *next; - // _SPECIALIZE_FOR_ITER - iter = stack_pointer[-1]; + static_assert(INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR == 1, "incorrect cache size"); + PyObject *class; + PyObject *global_super; + PyObject *self; + PyObject *attr; + PyObject *null = NULL; + // _SPECIALIZE_LOAD_SUPER_ATTR + class = stack_pointer[-2]; + global_super = stack_pointer[-3]; { uint16_t counter = read_u16(&this_instr[1].cache); TIER_ONE_ONLY #if ENABLE_SPECIALIZATION + int load_method = oparg & 1; if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { next_instr = this_instr; - _Py_Specialize_ForIter(iter, next_instr, oparg); + _Py_Specialize_LoadSuperAttr(global_super, class, next_instr, load_method); DISPATCH_SAME_OPARG(); } - STAT_INC(FOR_ITER, deferred); + STAT_INC(LOAD_SUPER_ATTR, deferred); DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); #endif /* ENABLE_SPECIALIZATION */ } - // _FOR_ITER + // _LOAD_SUPER_ATTR + self = stack_pointer[-1]; { - /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */ - next = (*Py_TYPE(iter)->tp_iternext)(iter); - if (next == NULL) { - if (_PyErr_Occurred(tstate)) { - if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { - GOTO_ERROR(error); - } - monitor_raise(tstate, frame, this_instr); - _PyErr_Clear(tstate); - } - /* iterator ended normally */ - assert(next_instr[oparg].op.code == END_FOR || - next_instr[oparg].op.code == INSTRUMENTED_END_FOR); - Py_DECREF(iter); - STACK_SHRINK(1); - /* Jump forward oparg, then skip following END_FOR instruction */ - JUMPBY(oparg + 1); - DISPATCH(); + TIER_ONE_ONLY + if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { + PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, global_super, arg); + if (err) goto pop_3_error; } - // Common case: no jump, leave it to the code generator - } - STACK_GROW(1); - stack_pointer[-1] = next; - DISPATCH(); - } - - TARGET(INSTRUMENTED_FOR_ITER) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(INSTRUMENTED_FOR_ITER); - _Py_CODEUNIT *target; - PyObject *iter = TOP(); - PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter); - if (next != NULL) { - PUSH(next); - target = next_instr; - } - else { - if (_PyErr_Occurred(tstate)) { - if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { - GOTO_ERROR(error); + // we make no attempt to optimize here; specializations should + // handle any case whose performance we care about + PyObject *stack[] = {class, self}; + PyObject *super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL); + if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { + PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; + if (super == NULL) { + _Py_call_instrumentation_exc2( + tstate, PY_MONITORING_EVENT_C_RAISE, + frame, this_instr, global_super, arg); + } + else { + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_C_RETURN, + frame, this_instr, global_super, arg); + if (err < 0) { + Py_CLEAR(super); + } } - monitor_raise(tstate, frame, this_instr); - _PyErr_Clear(tstate); } - /* iterator ended normally */ - assert(next_instr[oparg].op.code == END_FOR || - next_instr[oparg].op.code == INSTRUMENTED_END_FOR); - STACK_SHRINK(1); - Py_DECREF(iter); - /* Skip END_FOR */ - target = next_instr + oparg + 1; + Py_DECREF(global_super); + Py_DECREF(class); + Py_DECREF(self); + if (super == NULL) goto pop_3_error; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + attr = PyObject_GetAttr(super, name); + Py_DECREF(super); + if (attr == NULL) goto pop_3_error; + null = NULL; } - INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH); + STACK_SHRINK(2); + STACK_GROW(((oparg & 1) ? 1 : 0)); + stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; + if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } DISPATCH(); } - TARGET(FOR_ITER_LIST) { + TARGET(LOAD_SUPER_ATTR_ATTR) { frame->instr_ptr = next_instr; next_instr += 2; - INSTRUCTION_STATS(FOR_ITER_LIST); - PyObject *iter; - PyObject *next; - // _ITER_CHECK_LIST - iter = stack_pointer[-1]; - { - DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type, FOR_ITER); - } - // _ITER_JUMP_LIST - { - _PyListIterObject *it = (_PyListIterObject *)iter; - assert(Py_TYPE(iter) == &PyListIter_Type); - STAT_INC(FOR_ITER, hit); - PyListObject *seq = it->it_seq; - if (seq == NULL || it->it_index >= PyList_GET_SIZE(seq)) { - if (seq != NULL) { - it->it_seq = NULL; - Py_DECREF(seq); - } - Py_DECREF(iter); - STACK_SHRINK(1); - /* Jump forward oparg, then skip following END_FOR instruction */ - JUMPBY(oparg + 1); - DISPATCH(); - } - } - // _ITER_NEXT_LIST - { - _PyListIterObject *it = (_PyListIterObject *)iter; - assert(Py_TYPE(iter) == &PyListIter_Type); - PyListObject *seq = it->it_seq; - assert(seq); - assert(it->it_index < PyList_GET_SIZE(seq)); - next = Py_NewRef(PyList_GET_ITEM(seq, it->it_index++)); - } - STACK_GROW(1); - stack_pointer[-1] = next; + INSTRUCTION_STATS(LOAD_SUPER_ATTR_ATTR); + PyObject *self; + PyObject *class; + PyObject *global_super; + PyObject *attr; + self = stack_pointer[-1]; + class = stack_pointer[-2]; + global_super = stack_pointer[-3]; + assert(!(oparg & 1)); + DEOPT_IF(global_super != (PyObject *)&PySuper_Type, LOAD_SUPER_ATTR); + DEOPT_IF(!PyType_Check(class), LOAD_SUPER_ATTR); + STAT_INC(LOAD_SUPER_ATTR, hit); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + attr = _PySuper_Lookup((PyTypeObject *)class, self, name, NULL); + Py_DECREF(global_super); + Py_DECREF(class); + Py_DECREF(self); + if (attr == NULL) goto pop_3_error; + STACK_SHRINK(2); + stack_pointer[-1] = attr; DISPATCH(); } - TARGET(FOR_ITER_TUPLE) { + TARGET(LOAD_SUPER_ATTR_METHOD) { frame->instr_ptr = next_instr; next_instr += 2; - INSTRUCTION_STATS(FOR_ITER_TUPLE); - PyObject *iter; - PyObject *next; - // _ITER_CHECK_TUPLE - iter = stack_pointer[-1]; - { - DEOPT_IF(Py_TYPE(iter) != &PyTupleIter_Type, FOR_ITER); - } - // _ITER_JUMP_TUPLE - { - _PyTupleIterObject *it = (_PyTupleIterObject *)iter; - assert(Py_TYPE(iter) == &PyTupleIter_Type); - STAT_INC(FOR_ITER, hit); - PyTupleObject *seq = it->it_seq; - if (seq == NULL || it->it_index >= PyTuple_GET_SIZE(seq)) { - if (seq != NULL) { - it->it_seq = NULL; - Py_DECREF(seq); - } - Py_DECREF(iter); - STACK_SHRINK(1); - /* Jump forward oparg, then skip following END_FOR instruction */ - JUMPBY(oparg + 1); - DISPATCH(); - } + INSTRUCTION_STATS(LOAD_SUPER_ATTR_METHOD); + PyObject *self; + PyObject *class; + PyObject *global_super; + PyObject *attr; + PyObject *self_or_null; + self = stack_pointer[-1]; + class = stack_pointer[-2]; + global_super = stack_pointer[-3]; + assert(oparg & 1); + DEOPT_IF(global_super != (PyObject *)&PySuper_Type, LOAD_SUPER_ATTR); + DEOPT_IF(!PyType_Check(class), LOAD_SUPER_ATTR); + STAT_INC(LOAD_SUPER_ATTR, hit); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + PyTypeObject *cls = (PyTypeObject *)class; + int method_found = 0; + attr = _PySuper_Lookup(cls, self, name, + Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL); + Py_DECREF(global_super); + Py_DECREF(class); + if (attr == NULL) { + Py_DECREF(self); + if (true) goto pop_3_error; } - // _ITER_NEXT_TUPLE - { - _PyTupleIterObject *it = (_PyTupleIterObject *)iter; - assert(Py_TYPE(iter) == &PyTupleIter_Type); - PyTupleObject *seq = it->it_seq; - assert(seq); - assert(it->it_index < PyTuple_GET_SIZE(seq)); - next = Py_NewRef(PyTuple_GET_ITEM(seq, it->it_index++)); + if (method_found) { + self_or_null = self; // transfer ownership + } else { + Py_DECREF(self); + self_or_null = NULL; } - STACK_GROW(1); - stack_pointer[-1] = next; + STACK_SHRINK(1); + stack_pointer[-2] = attr; + stack_pointer[-1] = self_or_null; DISPATCH(); } - TARGET(FOR_ITER_RANGE) { + TARGET(MAKE_CELL) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(FOR_ITER_RANGE); - PyObject *iter; - PyObject *next; - // _ITER_CHECK_RANGE - iter = stack_pointer[-1]; - { - _PyRangeIterObject *r = (_PyRangeIterObject *)iter; - DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); - } - // _ITER_JUMP_RANGE - { - _PyRangeIterObject *r = (_PyRangeIterObject *)iter; - assert(Py_TYPE(r) == &PyRangeIter_Type); - STAT_INC(FOR_ITER, hit); - if (r->len <= 0) { - STACK_SHRINK(1); - Py_DECREF(r); - // Jump over END_FOR instruction. - JUMPBY(oparg + 1); - DISPATCH(); - } + next_instr += 1; + INSTRUCTION_STATS(MAKE_CELL); + // "initial" is probably NULL but not if it's an arg (or set + // via PyFrame_LocalsToFast() before MAKE_CELL has run). + PyObject *initial = GETLOCAL(oparg); + PyObject *cell = PyCell_New(initial); + if (cell == NULL) { + GOTO_ERROR(error); } - // _ITER_NEXT_RANGE - { - _PyRangeIterObject *r = (_PyRangeIterObject *)iter; - assert(Py_TYPE(r) == &PyRangeIter_Type); - assert(r->len > 0); - long value = r->start; - r->start = value + r->step; - r->len--; - next = PyLong_FromLong(value); - if (next == NULL) goto error; + SETLOCAL(oparg, cell); + DISPATCH(); + } + + TARGET(MAKE_FUNCTION) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(MAKE_FUNCTION); + PyObject *codeobj; + PyObject *func; + codeobj = stack_pointer[-1]; + + PyFunctionObject *func_obj = (PyFunctionObject *) + PyFunction_New(codeobj, GLOBALS()); + + Py_DECREF(codeobj); + if (func_obj == NULL) { + GOTO_ERROR(error); } - STACK_GROW(1); - stack_pointer[-1] = next; + + _PyFunction_SetVersion( + func_obj, ((PyCodeObject *)codeobj)->co_version); + func = (PyObject *)func_obj; + stack_pointer[-1] = func; DISPATCH(); } - TARGET(FOR_ITER_GEN) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(FOR_ITER_GEN); - PyObject *iter; - iter = stack_pointer[-1]; - DEOPT_IF(tstate->interp->eval_frame, FOR_ITER); - PyGenObject *gen = (PyGenObject *)iter; - DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER); - DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER); - STAT_INC(FOR_ITER, hit); - _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; - _PyFrame_StackPush(gen_frame, Py_None); - gen->gi_frame_state = FRAME_EXECUTING; - gen->gi_exc_state.previous_item = tstate->exc_info; - tstate->exc_info = &gen->gi_exc_state; - assert(next_instr[oparg].op.code == END_FOR || - next_instr[oparg].op.code == INSTRUMENTED_END_FOR); - assert(next_instr - this_instr + oparg <= UINT16_MAX); - frame->return_offset = (uint16_t)(next_instr - this_instr + oparg); - DISPATCH_INLINED(gen_frame); + TARGET(MAP_ADD) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(MAP_ADD); + PyObject *value; + PyObject *key; + PyObject *dict; + value = stack_pointer[-1]; + key = stack_pointer[-2]; + dict = stack_pointer[-3 - (oparg - 1)]; + assert(PyDict_CheckExact(dict)); + /* dict[key] = value */ + // Do not DECREF INPUTS because the function steals the references + if (_PyDict_SetItem_Take2((PyDictObject *)dict, key, value) != 0) goto pop_2_error; + STACK_SHRINK(2); + DISPATCH(); } - TARGET(BEFORE_ASYNC_WITH) { + TARGET(MATCH_CLASS) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(BEFORE_ASYNC_WITH); - PyObject *mgr; - PyObject *exit; - PyObject *res; - mgr = stack_pointer[-1]; - PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__aenter__)); - if (enter == NULL) { - if (!_PyErr_Occurred(tstate)) { - _PyErr_Format(tstate, PyExc_TypeError, - "'%.200s' object does not support the " - "asynchronous context manager protocol", - Py_TYPE(mgr)->tp_name); - } - GOTO_ERROR(error); - } - exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__)); - if (exit == NULL) { - if (!_PyErr_Occurred(tstate)) { - _PyErr_Format(tstate, PyExc_TypeError, - "'%.200s' object does not support the " - "asynchronous context manager protocol " - "(missed __aexit__ method)", - Py_TYPE(mgr)->tp_name); - } - Py_DECREF(enter); - GOTO_ERROR(error); + INSTRUCTION_STATS(MATCH_CLASS); + PyObject *names; + PyObject *type; + PyObject *subject; + PyObject *attrs; + names = stack_pointer[-1]; + type = stack_pointer[-2]; + subject = stack_pointer[-3]; + // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or + // None on failure. + assert(PyTuple_CheckExact(names)); + attrs = _PyEval_MatchClass(tstate, subject, type, oparg, names); + Py_DECREF(subject); + Py_DECREF(type); + Py_DECREF(names); + if (attrs) { + assert(PyTuple_CheckExact(attrs)); // Success! } - Py_DECREF(mgr); - res = _PyObject_CallNoArgsTstate(tstate, enter); - Py_DECREF(enter); - if (res == NULL) { - Py_DECREF(exit); - if (true) goto pop_1_error; + else { + if (_PyErr_Occurred(tstate)) goto pop_3_error; + attrs = Py_None; // Failure! } + STACK_SHRINK(2); + stack_pointer[-1] = attrs; + DISPATCH(); + } + + TARGET(MATCH_KEYS) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(MATCH_KEYS); + PyObject *keys; + PyObject *subject; + PyObject *values_or_none; + keys = stack_pointer[-1]; + subject = stack_pointer[-2]; + // On successful match, PUSH(values). Otherwise, PUSH(None). + values_or_none = _PyEval_MatchKeys(tstate, subject, keys); + if (values_or_none == NULL) goto error; STACK_GROW(1); - stack_pointer[-2] = exit; - stack_pointer[-1] = res; + stack_pointer[-1] = values_or_none; DISPATCH(); } - TARGET(BEFORE_WITH) { + TARGET(MATCH_MAPPING) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(BEFORE_WITH); - PyObject *mgr; - PyObject *exit; + INSTRUCTION_STATS(MATCH_MAPPING); + PyObject *subject; PyObject *res; - mgr = stack_pointer[-1]; - /* pop the context manager, push its __exit__ and the - * value returned from calling its __enter__ - */ - PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__enter__)); - if (enter == NULL) { - if (!_PyErr_Occurred(tstate)) { - _PyErr_Format(tstate, PyExc_TypeError, - "'%.200s' object does not support the " - "context manager protocol", - Py_TYPE(mgr)->tp_name); - } - GOTO_ERROR(error); - } - exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__)); - if (exit == NULL) { - if (!_PyErr_Occurred(tstate)) { - _PyErr_Format(tstate, PyExc_TypeError, - "'%.200s' object does not support the " - "context manager protocol " - "(missed __exit__ method)", - Py_TYPE(mgr)->tp_name); - } - Py_DECREF(enter); - GOTO_ERROR(error); - } - Py_DECREF(mgr); - res = _PyObject_CallNoArgsTstate(tstate, enter); - Py_DECREF(enter); - if (res == NULL) { - Py_DECREF(exit); - if (true) goto pop_1_error; - } + subject = stack_pointer[-1]; + int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; + res = match ? Py_True : Py_False; STACK_GROW(1); - stack_pointer[-2] = exit; stack_pointer[-1] = res; DISPATCH(); } - TARGET(WITH_EXCEPT_START) { + TARGET(MATCH_SEQUENCE) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(WITH_EXCEPT_START); - PyObject *val; - PyObject *lasti; - PyObject *exit_func; + INSTRUCTION_STATS(MATCH_SEQUENCE); + PyObject *subject; PyObject *res; - val = stack_pointer[-1]; - lasti = stack_pointer[-3]; - exit_func = stack_pointer[-4]; - /* At the top of the stack are 4 values: - - val: TOP = exc_info() - - unused: SECOND = previous exception - - lasti: THIRD = lasti of exception in exc_info() - - exit_func: FOURTH = the context.__exit__ bound method - We call FOURTH(type(TOP), TOP, GetTraceback(TOP)). - Then we push the __exit__ return value. - */ - PyObject *exc, *tb; - - assert(val && PyExceptionInstance_Check(val)); - exc = PyExceptionInstance_Class(val); - tb = PyException_GetTraceback(val); - if (tb == NULL) { - tb = Py_None; - } - else { - Py_DECREF(tb); - } - assert(PyLong_Check(lasti)); - (void)lasti; // Shut up compiler warning if asserts are off - PyObject *stack[4] = {NULL, exc, val, tb}; - res = PyObject_Vectorcall(exit_func, stack + 1, - 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); - if (res == NULL) goto error; + subject = stack_pointer[-1]; + int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; + res = match ? Py_True : Py_False; STACK_GROW(1); stack_pointer[-1] = res; DISPATCH(); } - TARGET(PUSH_EXC_INFO) { + TARGET(NOP) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(PUSH_EXC_INFO); - PyObject *new_exc; - PyObject *prev_exc; - new_exc = stack_pointer[-1]; + INSTRUCTION_STATS(NOP); + DISPATCH(); + } + + TARGET(POP_EXCEPT) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(POP_EXCEPT); + PyObject *exc_value; + exc_value = stack_pointer[-1]; _PyErr_StackItem *exc_info = tstate->exc_info; - if (exc_info->exc_value != NULL) { - prev_exc = exc_info->exc_value; - } - else { - prev_exc = Py_None; - } - assert(PyExceptionInstance_Check(new_exc)); - exc_info->exc_value = Py_NewRef(new_exc); - STACK_GROW(1); - stack_pointer[-2] = prev_exc; - stack_pointer[-1] = new_exc; + Py_XSETREF(exc_info->exc_value, exc_value); + STACK_SHRINK(1); DISPATCH(); } - TARGET(LOAD_ATTR_METHOD_WITH_VALUES) { + TARGET(POP_JUMP_IF_FALSE) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_METHOD_WITH_VALUES); - PyObject *owner; - PyObject *attr; - PyObject *self; - // _GUARD_TYPE_VERSION - owner = stack_pointer[-1]; + next_instr += 2; + INSTRUCTION_STATS(POP_JUMP_IF_FALSE); + PyObject *cond; + cond = stack_pointer[-1]; + assert(PyBool_Check(cond)); + int flag = Py_IsFalse(cond); + #if ENABLE_SPECIALIZATION + this_instr[1].cache = (this_instr[1].cache << 1) | flag; + #endif + JUMPBY(oparg * flag); + STACK_SHRINK(1); + DISPATCH(); + } + + TARGET(POP_JUMP_IF_NONE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(POP_JUMP_IF_NONE); + PyObject *value; + PyObject *b; + PyObject *cond; + // _IS_NONE + value = stack_pointer[-1]; { - uint32_t type_version = read_u32(&this_instr[2].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); - } - // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT - { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), LOAD_ATTR); - } - // _GUARD_KEYS_VERSION - { - uint32_t keys_version = read_u32(&this_instr[4].cache); - PyTypeObject *owner_cls = Py_TYPE(owner); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version, LOAD_ATTR); + if (Py_IsNone(value)) { + b = Py_True; + } + else { + b = Py_False; + Py_DECREF(value); + } } - // _LOAD_ATTR_METHOD_WITH_VALUES + // _POP_JUMP_IF_TRUE + cond = b; { - PyObject *descr = read_obj(&this_instr[6].cache); - assert(oparg & 1); - /* Cached method object */ - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - attr = Py_NewRef(descr); - assert(_PyType_HasFeature(Py_TYPE(attr), Py_TPFLAGS_METHOD_DESCRIPTOR)); - self = owner; + assert(PyBool_Check(cond)); + int flag = Py_IsTrue(cond); + #if ENABLE_SPECIALIZATION + this_instr[1].cache = (this_instr[1].cache << 1) | flag; + #endif + JUMPBY(oparg * flag); } - STACK_GROW(1); - stack_pointer[-2] = attr; - stack_pointer[-1] = self; + STACK_SHRINK(1); DISPATCH(); } - TARGET(LOAD_ATTR_METHOD_NO_DICT) { + TARGET(POP_JUMP_IF_NOT_NONE) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_METHOD_NO_DICT); - PyObject *owner; - PyObject *attr; - PyObject *self; - // _GUARD_TYPE_VERSION - owner = stack_pointer[-1]; + next_instr += 2; + INSTRUCTION_STATS(POP_JUMP_IF_NOT_NONE); + PyObject *value; + PyObject *b; + PyObject *cond; + // _IS_NONE + value = stack_pointer[-1]; { - uint32_t type_version = read_u32(&this_instr[2].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + if (Py_IsNone(value)) { + b = Py_True; + } + else { + b = Py_False; + Py_DECREF(value); + } } - // _LOAD_ATTR_METHOD_NO_DICT + // _POP_JUMP_IF_FALSE + cond = b; { - PyObject *descr = read_obj(&this_instr[6].cache); - assert(oparg & 1); - assert(Py_TYPE(owner)->tp_dictoffset == 0); - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); - attr = Py_NewRef(descr); - self = owner; + assert(PyBool_Check(cond)); + int flag = Py_IsFalse(cond); + #if ENABLE_SPECIALIZATION + this_instr[1].cache = (this_instr[1].cache << 1) | flag; + #endif + JUMPBY(oparg * flag); } - STACK_GROW(1); - stack_pointer[-2] = attr; - stack_pointer[-1] = self; + STACK_SHRINK(1); DISPATCH(); } - TARGET(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES) { + TARGET(POP_JUMP_IF_TRUE) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES); - PyObject *owner; - PyObject *attr; - // _GUARD_TYPE_VERSION - owner = stack_pointer[-1]; - { - uint32_t type_version = read_u32(&this_instr[2].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); - } - // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT - { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), LOAD_ATTR); - } - // _GUARD_KEYS_VERSION - { - uint32_t keys_version = read_u32(&this_instr[4].cache); - PyTypeObject *owner_cls = Py_TYPE(owner); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version, LOAD_ATTR); - } - // _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES - { - PyObject *descr = read_obj(&this_instr[6].cache); - assert((oparg & 1) == 0); - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - Py_DECREF(owner); - attr = Py_NewRef(descr); - } - stack_pointer[-1] = attr; + next_instr += 2; + INSTRUCTION_STATS(POP_JUMP_IF_TRUE); + PyObject *cond; + cond = stack_pointer[-1]; + assert(PyBool_Check(cond)); + int flag = Py_IsTrue(cond); + #if ENABLE_SPECIALIZATION + this_instr[1].cache = (this_instr[1].cache << 1) | flag; + #endif + JUMPBY(oparg * flag); + STACK_SHRINK(1); DISPATCH(); } - TARGET(LOAD_ATTR_NONDESCRIPTOR_NO_DICT) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_NONDESCRIPTOR_NO_DICT); - PyObject *owner; - PyObject *attr; - // _GUARD_TYPE_VERSION - owner = stack_pointer[-1]; - { - uint32_t type_version = read_u32(&this_instr[2].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); - } - // _LOAD_ATTR_NONDESCRIPTOR_NO_DICT - { - PyObject *descr = read_obj(&this_instr[6].cache); - assert((oparg & 1) == 0); - assert(Py_TYPE(owner)->tp_dictoffset == 0); - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - Py_DECREF(owner); - attr = Py_NewRef(descr); - } - stack_pointer[-1] = attr; + TARGET(POP_TOP) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(POP_TOP); + PyObject *value; + value = stack_pointer[-1]; + Py_DECREF(value); + STACK_SHRINK(1); DISPATCH(); } - TARGET(LOAD_ATTR_METHOD_LAZY_DICT) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_METHOD_LAZY_DICT); - PyObject *owner; - PyObject *attr; - PyObject *self; - // _GUARD_TYPE_VERSION - owner = stack_pointer[-1]; - { - uint32_t type_version = read_u32(&this_instr[2].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); - } - // _CHECK_ATTR_METHOD_LAZY_DICT - { - Py_ssize_t dictoffset = Py_TYPE(owner)->tp_dictoffset; - assert(dictoffset > 0); - PyObject *dict = *(PyObject **)((char *)owner + dictoffset); - /* This object has a __dict__, just not yet created */ - DEOPT_IF(dict != NULL, LOAD_ATTR); + TARGET(PUSH_EXC_INFO) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(PUSH_EXC_INFO); + PyObject *new_exc; + PyObject *prev_exc; + new_exc = stack_pointer[-1]; + _PyErr_StackItem *exc_info = tstate->exc_info; + if (exc_info->exc_value != NULL) { + prev_exc = exc_info->exc_value; } - // _LOAD_ATTR_METHOD_LAZY_DICT - { - PyObject *descr = read_obj(&this_instr[6].cache); - assert(oparg & 1); - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); - attr = Py_NewRef(descr); - self = owner; + else { + prev_exc = Py_None; } + assert(PyExceptionInstance_Check(new_exc)); + exc_info->exc_value = Py_NewRef(new_exc); STACK_GROW(1); - stack_pointer[-2] = attr; - stack_pointer[-1] = self; + stack_pointer[-2] = prev_exc; + stack_pointer[-1] = new_exc; DISPATCH(); } - TARGET(INSTRUMENTED_CALL) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(INSTRUMENTED_CALL); - int is_meth = PEEK(oparg + 1) != NULL; - int total_args = oparg + is_meth; - PyObject *function = PEEK(oparg + 2); - PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : PEEK(total_args); - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_CALL, - frame, this_instr, function, arg); - if (err) goto error; - INCREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - GO_TO_INSTRUCTION(CALL); + TARGET(PUSH_NULL) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(PUSH_NULL); + PyObject *res; + res = NULL; + STACK_GROW(1); + stack_pointer[-1] = res; + DISPATCH(); } - TARGET(CALL) { - frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL); - PREDICTED(CALL); - _Py_CODEUNIT *this_instr = next_instr - 4; - static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + TARGET(RAISE_VARARGS) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(RAISE_VARARGS); PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - // _SPECIALIZE_CALL - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - { - uint16_t counter = read_u16(&this_instr[1].cache); - TIER_ONE_ONLY - #if ENABLE_SPECIALIZATION - if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - next_instr = this_instr; - _Py_Specialize_Call(callable, next_instr, oparg + (self_or_null != NULL)); - DISPATCH_SAME_OPARG(); - } - STAT_INC(CALL, deferred); - DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - #endif /* ENABLE_SPECIALIZATION */ - } - // _CALL - { - // oparg counts all of the args, but *not* self: - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; - } - else if (Py_TYPE(callable) == &PyMethod_Type) { - args--; - total_args++; - PyObject *self = ((PyMethodObject *)callable)->im_self; - args[0] = Py_NewRef(self); - PyObject *method = ((PyMethodObject *)callable)->im_func; - args[-1] = Py_NewRef(method); - Py_DECREF(callable); - callable = method; - } - // Check if the call can be inlined or not - if (Py_TYPE(callable) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && - ((PyFunctionObject *)callable)->vectorcall == _PyFunction_Vectorcall) - { - int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable))->co_flags; - PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable)); - _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( - tstate, (PyFunctionObject *)callable, locals, - args, total_args, NULL - ); - // Manipulate stack directly since we leave using DISPATCH_INLINED(). - STACK_SHRINK(oparg + 2); - // The frame has stolen all the arguments from the stack, - // so there is no need to clean them up. - if (new_frame == NULL) { - GOTO_ERROR(error); - } - frame->return_offset = (uint16_t)(next_instr - this_instr); - DISPATCH_INLINED(new_frame); - } - /* Callable is not a normal Python function */ - res = PyObject_Vectorcall( - callable, args, - total_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - NULL); - if (opcode == INSTRUMENTED_CALL) { - PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : args[0]; - if (res == NULL) { - _Py_call_instrumentation_exc2( - tstate, PY_MONITORING_EVENT_C_RAISE, - frame, this_instr, callable, arg); - } - else { - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_C_RETURN, - frame, this_instr, callable, arg); - if (err < 0) { - Py_CLEAR(res); - } - } - } - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - Py_DECREF(callable); - for (int i = 0; i < total_args; i++) { - Py_DECREF(args[i]); - } - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); - DISPATCH(); - } - - TARGET(CALL_BOUND_METHOD_EXACT_ARGS) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_BOUND_METHOD_EXACT_ARGS); - PyObject *null; - PyObject *callable; - PyObject *self; - PyObject *self_or_null; - PyObject *func; - PyObject **args; - _PyInterpreterFrame *new_frame; - // _CHECK_PEP_523 - { - DEOPT_IF(tstate->interp->eval_frame, CALL); - } - // _CHECK_CALL_BOUND_METHOD_EXACT_ARGS - null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - { - DEOPT_IF(null != NULL, CALL); - DEOPT_IF(Py_TYPE(callable) != &PyMethod_Type, CALL); - } - // _INIT_CALL_BOUND_METHOD_EXACT_ARGS - { - STAT_INC(CALL, hit); - self = Py_NewRef(((PyMethodObject *)callable)->im_self); - stack_pointer[-1 - oparg] = self; // Patch stack as it is used by _INIT_CALL_PY_EXACT_ARGS - func = Py_NewRef(((PyMethodObject *)callable)->im_func); - stack_pointer[-2 - oparg] = func; // This is used by CALL, upon deoptimization - Py_DECREF(callable); - } - // _CHECK_FUNCTION_EXACT_ARGS - self_or_null = self; - callable = func; - { - uint32_t func_version = read_u32(&this_instr[2].cache); - DEOPT_IF(!PyFunction_Check(callable), CALL); - PyFunctionObject *func = (PyFunctionObject *)callable; - DEOPT_IF(func->func_version != func_version, CALL); - PyCodeObject *code = (PyCodeObject *)func->func_code; - DEOPT_IF(code->co_argcount != oparg + (self_or_null != NULL), CALL); - } - // _CHECK_STACK_SPACE - { - PyFunctionObject *func = (PyFunctionObject *)callable; - PyCodeObject *code = (PyCodeObject *)func->func_code; - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); - DEOPT_IF(tstate->py_recursion_remaining <= 1, CALL); - } - // _INIT_CALL_PY_EXACT_ARGS args = stack_pointer - oparg; - { - int argcount = oparg; - if (self_or_null != NULL) { - args--; - argcount++; - } - STAT_INC(CALL, hit); - PyFunctionObject *func = (PyFunctionObject *)callable; - new_frame = _PyFrame_PushUnchecked(tstate, func, argcount); - for (int i = 0; i < argcount; i++) { - new_frame->localsplus[i] = args[i]; - } - } - // _SAVE_RETURN_OFFSET - { - #if TIER_ONE - frame->return_offset = (uint16_t)(next_instr - this_instr); - #endif - #if TIER_TWO - frame->return_offset = oparg; - #endif - } - // _PUSH_FRAME - STACK_SHRINK(oparg); - STACK_SHRINK(2); - { - // Write it out explicitly because it's subtly different. - // Eventually this should be the only occurrence of this code. - assert(tstate->interp->eval_frame == NULL); - STORE_SP(); - new_frame->previous = frame; - CALL_STAT_INC(inlined_py_calls); - frame = tstate->current_frame = new_frame; - tstate->py_recursion_remaining--; - LOAD_SP(); - LOAD_IP(0); - #if LLTRACE && TIER_ONE - lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); - if (lltrace < 0) { - goto exit_unwind; + TIER_ONE_ONLY + PyObject *cause = NULL, *exc = NULL; + switch (oparg) { + case 2: + cause = args[1]; + /* fall through */ + case 1: + exc = args[0]; + /* fall through */ + case 0: + if (do_raise(tstate, exc, cause)) { + assert(oparg == 0); + monitor_reraise(tstate, frame, this_instr); + goto exception_unwind; } - #endif + break; + default: + _PyErr_SetString(tstate, PyExc_SystemError, + "bad RAISE_VARARGS oparg"); + break; } - DISPATCH(); + if (true) { STACK_SHRINK(oparg); goto error; } } - TARGET(CALL_PY_EXACT_ARGS) { + TARGET(RERAISE) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_PY_EXACT_ARGS); - PyObject *self_or_null; - PyObject *callable; - PyObject **args; - _PyInterpreterFrame *new_frame; - // _CHECK_PEP_523 - { - DEOPT_IF(tstate->interp->eval_frame, CALL); - } - // _CHECK_FUNCTION_EXACT_ARGS - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - { - uint32_t func_version = read_u32(&this_instr[2].cache); - DEOPT_IF(!PyFunction_Check(callable), CALL); - PyFunctionObject *func = (PyFunctionObject *)callable; - DEOPT_IF(func->func_version != func_version, CALL); - PyCodeObject *code = (PyCodeObject *)func->func_code; - DEOPT_IF(code->co_argcount != oparg + (self_or_null != NULL), CALL); - } - // _CHECK_STACK_SPACE - { - PyFunctionObject *func = (PyFunctionObject *)callable; - PyCodeObject *code = (PyCodeObject *)func->func_code; - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); - DEOPT_IF(tstate->py_recursion_remaining <= 1, CALL); - } - // _INIT_CALL_PY_EXACT_ARGS - args = stack_pointer - oparg; - { - int argcount = oparg; - if (self_or_null != NULL) { - args--; - argcount++; - } - STAT_INC(CALL, hit); - PyFunctionObject *func = (PyFunctionObject *)callable; - new_frame = _PyFrame_PushUnchecked(tstate, func, argcount); - for (int i = 0; i < argcount; i++) { - new_frame->localsplus[i] = args[i]; + next_instr += 1; + INSTRUCTION_STATS(RERAISE); + PyObject *exc; + PyObject **values; + exc = stack_pointer[-1]; + values = stack_pointer - 1 - oparg; + TIER_ONE_ONLY + assert(oparg >= 0 && oparg <= 2); + if (oparg) { + PyObject *lasti = values[0]; + if (PyLong_Check(lasti)) { + frame->instr_ptr = _PyCode_CODE(_PyFrame_GetCode(frame)) + PyLong_AsLong(lasti); + assert(!_PyErr_Occurred(tstate)); } - } - // _SAVE_RETURN_OFFSET - { - #if TIER_ONE - frame->return_offset = (uint16_t)(next_instr - this_instr); - #endif - #if TIER_TWO - frame->return_offset = oparg; - #endif - } - // _PUSH_FRAME - STACK_SHRINK(oparg); - STACK_SHRINK(2); - { - // Write it out explicitly because it's subtly different. - // Eventually this should be the only occurrence of this code. - assert(tstate->interp->eval_frame == NULL); - STORE_SP(); - new_frame->previous = frame; - CALL_STAT_INC(inlined_py_calls); - frame = tstate->current_frame = new_frame; - tstate->py_recursion_remaining--; - LOAD_SP(); - LOAD_IP(0); - #if LLTRACE && TIER_ONE - lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); - if (lltrace < 0) { - goto exit_unwind; + else { + assert(PyLong_Check(lasti)); + _PyErr_SetString(tstate, PyExc_SystemError, "lasti is not an int"); + GOTO_ERROR(error); } - #endif - } - DISPATCH(); - } - - TARGET(CALL_PY_WITH_DEFAULTS) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_PY_WITH_DEFAULTS); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - uint32_t func_version = read_u32(&this_instr[2].cache); - DEOPT_IF(tstate->interp->eval_frame, CALL); - int argcount = oparg; - if (self_or_null != NULL) { - args--; - argcount++; - } - DEOPT_IF(!PyFunction_Check(callable), CALL); - PyFunctionObject *func = (PyFunctionObject *)callable; - DEOPT_IF(func->func_version != func_version, CALL); - PyCodeObject *code = (PyCodeObject *)func->func_code; - assert(func->func_defaults); - assert(PyTuple_CheckExact(func->func_defaults)); - int defcount = (int)PyTuple_GET_SIZE(func->func_defaults); - assert(defcount <= code->co_argcount); - int min_args = code->co_argcount - defcount; - DEOPT_IF(argcount > code->co_argcount, CALL); - DEOPT_IF(argcount < min_args, CALL); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); - STAT_INC(CALL, hit); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, code->co_argcount); - for (int i = 0; i < argcount; i++) { - new_frame->localsplus[i] = args[i]; } - for (int i = argcount; i < code->co_argcount; i++) { - PyObject *def = PyTuple_GET_ITEM(func->func_defaults, i - min_args); - new_frame->localsplus[i] = Py_NewRef(def); - } - // Manipulate stack and cache directly since we leave using DISPATCH_INLINED(). - STACK_SHRINK(oparg + 2); - frame->return_offset = (uint16_t)(next_instr - this_instr); - DISPATCH_INLINED(new_frame); + assert(exc && PyExceptionInstance_Check(exc)); + Py_INCREF(exc); + _PyErr_SetRaisedException(tstate, exc); + monitor_reraise(tstate, frame, this_instr); + goto exception_unwind; } - TARGET(CALL_TYPE_1) { + TARGET(RESERVED) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_TYPE_1); - PyObject **args; - PyObject *null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - assert(oparg == 1); - DEOPT_IF(null != NULL, CALL); - PyObject *obj = args[0]; - DEOPT_IF(callable != (PyObject *)&PyType_Type, CALL); - STAT_INC(CALL, hit); - res = Py_NewRef(Py_TYPE(obj)); - Py_DECREF(obj); - Py_DECREF(&PyType_Type); // I.e., callable - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - DISPATCH(); + next_instr += 1; + INSTRUCTION_STATS(RESERVED); + assert(0 && "Executing RESERVED instruction."); + Py_UNREACHABLE(); } - TARGET(CALL_STR_1) { + TARGET(RESUME) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_STR_1); - PyObject **args; - PyObject *null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - assert(oparg == 1); - DEOPT_IF(null != NULL, CALL); - DEOPT_IF(callable != (PyObject *)&PyUnicode_Type, CALL); - STAT_INC(CALL, hit); - PyObject *arg = args[0]; - res = PyObject_Str(arg); - Py_DECREF(arg); - Py_DECREF(&PyUnicode_Type); // I.e., callable - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); + next_instr += 1; + INSTRUCTION_STATS(RESUME); + PREDICTED(RESUME); + _Py_CODEUNIT *this_instr = next_instr - 1; + static_assert(0 == 0, "incorrect cache size"); + TIER_ONE_ONLY + assert(frame == tstate->current_frame); + uintptr_t global_version = + _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker) & + ~_PY_EVAL_EVENTS_MASK; + uintptr_t code_version = _PyFrame_GetCode(frame)->_co_instrumentation_version; + assert((code_version & 255) == 0); + if (code_version != global_version) { + int err = _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp); + if (err) goto error; + next_instr = this_instr; + } + else { + if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { + CHECK_EVAL_BREAKER(); + } + this_instr->op.code = RESUME_CHECK; + } DISPATCH(); } - TARGET(CALL_TUPLE_1) { + TARGET(RESUME_CHECK) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_TUPLE_1); - PyObject **args; - PyObject *null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - assert(oparg == 1); - DEOPT_IF(null != NULL, CALL); - DEOPT_IF(callable != (PyObject *)&PyTuple_Type, CALL); - STAT_INC(CALL, hit); - PyObject *arg = args[0]; - res = PySequence_Tuple(arg); - Py_DECREF(arg); - Py_DECREF(&PyTuple_Type); // I.e., tuple - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); + next_instr += 1; + INSTRUCTION_STATS(RESUME_CHECK); +#if defined(__EMSCRIPTEN__) + DEOPT_IF(_Py_emscripten_signal_clock == 0, RESUME); + _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; +#endif + uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker); + uintptr_t version = _PyFrame_GetCode(frame)->_co_instrumentation_version; + assert((version & _PY_EVAL_EVENTS_MASK) == 0); + DEOPT_IF(eval_breaker != version, RESUME); DISPATCH(); } - TARGET(CALL_ALLOC_AND_ENTER_INIT) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_ALLOC_AND_ENTER_INIT); - PyObject **args; - PyObject *null; - PyObject *callable; - args = stack_pointer - oparg; - null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - /* This instruction does the following: - * 1. Creates the object (by calling ``object.__new__``) - * 2. Pushes a shim frame to the frame stack (to cleanup after ``__init__``) - * 3. Pushes the frame for ``__init__`` to the frame stack - * */ - _PyCallCache *cache = (_PyCallCache *)&this_instr[1]; - DEOPT_IF(null != NULL, CALL); - DEOPT_IF(!PyType_Check(callable), CALL); - PyTypeObject *tp = (PyTypeObject *)callable; - DEOPT_IF(tp->tp_version_tag != read_u32(cache->func_version), CALL); - PyHeapTypeObject *cls = (PyHeapTypeObject *)callable; - PyFunctionObject *init = (PyFunctionObject *)cls->_spec_cache.init; - PyCodeObject *code = (PyCodeObject *)init->func_code; - DEOPT_IF(code->co_argcount != oparg+1, CALL); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize + _Py_InitCleanup.co_framesize), CALL); - STAT_INC(CALL, hit); - PyObject *self = _PyType_NewManagedObject(tp); - if (self == NULL) { - GOTO_ERROR(error); + TARGET(RETURN_CONST) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(RETURN_CONST); + PyObject *value; + PyObject *retval; + // LOAD_CONST + { + value = GETITEM(FRAME_CO_CONSTS, oparg); + Py_INCREF(value); } - Py_DECREF(tp); - _PyInterpreterFrame *shim = _PyFrame_PushTrampolineUnchecked( - tstate, (PyCodeObject *)&_Py_InitCleanup, 1); - assert(_PyCode_CODE((PyCodeObject *)shim->f_executable)[0].op.code == EXIT_INIT_CHECK); - /* Push self onto stack of shim */ - Py_INCREF(self); - shim->localsplus[0] = self; - Py_INCREF(init); - _PyInterpreterFrame *init_frame = _PyFrame_PushUnchecked(tstate, init, oparg+1); - /* Copy self followed by args to __init__ frame */ - init_frame->localsplus[0] = self; - for (int i = 0; i < oparg; i++) { - init_frame->localsplus[i+1] = args[i]; + // _POP_FRAME + retval = value; + { + assert(EMPTY()); + #if TIER_ONE + assert(frame != &entry_frame); + #endif + STORE_SP(); + _Py_LeaveRecursiveCallPy(tstate); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = tstate->current_frame = dying->previous; + _PyEval_FrameClearAndPop(tstate, dying); + _PyFrame_StackPush(frame, retval); + LOAD_SP(); + LOAD_IP(frame->return_offset); + #if LLTRACE && TIER_ONE + lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); + if (lltrace < 0) { + goto exit_unwind; + } + #endif } - frame->return_offset = (uint16_t)(next_instr - this_instr); - STACK_SHRINK(oparg+2); - _PyFrame_SetStackPointer(frame, stack_pointer); - /* Link frames */ - init_frame->previous = shim; - shim->previous = frame; - frame = tstate->current_frame = init_frame; - CALL_STAT_INC(inlined_py_calls); - /* Account for pushing the extra frame. - * We don't check recursion depth here, - * as it will be checked after start_frame */ - tstate->py_recursion_remaining--; - goto start_frame; + DISPATCH(); } - TARGET(EXIT_INIT_CHECK) { + TARGET(RETURN_GENERATOR) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(EXIT_INIT_CHECK); - PyObject *should_be_none; - should_be_none = stack_pointer[-1]; - assert(STACK_LEVEL() == 2); - if (should_be_none != Py_None) { - PyErr_Format(PyExc_TypeError, - "__init__() should return None, not '%.200s'", - Py_TYPE(should_be_none)->tp_name); + INSTRUCTION_STATS(RETURN_GENERATOR); + assert(PyFunction_Check(frame->f_funcobj)); + PyFunctionObject *func = (PyFunctionObject *)frame->f_funcobj; + PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); + if (gen == NULL) { GOTO_ERROR(error); } + assert(EMPTY()); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; + frame->instr_ptr = next_instr; + _PyFrame_Copy(frame, gen_frame); + assert(frame->frame_obj == NULL); + gen->gi_frame_state = FRAME_CREATED; + gen_frame->owner = FRAME_OWNED_BY_GENERATOR; + _Py_LeaveRecursiveCallPy(tstate); + assert(frame != &entry_frame); + _PyInterpreterFrame *prev = frame->previous; + _PyThreadState_PopFrame(tstate, frame); + frame = tstate->current_frame = prev; + _PyFrame_StackPush(frame, (PyObject *)gen); + LOAD_IP(frame->return_offset); + goto resume_frame; + } + + TARGET(RETURN_VALUE) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(RETURN_VALUE); + PyObject *retval; + retval = stack_pointer[-1]; STACK_SHRINK(1); + assert(EMPTY()); + #if TIER_ONE + assert(frame != &entry_frame); + #endif + STORE_SP(); + _Py_LeaveRecursiveCallPy(tstate); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = tstate->current_frame = dying->previous; + _PyEval_FrameClearAndPop(tstate, dying); + _PyFrame_StackPush(frame, retval); + LOAD_SP(); + LOAD_IP(frame->return_offset); +#if LLTRACE && TIER_ONE + lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); + if (lltrace < 0) { + goto exit_unwind; + } +#endif DISPATCH(); } - TARGET(CALL_BUILTIN_CLASS) { + TARGET(SEND) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_BUILTIN_CLASS); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; + next_instr += 2; + INSTRUCTION_STATS(SEND); + PREDICTED(SEND); + _Py_CODEUNIT *this_instr = next_instr - 2; + static_assert(INLINE_CACHE_ENTRIES_SEND == 1, "incorrect cache size"); + PyObject *receiver; + PyObject *v; + PyObject *retval; + // _SPECIALIZE_SEND + receiver = stack_pointer[-2]; + { + uint16_t counter = read_u16(&this_instr[1].cache); + TIER_ONE_ONLY + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + next_instr = this_instr; + _Py_Specialize_Send(receiver, next_instr); + DISPATCH_SAME_OPARG(); + } + STAT_INC(SEND, deferred); + DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + #endif /* ENABLE_SPECIALIZATION */ } - DEOPT_IF(!PyType_Check(callable), CALL); - PyTypeObject *tp = (PyTypeObject *)callable; - DEOPT_IF(tp->tp_vectorcall == NULL, CALL); - STAT_INC(CALL, hit); - res = tp->tp_vectorcall((PyObject *)tp, args, total_args, NULL); - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - Py_DECREF(args[i]); + // _SEND + v = stack_pointer[-1]; + { + assert(frame != &entry_frame); + if ((tstate->interp->eval_frame == NULL) && + (Py_TYPE(receiver) == &PyGen_Type || Py_TYPE(receiver) == &PyCoro_Type) && + ((PyGenObject *)receiver)->gi_frame_state < FRAME_EXECUTING) + { + PyGenObject *gen = (PyGenObject *)receiver; + _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; + STACK_SHRINK(1); + _PyFrame_StackPush(gen_frame, v); + gen->gi_frame_state = FRAME_EXECUTING; + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + assert(next_instr - this_instr + oparg <= UINT16_MAX); + frame->return_offset = (uint16_t)(next_instr - this_instr + oparg); + DISPATCH_INLINED(gen_frame); + } + if (Py_IsNone(v) && PyIter_Check(receiver)) { + retval = Py_TYPE(receiver)->tp_iternext(receiver); + } + else { + retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v); + } + if (retval == NULL) { + if (_PyErr_ExceptionMatches(tstate, PyExc_StopIteration) + ) { + monitor_raise(tstate, frame, this_instr); + } + if (_PyGen_FetchStopIterationValue(&retval) == 0) { + assert(retval != NULL); + JUMPBY(oparg); + } + else { + GOTO_ERROR(error); + } + } + Py_DECREF(v); } - Py_DECREF(tp); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); + stack_pointer[-1] = retval; DISPATCH(); } - TARGET(CALL_BUILTIN_O) { + TARGET(SEND_GEN) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(SEND_GEN); + PyObject *v; + PyObject *receiver; + v = stack_pointer[-1]; + receiver = stack_pointer[-2]; + DEOPT_IF(tstate->interp->eval_frame, SEND); + PyGenObject *gen = (PyGenObject *)receiver; + DEOPT_IF(Py_TYPE(gen) != &PyGen_Type && Py_TYPE(gen) != &PyCoro_Type, SEND); + DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, SEND); + STAT_INC(SEND, hit); + _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; + STACK_SHRINK(1); + _PyFrame_StackPush(gen_frame, v); + gen->gi_frame_state = FRAME_EXECUTING; + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + assert(next_instr - this_instr + oparg <= UINT16_MAX); + frame->return_offset = (uint16_t)(next_instr - this_instr + oparg); + DISPATCH_INLINED(gen_frame); + } + + TARGET(SETUP_ANNOTATIONS) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_BUILTIN_O); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - /* Builtin METH_O functions */ - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; + next_instr += 1; + INSTRUCTION_STATS(SETUP_ANNOTATIONS); + int err; + PyObject *ann_dict; + if (LOCALS() == NULL) { + _PyErr_Format(tstate, PyExc_SystemError, + "no locals found when setting up annotations"); + if (true) goto error; } - DEOPT_IF(total_args != 1, CALL); - DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); - DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_O, CALL); - STAT_INC(CALL, hit); - PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); - // This is slower but CPython promises to check all non-vectorcall - // function calls. - if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { - GOTO_ERROR(error); + /* check if __annotations__ in locals()... */ + if (PyMapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict) < 0) goto error; + if (ann_dict == NULL) { + ann_dict = PyDict_New(); + if (ann_dict == NULL) goto error; + err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__), + ann_dict); + Py_DECREF(ann_dict); + if (err) goto error; + } + else { + Py_DECREF(ann_dict); } - PyObject *arg = args[0]; - res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg); - _Py_LeaveRecursiveCallTstate(tstate); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - - Py_DECREF(arg); - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(CALL_BUILTIN_FAST) { + TARGET(SET_ADD) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_BUILTIN_FAST); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - /* Builtin METH_FASTCALL functions, without keywords */ - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; - } - DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); - DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_FASTCALL, CALL); - STAT_INC(CALL, hit); - PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); - /* res = func(self, args, nargs) */ - res = ((_PyCFunctionFast)(void(*)(void))cfunc)( - PyCFunction_GET_SELF(callable), - args, - total_args); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - Py_DECREF(args[i]); - } - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - /* Not deopting because this doesn't mean our optimization was - wrong. `res` can be NULL for valid reasons. Eg. getattr(x, - 'invalid'). In those cases an exception is set, so we must - handle it. - */ - STACK_SHRINK(oparg); + next_instr += 1; + INSTRUCTION_STATS(SET_ADD); + PyObject *v; + PyObject *set; + v = stack_pointer[-1]; + set = stack_pointer[-2 - (oparg-1)]; + int err = PySet_Add(set, v); + Py_DECREF(v); + if (err) goto pop_1_error; STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) { + TARGET(SET_FUNCTION_ATTRIBUTE) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_BUILTIN_FAST_WITH_KEYWORDS); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; - } - DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); - DEOPT_IF(PyCFunction_GET_FLAGS(callable) != (METH_FASTCALL | METH_KEYWORDS), CALL); - STAT_INC(CALL, hit); - /* res = func(self, args, nargs, kwnames) */ - _PyCFunctionFastWithKeywords cfunc = - (_PyCFunctionFastWithKeywords)(void(*)(void)) - PyCFunction_GET_FUNCTION(callable); - res = cfunc(PyCFunction_GET_SELF(callable), args, total_args, NULL); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - Py_DECREF(args[i]); + next_instr += 1; + INSTRUCTION_STATS(SET_FUNCTION_ATTRIBUTE); + PyObject *func; + PyObject *attr; + func = stack_pointer[-1]; + attr = stack_pointer[-2]; + assert(PyFunction_Check(func)); + PyFunctionObject *func_obj = (PyFunctionObject *)func; + switch(oparg) { + case MAKE_FUNCTION_CLOSURE: + assert(func_obj->func_closure == NULL); + func_obj->func_closure = attr; + break; + case MAKE_FUNCTION_ANNOTATIONS: + assert(func_obj->func_annotations == NULL); + func_obj->func_annotations = attr; + break; + case MAKE_FUNCTION_KWDEFAULTS: + assert(PyDict_CheckExact(attr)); + assert(func_obj->func_kwdefaults == NULL); + func_obj->func_kwdefaults = attr; + break; + case MAKE_FUNCTION_DEFAULTS: + assert(PyTuple_CheckExact(attr)); + assert(func_obj->func_defaults == NULL); + func_obj->func_defaults = attr; + break; + default: + Py_UNREACHABLE(); } - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); + stack_pointer[-1] = func; DISPATCH(); } - TARGET(CALL_LEN) { + TARGET(SET_UPDATE) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_LEN); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - /* len(o) */ - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; - } - DEOPT_IF(total_args != 1, CALL); - PyInterpreterState *interp = tstate->interp; - DEOPT_IF(callable != interp->callable_cache.len, CALL); - STAT_INC(CALL, hit); - PyObject *arg = args[0]; - Py_ssize_t len_i = PyObject_Length(arg); - if (len_i < 0) { - GOTO_ERROR(error); - } - res = PyLong_FromSsize_t(len_i); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - - Py_DECREF(callable); - Py_DECREF(arg); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); + next_instr += 1; + INSTRUCTION_STATS(SET_UPDATE); + PyObject *iterable; + PyObject *set; + iterable = stack_pointer[-1]; + set = stack_pointer[-2 - (oparg-1)]; + int err = _PySet_Update(set, iterable); + Py_DECREF(iterable); + if (err < 0) goto pop_1_error; STACK_SHRINK(1); - stack_pointer[-1] = res; DISPATCH(); } - TARGET(CALL_ISINSTANCE) { + TARGET(STORE_ATTR) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_ISINSTANCE); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - /* isinstance(o, o2) */ - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; + next_instr += 5; + INSTRUCTION_STATS(STORE_ATTR); + PREDICTED(STORE_ATTR); + _Py_CODEUNIT *this_instr = next_instr - 5; + static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); + PyObject *owner; + PyObject *v; + // _SPECIALIZE_STORE_ATTR + owner = stack_pointer[-1]; + { + uint16_t counter = read_u16(&this_instr[1].cache); + TIER_ONE_ONLY + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + next_instr = this_instr; + _Py_Specialize_StoreAttr(owner, next_instr, name); + DISPATCH_SAME_OPARG(); + } + STAT_INC(STORE_ATTR, deferred); + DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + #endif /* ENABLE_SPECIALIZATION */ } - DEOPT_IF(total_args != 2, CALL); - PyInterpreterState *interp = tstate->interp; - DEOPT_IF(callable != interp->callable_cache.isinstance, CALL); - STAT_INC(CALL, hit); - PyObject *cls = args[1]; - PyObject *inst = args[0]; - int retval = PyObject_IsInstance(inst, cls); - if (retval < 0) { - GOTO_ERROR(error); + // _STORE_ATTR + v = stack_pointer[-2]; + { + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + int err = PyObject_SetAttr(owner, name, v); + Py_DECREF(v); + Py_DECREF(owner); + if (err) goto pop_2_error; } - res = PyBool_FromLong(retval); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - - Py_DECREF(inst); - Py_DECREF(cls); - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + STACK_SHRINK(2); DISPATCH(); } - TARGET(CALL_LIST_APPEND) { - frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_LIST_APPEND); - PyObject **args; - PyObject *self; - PyObject *callable; - args = stack_pointer - oparg; - self = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - assert(oparg == 1); - PyInterpreterState *interp = tstate->interp; - DEOPT_IF(callable != interp->callable_cache.list_append, CALL); - assert(self != NULL); - DEOPT_IF(!PyList_Check(self), CALL); - STAT_INC(CALL, hit); - if (_PyList_AppendTakeRef((PyListObject *)self, args[0]) < 0) { - goto pop_1_error; // Since arg is DECREF'ed already + TARGET(STORE_ATTR_INSTANCE_VALUE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 5; + INSTRUCTION_STATS(STORE_ATTR_INSTANCE_VALUE); + PyObject *owner; + PyObject *value; + // _GUARD_TYPE_VERSION + owner = stack_pointer[-1]; + { + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); } - Py_DECREF(self); - Py_DECREF(callable); - STACK_SHRINK(3); - // Skip POP_TOP - assert(next_instr->op.code == POP_TOP); - SKIP_OVER(1); + // _GUARD_DORV_VALUES + { + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + DEOPT_IF(!_PyDictOrValues_IsValues(dorv), STORE_ATTR); + } + // _STORE_ATTR_INSTANCE_VALUE + value = stack_pointer[-2]; + { + uint16_t index = read_u16(&this_instr[4].cache); + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + STAT_INC(STORE_ATTR, hit); + PyDictValues *values = _PyDictOrValues_GetValues(dorv); + PyObject *old_value = values->values[index]; + values->values[index] = value; + if (old_value == NULL) { + _PyDictValues_AddToInsertionOrder(values, index); + } + else { + Py_DECREF(old_value); + } + Py_DECREF(owner); + } + STACK_SHRINK(2); DISPATCH(); } - TARGET(CALL_METHOD_DESCRIPTOR_O) { - frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_O); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; + TARGET(STORE_ATTR_SLOT) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 5; + INSTRUCTION_STATS(STORE_ATTR_SLOT); + PyObject *owner; + PyObject *value; + // _GUARD_TYPE_VERSION + owner = stack_pointer[-1]; + { + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); } - PyMethodDescrObject *method = (PyMethodDescrObject *)callable; - DEOPT_IF(total_args != 2, CALL); - DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); - PyMethodDef *meth = method->d_method; - DEOPT_IF(meth->ml_flags != METH_O, CALL); - PyObject *arg = args[1]; - PyObject *self = args[0]; - DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); - STAT_INC(CALL, hit); - PyCFunction cfunc = meth->ml_meth; - // This is slower but CPython promises to check all non-vectorcall - // function calls. - if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { - GOTO_ERROR(error); + // _STORE_ATTR_SLOT + value = stack_pointer[-2]; + { + uint16_t index = read_u16(&this_instr[4].cache); + char *addr = (char *)owner + index; + STAT_INC(STORE_ATTR, hit); + PyObject *old_value = *(PyObject **)addr; + *(PyObject **)addr = value; + Py_XDECREF(old_value); + Py_DECREF(owner); } - res = _PyCFunction_TrampolineCall(cfunc, self, arg); - _Py_LeaveRecursiveCallTstate(tstate); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - Py_DECREF(self); - Py_DECREF(arg); - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); + STACK_SHRINK(2); DISPATCH(); } - TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) { - frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; - } - PyMethodDescrObject *method = (PyMethodDescrObject *)callable; - DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); - PyMethodDef *meth = method->d_method; - DEOPT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS), CALL); - PyTypeObject *d_type = method->d_common.d_type; - PyObject *self = args[0]; - DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL); - STAT_INC(CALL, hit); - int nargs = total_args - 1; - _PyCFunctionFastWithKeywords cfunc = - (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; - res = cfunc(self, args + 1, nargs, NULL); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - Py_DECREF(args[i]); + TARGET(STORE_ATTR_WITH_HINT) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 5; + INSTRUCTION_STATS(STORE_ATTR_WITH_HINT); + PyObject *owner; + PyObject *value; + owner = stack_pointer[-1]; + value = stack_pointer[-2]; + uint32_t type_version = read_u32(&this_instr[2].cache); + uint16_t hint = read_u16(&this_instr[4].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); + assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + DEOPT_IF(_PyDictOrValues_IsValues(dorv), STORE_ATTR); + PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + DEOPT_IF(dict == NULL, STORE_ATTR); + assert(PyDict_CheckExact((PyObject *)dict)); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR); + PyObject *old_value; + uint64_t new_version; + if (DK_IS_UNICODE(dict->ma_keys)) { + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; + DEOPT_IF(ep->me_key != name, STORE_ATTR); + old_value = ep->me_value; + DEOPT_IF(old_value == NULL, STORE_ATTR); + new_version = _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, value); + ep->me_value = value; } - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); + else { + PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + hint; + DEOPT_IF(ep->me_key != name, STORE_ATTR); + old_value = ep->me_value; + DEOPT_IF(old_value == NULL, STORE_ATTR); + new_version = _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, value); + ep->me_value = value; + } + Py_DECREF(old_value); + STAT_INC(STORE_ATTR, hit); + /* Ensure dict is GC tracked if it needs to be */ + if (!_PyObject_GC_IS_TRACKED(dict) && _PyObject_GC_MAY_BE_TRACKED(value)) { + _PyObject_GC_TRACK(dict); + } + /* PEP 509 */ + dict->ma_version_tag = new_version; + Py_DECREF(owner); + STACK_SHRINK(2); DISPATCH(); } - TARGET(CALL_METHOD_DESCRIPTOR_NOARGS) { + TARGET(STORE_DEREF) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_NOARGS); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - assert(oparg == 0 || oparg == 1); - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; - } - DEOPT_IF(total_args != 1, CALL); - PyMethodDescrObject *method = (PyMethodDescrObject *)callable; - DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); - PyMethodDef *meth = method->d_method; - PyObject *self = args[0]; - DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); - DEOPT_IF(meth->ml_flags != METH_NOARGS, CALL); - STAT_INC(CALL, hit); - PyCFunction cfunc = meth->ml_meth; - // This is slower but CPython promises to check all non-vectorcall - // function calls. - if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { - GOTO_ERROR(error); - } - res = _PyCFunction_TrampolineCall(cfunc, self, NULL); - _Py_LeaveRecursiveCallTstate(tstate); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - Py_DECREF(self); - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); + next_instr += 1; + INSTRUCTION_STATS(STORE_DEREF); + PyObject *v; + v = stack_pointer[-1]; + PyObject *cell = GETLOCAL(oparg); + PyObject *oldobj = PyCell_GET(cell); + PyCell_SET(cell, v); + Py_XDECREF(oldobj); STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(CALL_METHOD_DESCRIPTOR_FAST) { + TARGET(STORE_FAST) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_FAST); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; - } - PyMethodDescrObject *method = (PyMethodDescrObject *)callable; - /* Builtin METH_FASTCALL methods, without keywords */ - DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); - PyMethodDef *meth = method->d_method; - DEOPT_IF(meth->ml_flags != METH_FASTCALL, CALL); - PyObject *self = args[0]; - DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); - STAT_INC(CALL, hit); - _PyCFunctionFast cfunc = - (_PyCFunctionFast)(void(*)(void))meth->ml_meth; - int nargs = total_args - 1; - res = cfunc(self, args + 1, nargs); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - /* Clear the stack of the arguments. */ - for (int i = 0; i < total_args; i++) { - Py_DECREF(args[i]); - } - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); + next_instr += 1; + INSTRUCTION_STATS(STORE_FAST); + PyObject *value; + value = stack_pointer[-1]; + SETLOCAL(oparg, value); STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(INSTRUMENTED_CALL_KW) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + TARGET(STORE_FAST_LOAD_FAST) { + frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_CALL_KW); - int is_meth = PEEK(oparg + 2) != NULL; - int total_args = oparg + is_meth; - PyObject *function = PEEK(oparg + 3); - PyObject *arg = total_args == 0 ? &_PyInstrumentation_MISSING - : PEEK(total_args + 1); - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_CALL, - frame, this_instr, function, arg); - if (err) goto error; - GO_TO_INSTRUCTION(CALL_KW); + INSTRUCTION_STATS(STORE_FAST_LOAD_FAST); + PyObject *value1; + PyObject *value2; + value1 = stack_pointer[-1]; + uint32_t oparg1 = oparg >> 4; + uint32_t oparg2 = oparg & 15; + SETLOCAL(oparg1, value1); + value2 = GETLOCAL(oparg2); + Py_INCREF(value2); + stack_pointer[-1] = value2; + DISPATCH(); } - TARGET(CALL_KW) { + TARGET(STORE_FAST_STORE_FAST) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(CALL_KW); - PREDICTED(CALL_KW); - _Py_CODEUNIT *this_instr = next_instr - 1; - PyObject *kwnames; - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - kwnames = stack_pointer[-1]; - args = stack_pointer - 1 - oparg; - self_or_null = stack_pointer[-2 - oparg]; - callable = stack_pointer[-3 - oparg]; - // oparg counts all of the args, but *not* self: - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; - } - if (self_or_null == NULL && Py_TYPE(callable) == &PyMethod_Type) { - args--; - total_args++; - PyObject *self = ((PyMethodObject *)callable)->im_self; - args[0] = Py_NewRef(self); - PyObject *method = ((PyMethodObject *)callable)->im_func; - args[-1] = Py_NewRef(method); - Py_DECREF(callable); - callable = method; - } - int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames); - // Check if the call can be inlined or not - if (Py_TYPE(callable) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && - ((PyFunctionObject *)callable)->vectorcall == _PyFunction_Vectorcall) - { - int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable))->co_flags; - PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable)); - _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( - tstate, (PyFunctionObject *)callable, locals, - args, positional_args, kwnames - ); - Py_DECREF(kwnames); - // Manipulate stack directly since we leave using DISPATCH_INLINED(). - STACK_SHRINK(oparg + 3); - // The frame has stolen all the arguments from the stack, - // so there is no need to clean them up. - if (new_frame == NULL) { - GOTO_ERROR(error); - } - assert(next_instr - this_instr == 1); - frame->return_offset = 1; - DISPATCH_INLINED(new_frame); - } - /* Callable is not a normal Python function */ - res = PyObject_Vectorcall( - callable, args, - positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - kwnames); - if (opcode == INSTRUMENTED_CALL_KW) { - PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : args[0]; - if (res == NULL) { - _Py_call_instrumentation_exc2( - tstate, PY_MONITORING_EVENT_C_RAISE, - frame, this_instr, callable, arg); - } - else { - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_C_RETURN, - frame, this_instr, callable, arg); - if (err < 0) { - Py_CLEAR(res); - } - } - } - Py_DECREF(kwnames); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - Py_DECREF(callable); - for (int i = 0; i < total_args; i++) { - Py_DECREF(args[i]); - } - if (res == NULL) { STACK_SHRINK(oparg); goto pop_3_error; } - STACK_SHRINK(oparg); + INSTRUCTION_STATS(STORE_FAST_STORE_FAST); + PyObject *value1; + PyObject *value2; + value1 = stack_pointer[-1]; + value2 = stack_pointer[-2]; + uint32_t oparg1 = oparg >> 4; + uint32_t oparg2 = oparg & 15; + SETLOCAL(oparg1, value1); + SETLOCAL(oparg2, value2); STACK_SHRINK(2); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(INSTRUMENTED_CALL_FUNCTION_EX) { + TARGET(STORE_GLOBAL) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(STORE_GLOBAL); + PyObject *v; + v = stack_pointer[-1]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + int err = PyDict_SetItem(GLOBALS(), name, v); + Py_DECREF(v); + if (err) goto pop_1_error; + STACK_SHRINK(1); + DISPATCH(); + } + + TARGET(STORE_NAME) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_CALL_FUNCTION_EX); - GO_TO_INSTRUCTION(CALL_FUNCTION_EX); + INSTRUCTION_STATS(STORE_NAME); + PyObject *v; + v = stack_pointer[-1]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + PyObject *ns = LOCALS(); + int err; + if (ns == NULL) { + _PyErr_Format(tstate, PyExc_SystemError, + "no locals found when storing %R", name); + Py_DECREF(v); + if (true) goto pop_1_error; + } + if (PyDict_CheckExact(ns)) + err = PyDict_SetItem(ns, name, v); + else + err = PyObject_SetItem(ns, name, v); + Py_DECREF(v); + if (err) goto pop_1_error; + STACK_SHRINK(1); + DISPATCH(); } - TARGET(CALL_FUNCTION_EX) { + TARGET(STORE_SLICE) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(CALL_FUNCTION_EX); - PREDICTED(CALL_FUNCTION_EX); - _Py_CODEUNIT *this_instr = next_instr - 1; - PyObject *kwargs = NULL; - PyObject *callargs; - PyObject *func; - PyObject *result; - if (oparg & 1) { kwargs = stack_pointer[-(oparg & 1 ? 1 : 0)]; } - callargs = stack_pointer[-1 - (oparg & 1 ? 1 : 0)]; - func = stack_pointer[-3 - (oparg & 1 ? 1 : 0)]; - // DICT_MERGE is called before this opcode if there are kwargs. - // It converts all dict subtypes in kwargs into regular dicts. - assert(kwargs == NULL || PyDict_CheckExact(kwargs)); - if (!PyTuple_CheckExact(callargs)) { - if (check_args_iterable(tstate, func, callargs) < 0) { - GOTO_ERROR(error); - } - PyObject *tuple = PySequence_Tuple(callargs); - if (tuple == NULL) { - GOTO_ERROR(error); - } - Py_SETREF(callargs, tuple); + INSTRUCTION_STATS(STORE_SLICE); + PyObject *stop; + PyObject *start; + PyObject *container; + PyObject *v; + stop = stack_pointer[-1]; + start = stack_pointer[-2]; + container = stack_pointer[-3]; + v = stack_pointer[-4]; + PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop); + int err; + if (slice == NULL) { + err = 1; } - assert(PyTuple_CheckExact(callargs)); - EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_FUNCTION_EX, func); - if (opcode == INSTRUMENTED_CALL_FUNCTION_EX && - !PyFunction_Check(func) && !PyMethod_Check(func) - ) { - PyObject *arg = PyTuple_GET_SIZE(callargs) > 0 ? - PyTuple_GET_ITEM(callargs, 0) : Py_None; - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_CALL, - frame, this_instr, func, arg); - if (err) GOTO_ERROR(error); - result = PyObject_Call(func, callargs, kwargs); - if (result == NULL) { - _Py_call_instrumentation_exc2( - tstate, PY_MONITORING_EVENT_C_RAISE, - frame, this_instr, func, arg); - } - else { - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_C_RETURN, - frame, this_instr, func, arg); - if (err < 0) { - Py_CLEAR(result); - } + else { + err = PyObject_SetItem(container, slice, v); + Py_DECREF(slice); + } + Py_DECREF(v); + Py_DECREF(container); + if (err) goto pop_4_error; + STACK_SHRINK(4); + DISPATCH(); + } + + TARGET(STORE_SUBSCR) { + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(STORE_SUBSCR); + PREDICTED(STORE_SUBSCR); + _Py_CODEUNIT *this_instr = next_instr - 2; + static_assert(INLINE_CACHE_ENTRIES_STORE_SUBSCR == 1, "incorrect cache size"); + PyObject *sub; + PyObject *container; + PyObject *v; + // _SPECIALIZE_STORE_SUBSCR + sub = stack_pointer[-1]; + container = stack_pointer[-2]; + { + uint16_t counter = read_u16(&this_instr[1].cache); + TIER_ONE_ONLY + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + next_instr = this_instr; + _Py_Specialize_StoreSubscr(container, sub, next_instr); + DISPATCH_SAME_OPARG(); } + STAT_INC(STORE_SUBSCR, deferred); + DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + #endif /* ENABLE_SPECIALIZATION */ } - else { - if (Py_TYPE(func) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && - ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { - assert(PyTuple_CheckExact(callargs)); - Py_ssize_t nargs = PyTuple_GET_SIZE(callargs); - int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags; - PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func)); + // _STORE_SUBSCR + v = stack_pointer[-3]; + { + /* container[sub] = v */ + int err = PyObject_SetItem(container, sub, v); + Py_DECREF(v); + Py_DECREF(container); + Py_DECREF(sub); + if (err) goto pop_3_error; + } + STACK_SHRINK(3); + DISPATCH(); + } - _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex(tstate, - (PyFunctionObject *)func, locals, - nargs, callargs, kwargs); - // Need to manually shrink the stack since we exit with DISPATCH_INLINED. - STACK_SHRINK(oparg + 3); - if (new_frame == NULL) { - GOTO_ERROR(error); - } - assert(next_instr - this_instr == 1); - frame->return_offset = 1; - DISPATCH_INLINED(new_frame); + TARGET(STORE_SUBSCR_DICT) { + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(STORE_SUBSCR_DICT); + PyObject *sub; + PyObject *dict; + PyObject *value; + sub = stack_pointer[-1]; + dict = stack_pointer[-2]; + value = stack_pointer[-3]; + DEOPT_IF(!PyDict_CheckExact(dict), STORE_SUBSCR); + STAT_INC(STORE_SUBSCR, hit); + int err = _PyDict_SetItem_Take2((PyDictObject *)dict, sub, value); + Py_DECREF(dict); + if (err) goto pop_3_error; + STACK_SHRINK(3); + DISPATCH(); + } + + TARGET(STORE_SUBSCR_LIST_INT) { + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(STORE_SUBSCR_LIST_INT); + PyObject *sub; + PyObject *list; + PyObject *value; + sub = stack_pointer[-1]; + list = stack_pointer[-2]; + value = stack_pointer[-3]; + DEOPT_IF(!PyLong_CheckExact(sub), STORE_SUBSCR); + DEOPT_IF(!PyList_CheckExact(list), STORE_SUBSCR); + + // Ensure nonnegative, zero-or-one-digit ints. + DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), STORE_SUBSCR); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + // Ensure index < len(list) + DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR); + STAT_INC(STORE_SUBSCR, hit); + + PyObject *old_value = PyList_GET_ITEM(list, index); + PyList_SET_ITEM(list, index, value); + assert(old_value != NULL); + Py_DECREF(old_value); + _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); + Py_DECREF(list); + STACK_SHRINK(3); + DISPATCH(); + } + + TARGET(SWAP) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(SWAP); + PyObject *top; + PyObject *bottom; + top = stack_pointer[-1]; + bottom = stack_pointer[-2 - (oparg-2)]; + assert(oparg >= 2); + stack_pointer[-2 - (oparg-2)] = top; + stack_pointer[-1] = bottom; + DISPATCH(); + } + + TARGET(TO_BOOL) { + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL); + PREDICTED(TO_BOOL); + _Py_CODEUNIT *this_instr = next_instr - 4; + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); + PyObject *value; + PyObject *res; + // _SPECIALIZE_TO_BOOL + value = stack_pointer[-1]; + { + uint16_t counter = read_u16(&this_instr[1].cache); + TIER_ONE_ONLY + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + next_instr = this_instr; + _Py_Specialize_ToBool(value, next_instr); + DISPATCH_SAME_OPARG(); } - result = PyObject_Call(func, callargs, kwargs); + STAT_INC(TO_BOOL, deferred); + DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + #endif /* ENABLE_SPECIALIZATION */ } - Py_DECREF(func); - Py_DECREF(callargs); - Py_XDECREF(kwargs); - assert(PEEK(2 + (oparg & 1)) == NULL); - if (result == NULL) { STACK_SHRINK(((oparg & 1) ? 1 : 0)); goto pop_3_error; } - STACK_SHRINK(((oparg & 1) ? 1 : 0)); - STACK_SHRINK(2); - stack_pointer[-1] = result; - CHECK_EVAL_BREAKER(); + // _TO_BOOL + { + int err = PyObject_IsTrue(value); + Py_DECREF(value); + if (err < 0) goto pop_1_error; + res = err ? Py_True : Py_False; + } + stack_pointer[-1] = res; DISPATCH(); } - TARGET(MAKE_FUNCTION) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(MAKE_FUNCTION); - PyObject *codeobj; - PyObject *func; - codeobj = stack_pointer[-1]; - - PyFunctionObject *func_obj = (PyFunctionObject *) - PyFunction_New(codeobj, GLOBALS()); - - Py_DECREF(codeobj); - if (func_obj == NULL) { - GOTO_ERROR(error); - } + TARGET(TO_BOOL_ALWAYS_TRUE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_ALWAYS_TRUE); + PyObject *value; + PyObject *res; + value = stack_pointer[-1]; + uint32_t version = read_u32(&this_instr[2].cache); + // This one is a bit weird, because we expect *some* failures: + assert(version); + DEOPT_IF(Py_TYPE(value)->tp_version_tag != version, TO_BOOL); + STAT_INC(TO_BOOL, hit); + Py_DECREF(value); + res = Py_True; + stack_pointer[-1] = res; + DISPATCH(); + } - _PyFunction_SetVersion( - func_obj, ((PyCodeObject *)codeobj)->co_version); - func = (PyObject *)func_obj; - stack_pointer[-1] = func; + TARGET(TO_BOOL_BOOL) { + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_BOOL); + PyObject *value; + value = stack_pointer[-1]; + DEOPT_IF(!PyBool_Check(value), TO_BOOL); + STAT_INC(TO_BOOL, hit); DISPATCH(); } - TARGET(SET_FUNCTION_ATTRIBUTE) { + TARGET(TO_BOOL_INT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(SET_FUNCTION_ATTRIBUTE); - PyObject *func; - PyObject *attr; - func = stack_pointer[-1]; - attr = stack_pointer[-2]; - assert(PyFunction_Check(func)); - PyFunctionObject *func_obj = (PyFunctionObject *)func; - switch(oparg) { - case MAKE_FUNCTION_CLOSURE: - assert(func_obj->func_closure == NULL); - func_obj->func_closure = attr; - break; - case MAKE_FUNCTION_ANNOTATIONS: - assert(func_obj->func_annotations == NULL); - func_obj->func_annotations = attr; - break; - case MAKE_FUNCTION_KWDEFAULTS: - assert(PyDict_CheckExact(attr)); - assert(func_obj->func_kwdefaults == NULL); - func_obj->func_kwdefaults = attr; - break; - case MAKE_FUNCTION_DEFAULTS: - assert(PyTuple_CheckExact(attr)); - assert(func_obj->func_defaults == NULL); - func_obj->func_defaults = attr; - break; - default: - Py_UNREACHABLE(); + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_INT); + PyObject *value; + PyObject *res; + value = stack_pointer[-1]; + DEOPT_IF(!PyLong_CheckExact(value), TO_BOOL); + STAT_INC(TO_BOOL, hit); + if (_PyLong_IsZero((PyLongObject *)value)) { + assert(_Py_IsImmortal(value)); + res = Py_False; } - STACK_SHRINK(1); - stack_pointer[-1] = func; + else { + Py_DECREF(value); + res = Py_True; + } + stack_pointer[-1] = res; DISPATCH(); } - TARGET(RETURN_GENERATOR) { + TARGET(TO_BOOL_LIST) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(RETURN_GENERATOR); - assert(PyFunction_Check(frame->f_funcobj)); - PyFunctionObject *func = (PyFunctionObject *)frame->f_funcobj; - PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); - if (gen == NULL) { - GOTO_ERROR(error); - } - assert(EMPTY()); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_LIST); + PyObject *value; + PyObject *res; + value = stack_pointer[-1]; + DEOPT_IF(!PyList_CheckExact(value), TO_BOOL); + STAT_INC(TO_BOOL, hit); + res = Py_SIZE(value) ? Py_True : Py_False; + Py_DECREF(value); + stack_pointer[-1] = res; + DISPATCH(); + } + + TARGET(TO_BOOL_NONE) { frame->instr_ptr = next_instr; - _PyFrame_Copy(frame, gen_frame); - assert(frame->frame_obj == NULL); - gen->gi_frame_state = FRAME_CREATED; - gen_frame->owner = FRAME_OWNED_BY_GENERATOR; - _Py_LeaveRecursiveCallPy(tstate); - assert(frame != &entry_frame); - _PyInterpreterFrame *prev = frame->previous; - _PyThreadState_PopFrame(tstate, frame); - frame = tstate->current_frame = prev; - _PyFrame_StackPush(frame, (PyObject *)gen); - LOAD_IP(frame->return_offset); - goto resume_frame; + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_NONE); + PyObject *value; + PyObject *res; + value = stack_pointer[-1]; + // This one is a bit weird, because we expect *some* failures: + DEOPT_IF(!Py_IsNone(value), TO_BOOL); + STAT_INC(TO_BOOL, hit); + res = Py_False; + stack_pointer[-1] = res; + DISPATCH(); } - TARGET(BUILD_SLICE) { + TARGET(TO_BOOL_STR) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(BUILD_SLICE); - PyObject *step = NULL; - PyObject *stop; - PyObject *start; - PyObject *slice; - if (oparg == 3) { step = stack_pointer[-(oparg == 3 ? 1 : 0)]; } - stop = stack_pointer[-1 - (oparg == 3 ? 1 : 0)]; - start = stack_pointer[-2 - (oparg == 3 ? 1 : 0)]; - slice = PySlice_New(start, stop, step); - Py_DECREF(start); - Py_DECREF(stop); - Py_XDECREF(step); - if (slice == NULL) { STACK_SHRINK(((oparg == 3) ? 1 : 0)); goto pop_2_error; } - STACK_SHRINK(((oparg == 3) ? 1 : 0)); - STACK_SHRINK(1); - stack_pointer[-1] = slice; + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_STR); + PyObject *value; + PyObject *res; + value = stack_pointer[-1]; + DEOPT_IF(!PyUnicode_CheckExact(value), TO_BOOL); + STAT_INC(TO_BOOL, hit); + if (value == &_Py_STR(empty)) { + assert(_Py_IsImmortal(value)); + res = Py_False; + } + else { + assert(Py_SIZE(value)); + Py_DECREF(value); + res = Py_True; + } + stack_pointer[-1] = res; DISPATCH(); } - TARGET(CONVERT_VALUE) { + TARGET(UNARY_INVERT) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(CONVERT_VALUE); + INSTRUCTION_STATS(UNARY_INVERT); PyObject *value; - PyObject *result; + PyObject *res; value = stack_pointer[-1]; - convertion_func_ptr conv_fn; - assert(oparg >= FVC_STR && oparg <= FVC_ASCII); - conv_fn = CONVERSION_FUNCTIONS[oparg]; - result = conv_fn(value); + res = PyNumber_Invert(value); Py_DECREF(value); - if (result == NULL) goto pop_1_error; - stack_pointer[-1] = result; + if (res == NULL) goto pop_1_error; + stack_pointer[-1] = res; DISPATCH(); } - TARGET(FORMAT_SIMPLE) { + TARGET(UNARY_NEGATIVE) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(FORMAT_SIMPLE); + INSTRUCTION_STATS(UNARY_NEGATIVE); PyObject *value; PyObject *res; value = stack_pointer[-1]; - /* If value is a unicode object, then we know the result - * of format(value) is value itself. */ - if (!PyUnicode_CheckExact(value)) { - res = PyObject_Format(value, NULL); - Py_DECREF(value); - if (res == NULL) goto pop_1_error; - } - else { - res = value; - } + res = PyNumber_Negative(value); + Py_DECREF(value); + if (res == NULL) goto pop_1_error; stack_pointer[-1] = res; DISPATCH(); } - TARGET(FORMAT_WITH_SPEC) { + TARGET(UNARY_NOT) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(FORMAT_WITH_SPEC); - PyObject *fmt_spec; + INSTRUCTION_STATS(UNARY_NOT); PyObject *value; PyObject *res; - fmt_spec = stack_pointer[-1]; - value = stack_pointer[-2]; - res = PyObject_Format(value, fmt_spec); - Py_DECREF(value); - Py_DECREF(fmt_spec); - if (res == NULL) goto pop_2_error; - STACK_SHRINK(1); + value = stack_pointer[-1]; + assert(PyBool_Check(value)); + res = Py_IsFalse(value) ? Py_True : Py_False; stack_pointer[-1] = res; DISPATCH(); } - TARGET(COPY) { + TARGET(UNPACK_EX) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(COPY); - PyObject *bottom; - PyObject *top; - bottom = stack_pointer[-1 - (oparg-1)]; - assert(oparg > 0); - top = Py_NewRef(bottom); - STACK_GROW(1); - stack_pointer[-1] = top; + INSTRUCTION_STATS(UNPACK_EX); + PyObject *seq; + seq = stack_pointer[-1]; + int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8); + PyObject **top = stack_pointer + totalargs - 1; + int res = _PyEval_UnpackIterable(tstate, seq, oparg & 0xFF, oparg >> 8, top); + Py_DECREF(seq); + if (res == 0) goto pop_1_error; + STACK_GROW((oparg & 0xFF) + (oparg >> 8)); DISPATCH(); } - TARGET(BINARY_OP) { + TARGET(UNPACK_SEQUENCE) { frame->instr_ptr = next_instr; next_instr += 2; - INSTRUCTION_STATS(BINARY_OP); - PREDICTED(BINARY_OP); - _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); - PyObject *rhs; - PyObject *lhs; - PyObject *res; - // _SPECIALIZE_BINARY_OP - rhs = stack_pointer[-1]; - lhs = stack_pointer[-2]; + INSTRUCTION_STATS(UNPACK_SEQUENCE); + PREDICTED(UNPACK_SEQUENCE); + _Py_CODEUNIT *this_instr = next_instr - 2; + static_assert(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE == 1, "incorrect cache size"); + PyObject *seq; + // _SPECIALIZE_UNPACK_SEQUENCE + seq = stack_pointer[-1]; { uint16_t counter = read_u16(&this_instr[1].cache); TIER_ONE_ONLY #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { next_instr = this_instr; - _Py_Specialize_BinaryOp(lhs, rhs, next_instr, oparg, LOCALS_ARRAY); + _Py_Specialize_UnpackSequence(seq, next_instr, oparg); DISPATCH_SAME_OPARG(); } - STAT_INC(BINARY_OP, deferred); + STAT_INC(UNPACK_SEQUENCE, deferred); DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); #endif /* ENABLE_SPECIALIZATION */ - assert(NB_ADD <= oparg); - assert(oparg <= NB_INPLACE_XOR); + (void)seq; + (void)counter; } - // _BINARY_OP + // _UNPACK_SEQUENCE { - assert(_PyEval_BinaryOps[oparg]); - res = _PyEval_BinaryOps[oparg](lhs, rhs); - Py_DECREF(lhs); - Py_DECREF(rhs); - if (res == NULL) goto pop_2_error; + PyObject **top = stack_pointer + oparg - 1; + int res = _PyEval_UnpackIterable(tstate, seq, oparg, -1, top); + Py_DECREF(seq); + if (res == 0) goto pop_1_error; } STACK_SHRINK(1); - stack_pointer[-1] = res; + STACK_GROW(oparg); DISPATCH(); } - TARGET(SWAP) { + TARGET(UNPACK_SEQUENCE_LIST) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(SWAP); - PyObject *top; - PyObject *bottom; - top = stack_pointer[-1]; - bottom = stack_pointer[-2 - (oparg-2)]; - assert(oparg >= 2); - stack_pointer[-2 - (oparg-2)] = top; - stack_pointer[-1] = bottom; - DISPATCH(); - } - - TARGET(INSTRUMENTED_INSTRUCTION) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_INSTRUCTION); - int next_opcode = _Py_call_instrumentation_instruction( - tstate, frame, this_instr); - if (next_opcode < 0) goto error; - next_instr = this_instr; - if (_PyOpcode_Caches[next_opcode]) { - INCREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - } - assert(next_opcode > 0 && next_opcode < 256); - opcode = next_opcode; - DISPATCH_GOTO(); - } - - TARGET(INSTRUMENTED_JUMP_FORWARD) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_JUMP_FORWARD); - INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_JUMP); - DISPATCH(); - } - - TARGET(INSTRUMENTED_JUMP_BACKWARD) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 2; - INSTRUCTION_STATS(INSTRUMENTED_JUMP_BACKWARD); - CHECK_EVAL_BREAKER(); - INSTRUMENTED_JUMP(this_instr, next_instr - oparg, PY_MONITORING_EVENT_JUMP); + INSTRUCTION_STATS(UNPACK_SEQUENCE_LIST); + PyObject *seq; + PyObject **values; + seq = stack_pointer[-1]; + values = stack_pointer - 1; + DEOPT_IF(!PyList_CheckExact(seq), UNPACK_SEQUENCE); + DEOPT_IF(PyList_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); + STAT_INC(UNPACK_SEQUENCE, hit); + PyObject **items = _PyList_ITEMS(seq); + for (int i = oparg; --i >= 0; ) { + *values++ = Py_NewRef(items[i]); + } + Py_DECREF(seq); + STACK_SHRINK(1); + STACK_GROW(oparg); DISPATCH(); } - TARGET(INSTRUMENTED_POP_JUMP_IF_TRUE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + TARGET(UNPACK_SEQUENCE_TUPLE) { + frame->instr_ptr = next_instr; next_instr += 2; - INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_TRUE); - PyObject *cond = POP(); - assert(PyBool_Check(cond)); - int flag = Py_IsTrue(cond); - int offset = flag * oparg; - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif - INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + INSTRUCTION_STATS(UNPACK_SEQUENCE_TUPLE); + PyObject *seq; + PyObject **values; + seq = stack_pointer[-1]; + values = stack_pointer - 1; + DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); + DEOPT_IF(PyTuple_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); + STAT_INC(UNPACK_SEQUENCE, hit); + PyObject **items = _PyTuple_ITEMS(seq); + for (int i = oparg; --i >= 0; ) { + *values++ = Py_NewRef(items[i]); + } + Py_DECREF(seq); + STACK_SHRINK(1); + STACK_GROW(oparg); DISPATCH(); } - TARGET(INSTRUMENTED_POP_JUMP_IF_FALSE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + TARGET(UNPACK_SEQUENCE_TWO_TUPLE) { + frame->instr_ptr = next_instr; next_instr += 2; - INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_FALSE); - PyObject *cond = POP(); - assert(PyBool_Check(cond)); - int flag = Py_IsFalse(cond); - int offset = flag * oparg; - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif - INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + INSTRUCTION_STATS(UNPACK_SEQUENCE_TWO_TUPLE); + PyObject *seq; + PyObject **values; + seq = stack_pointer[-1]; + values = stack_pointer - 1; + DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); + DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE); + assert(oparg == 2); + STAT_INC(UNPACK_SEQUENCE, hit); + values[0] = Py_NewRef(PyTuple_GET_ITEM(seq, 1)); + values[1] = Py_NewRef(PyTuple_GET_ITEM(seq, 0)); + Py_DECREF(seq); + STACK_SHRINK(1); + STACK_GROW(oparg); DISPATCH(); } - TARGET(INSTRUMENTED_POP_JUMP_IF_NONE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NONE); - PyObject *value = POP(); - int flag = Py_IsNone(value); - int offset; - if (flag) { - offset = oparg; - } - else { - Py_DECREF(value); - offset = 0; - } - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif - INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); - DISPATCH(); - } + TARGET(WITH_EXCEPT_START) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(WITH_EXCEPT_START); + PyObject *val; + PyObject *lasti; + PyObject *exit_func; + PyObject *res; + val = stack_pointer[-1]; + lasti = stack_pointer[-3]; + exit_func = stack_pointer[-4]; + /* At the top of the stack are 4 values: + - val: TOP = exc_info() + - unused: SECOND = previous exception + - lasti: THIRD = lasti of exception in exc_info() + - exit_func: FOURTH = the context.__exit__ bound method + We call FOURTH(type(TOP), TOP, GetTraceback(TOP)). + Then we push the __exit__ return value. + */ + PyObject *exc, *tb; - TARGET(INSTRUMENTED_POP_JUMP_IF_NOT_NONE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NOT_NONE); - PyObject *value = POP(); - int offset; - int nflag = Py_IsNone(value); - if (nflag) { - offset = 0; + assert(val && PyExceptionInstance_Check(val)); + exc = PyExceptionInstance_Class(val); + tb = PyException_GetTraceback(val); + if (tb == NULL) { + tb = Py_None; } else { - Py_DECREF(value); - offset = oparg; + Py_DECREF(tb); } - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | !nflag; - #endif - INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + assert(PyLong_Check(lasti)); + (void)lasti; // Shut up compiler warning if asserts are off + PyObject *stack[4] = {NULL, exc, val, tb}; + res = PyObject_Vectorcall(exit_func, stack + 1, + 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); + if (res == NULL) goto error; + STACK_GROW(1); + stack_pointer[-1] = res; DISPATCH(); } - TARGET(EXTENDED_ARG) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(EXTENDED_ARG); - assert(oparg); - opcode = next_instr->op.code; - oparg = oparg << 8 | next_instr->op.arg; - PRE_DISPATCH_GOTO(); - DISPATCH_GOTO(); - } - - TARGET(CACHE) { + TARGET(YIELD_VALUE) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(CACHE); - assert(0 && "Executing a cache."); - Py_UNREACHABLE(); - } - - TARGET(RESERVED) { + INSTRUCTION_STATS(YIELD_VALUE); + PyObject *retval; + retval = stack_pointer[-1]; + // NOTE: It's important that YIELD_VALUE never raises an exception! + // The compiler treats any exception raised here as a failed close() + // or throw() call. + assert(frame != &entry_frame); frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(RESERVED); - assert(0 && "Executing RESERVED instruction."); - Py_UNREACHABLE(); + PyGenObject *gen = _PyFrame_GetGenerator(frame); + assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); + assert(oparg == 0 || oparg == 1); + gen->gi_frame_state = FRAME_SUSPENDED + oparg; + _PyFrame_SetStackPointer(frame, stack_pointer - 1); + tstate->exc_info = gen->gi_exc_state.previous_item; + gen->gi_exc_state.previous_item = NULL; + _Py_LeaveRecursiveCallPy(tstate); + _PyInterpreterFrame *gen_frame = frame; + frame = tstate->current_frame = frame->previous; + gen_frame->previous = NULL; + _PyFrame_StackPush(frame, retval); + /* We don't know which of these is relevant here, so keep them equal */ + assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); + LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); + goto resume_frame; } #undef TIER_ONE diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 851bd2f53879e5..1f94c1fedb2ac7 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -769,6 +769,7 @@ def write_instructions( # Write and count instructions of all kinds n_macros = 0 + cases = [] for thing in self.everything: match thing: case parsing.InstDef(): @@ -776,11 +777,14 @@ def write_instructions( case parsing.Macro(): n_macros += 1 mac = self.macro_instrs[thing.name] - stacking.write_macro_instr(mac, self.out) + cases.append((mac.name, mac)) case parsing.Pseudo(): pass case _: assert_never(thing) + cases.sort() + for _, mac in cases: + stacking.write_macro_instr(mac, self.out) self.out.write_raw("\n") self.out.write_raw("#undef TIER_ONE\n") From 1c5279b9d63ab5b8562c42cad18b042aab931758 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Wed, 22 Nov 2023 22:24:48 +0100 Subject: [PATCH 037/228] Post 3.13.0a2 --- Include/patchlevel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 592491eed5ff84..fad79ecfda7b28 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -23,7 +23,7 @@ #define PY_RELEASE_SERIAL 2 /* Version as a string */ -#define PY_VERSION "3.13.0a2" +#define PY_VERSION "3.13.0a2+" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. From 5c3a129ecfbaac107bccf3083533276ee0ccc036 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 22 Nov 2023 14:48:45 -0700 Subject: [PATCH 038/228] gh-76785: Clean up the Failure-Related _xxsubinterpreters Tests (gh-112322) --- Lib/test/test__xxsubinterpreters.py | 194 ++++++++++++++++++++++------ 1 file changed, 157 insertions(+), 37 deletions(-) diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index 97314ddbb55ec8..d2056c9f52287b 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -10,6 +10,7 @@ import _testinternalcapi from test import support from test.support import import_helper +from test.support import os_helper from test.support import script_helper @@ -759,43 +760,6 @@ def test_bytes_for_script(self): with self.assertRaises(TypeError): interpreters.run_string(self.id, b'print("spam")') - @contextlib.contextmanager - def assert_run_failed(self, exctype, msg=None): - with self.assertRaises(interpreters.RunFailedError) as caught: - yield - if msg is None: - self.assertEqual(str(caught.exception).split(':')[0], - exctype.__name__) - else: - self.assertEqual(str(caught.exception), - "{}: {}".format(exctype.__name__, msg)) - - def test_invalid_syntax(self): - with self.assert_run_failed(SyntaxError): - # missing close paren - interpreters.run_string(self.id, 'print("spam"') - - def test_failure(self): - with self.assert_run_failed(Exception, 'spam'): - interpreters.run_string(self.id, 'raise Exception("spam")') - - def test_SystemExit(self): - with self.assert_run_failed(SystemExit, '42'): - interpreters.run_string(self.id, 'raise SystemExit(42)') - - def test_sys_exit(self): - with self.assert_run_failed(SystemExit): - interpreters.run_string(self.id, dedent(""" - import sys - sys.exit() - """)) - - with self.assert_run_failed(SystemExit, '42'): - interpreters.run_string(self.id, dedent(""" - import sys - sys.exit(42) - """)) - def test_with_shared(self): r, w = os.pipe() @@ -959,6 +923,162 @@ def f(): self.assertEqual(retcode, 0) +class RunFailedTests(TestBase): + + def setUp(self): + super().setUp() + self.id = interpreters.create() + + def add_module(self, modname, text): + import tempfile + tempdir = tempfile.mkdtemp() + self.addCleanup(lambda: os_helper.rmtree(tempdir)) + interpreters.run_string(self.id, dedent(f""" + import sys + sys.path.insert(0, {tempdir!r}) + """)) + return script_helper.make_script(tempdir, modname, text) + + def run_script(self, text, *, fails=False): + excwrapper = interpreters.RunFailedError + r, w = os.pipe() + try: + script = dedent(f""" + import os, sys + os.write({w}, b'0') + + # This raises an exception: + {{}} + + # Nothing from here down should ever run. + os.write({w}, b'1') + class NeverError(Exception): pass + raise NeverError # never raised + """).format(dedent(text)) + if fails: + with self.assertRaises(excwrapper) as caught: + interpreters.run_string(self.id, script) + return caught.exception + else: + interpreters.run_string(self.id, script) + return None + except: + raise # re-raise + else: + msg = os.read(r, 100) + self.assertEqual(msg, b'0') + finally: + os.close(r) + os.close(w) + + def _assert_run_failed(self, exctype, msg, script): + if isinstance(exctype, str): + exctype_name = exctype + exctype = None + else: + exctype_name = exctype.__name__ + + # Run the script. + exc = self.run_script(script, fails=True) + + # Check the wrapper exception. + if msg is None: + self.assertEqual(str(exc).split(':')[0], + exctype_name) + else: + self.assertEqual(str(exc), + '{}: {}'.format(exctype_name, msg)) + + return exc + + def assert_run_failed(self, exctype, script): + self._assert_run_failed(exctype, None, script) + + def assert_run_failed_msg(self, exctype, msg, script): + self._assert_run_failed(exctype, msg, script) + + def test_exit(self): + with self.subTest('sys.exit(0)'): + # XXX Should an unhandled SystemExit(0) be handled as not-an-error? + self.assert_run_failed(SystemExit, """ + sys.exit(0) + """) + + with self.subTest('sys.exit()'): + self.assert_run_failed(SystemExit, """ + import sys + sys.exit() + """) + + with self.subTest('sys.exit(42)'): + self.assert_run_failed_msg(SystemExit, '42', """ + import sys + sys.exit(42) + """) + + with self.subTest('SystemExit'): + self.assert_run_failed_msg(SystemExit, '42', """ + raise SystemExit(42) + """) + + # XXX Also check os._exit() (via a subprocess)? + + def test_plain_exception(self): + self.assert_run_failed_msg(Exception, 'spam', """ + raise Exception("spam") + """) + + def test_invalid_syntax(self): + script = dedent(""" + x = 1 + 2 + y = 2 + 4 + z = 4 + 8 + + # missing close paren + print("spam" + + if x + y + z < 20: + ... + """) + + with self.subTest('script'): + self.assert_run_failed(SyntaxError, script) + + with self.subTest('module'): + modname = 'spam_spam_spam' + filename = self.add_module(modname, script) + self.assert_run_failed(SyntaxError, f""" + import {modname} + """) + + def test_NameError(self): + self.assert_run_failed(NameError, """ + res = spam + eggs + """) + # XXX check preserved suggestions + + def test_AttributeError(self): + self.assert_run_failed(AttributeError, """ + object().spam + """) + # XXX check preserved suggestions + + def test_ExceptionGroup(self): + self.assert_run_failed(ExceptionGroup, """ + raise ExceptionGroup('exceptions', [ + Exception('spam'), + ImportError('eggs'), + ]) + """) + + def test_user_defined_exception(self): + self.assert_run_failed_msg('MyError', 'spam', """ + class MyError(Exception): + pass + raise MyError('spam') + """) + + class RunFuncTests(TestBase): def setUp(self): From 790db85c7737c2ebbb145f9a26f675a586c5f0d1 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 22 Nov 2023 15:03:33 -0700 Subject: [PATCH 039/228] gh-76785: Add _PyType_GetModuleName() to the Internal C-API (gh-112323) The new function corresponds to the existing (public) PyType_GetName() and PyType_GetQualName(). --- Include/internal/pycore_typeobject.h | 5 +++++ Lib/test/test_capi/test_misc.py | 15 +++++++++++++++ Modules/_testcapimodule.c | 7 +++++++ Modules/_testinternalcapi.c | 10 ++++++++++ Objects/typeobject.c | 6 ++++++ 5 files changed, 43 insertions(+) diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 27c6c8731cb3f9..bbf8544b09f0fb 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -143,6 +143,11 @@ extern PyTypeObject _PyBufferWrapper_Type; extern PyObject* _PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *meth_found); + +// This is exported for the _testinternalcapi module. +PyAPI_FUNC(PyObject *) _PyType_GetModuleName(PyTypeObject *); + + #ifdef __cplusplus } #endif diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 21a5cd3326d707..6cbf5d22203804 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -1098,6 +1098,21 @@ class Data(_testcapi.ObjExtraData): del d.extra self.assertIsNone(d.extra) + def test_get_type_module_name(self): + from collections import OrderedDict + ht = _testcapi.get_heaptype_for_name() + for cls, expected in { + int: 'builtins', + OrderedDict: 'collections', + ht: '_testcapi', + }.items(): + with self.subTest(repr(cls)): + modname = _testinternalcapi.get_type_module_name(cls) + self.assertEqual(modname, expected) + + ht.__module__ = 'test_module' + modname = _testinternalcapi.get_type_module_name(ht) + self.assertEqual(modname, 'test_module') @requires_limited_api class TestHeapTypeRelative(unittest.TestCase): diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 999bd866f14814..9fdd67093338e4 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -569,6 +569,12 @@ static PyType_Spec HeapTypeNameType_Spec = { .slots = HeapTypeNameType_slots, }; +static PyObject * +get_heaptype_for_name(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return PyType_FromSpec(&HeapTypeNameType_Spec); +} + static PyObject * test_get_type_name(PyObject *self, PyObject *Py_UNUSED(ignored)) { @@ -3210,6 +3216,7 @@ static PyMethodDef TestMethods[] = { {"py_buildvalue_ints", py_buildvalue_ints, METH_VARARGS}, {"test_buildvalue_N", test_buildvalue_N, METH_NOARGS}, {"test_get_statictype_slots", test_get_statictype_slots, METH_NOARGS}, + {"get_heaptype_for_name", get_heaptype_for_name, METH_NOARGS}, {"test_get_type_name", test_get_type_name, METH_NOARGS}, {"test_get_type_qualname", test_get_type_qualname, METH_NOARGS}, {"test_get_type_dict", test_get_type_dict, METH_NOARGS}, diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 4fc9e853c0cd91..4607a3faf17f74 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -27,6 +27,7 @@ #include "pycore_pathconfig.h" // _PyPathConfig_ClearGlobal() #include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "pycore_pystate.h" // _PyThreadState_GET() +#include "pycore_typeobject.h" // _PyType_GetModuleName() #include "interpreteridobject.h" // PyInterpreterID_LookUp() @@ -1616,6 +1617,14 @@ perf_trampoline_set_persist_after_fork(PyObject *self, PyObject *args) } +static PyObject * +get_type_module_name(PyObject *self, PyObject *type) +{ + assert(PyType_Check(type)); + return _PyType_GetModuleName((PyTypeObject *)type); +} + + static PyMethodDef module_functions[] = { {"get_configs", get_configs, METH_NOARGS}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, @@ -1678,6 +1687,7 @@ static PyMethodDef module_functions[] = { {"get_crossinterp_data", get_crossinterp_data, METH_VARARGS}, {"restore_crossinterp_data", restore_crossinterp_data, METH_VARARGS}, _TESTINTERNALCAPI_TEST_LONG_NUMBITS_METHODDEF + {"get_type_module_name", get_type_module_name, METH_O}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index f5975a3cd90cb4..aa00e04ad5e11b 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4540,6 +4540,12 @@ PyType_GetQualName(PyTypeObject *type) return type_qualname(type, NULL); } +PyObject * +_PyType_GetModuleName(PyTypeObject *type) +{ + return type_module(type, NULL); +} + void * PyType_GetSlot(PyTypeObject *type, int slot) { From 10e1a0c91613908757a5b97602834defbe575ab0 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Wed, 22 Nov 2023 22:36:55 +0000 Subject: [PATCH 040/228] gh-112137: change dis output to display labels instead of offsets (#112138) --- Doc/library/dis.rst | 36 +- Doc/whatsnew/3.13.rst | 9 + Lib/dis.py | 156 +- Lib/test/test_dis.py | 1451 ++++++++--------- ...-11-16-17-18-09.gh-issue-112137.QvjGjN.rst | 1 + 5 files changed, 838 insertions(+), 815 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-16-17-18-09.gh-issue-112137.QvjGjN.rst diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index e44fa46b5d4145..7e97f1a4524554 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -51,6 +51,11 @@ interpreter. transparent for forward jumps but needs to be taken into account when reasoning about backward jumps. + .. versionchanged:: 3.13 + The output shows logical labels rather than instruction offsets + for jump targets and exception handlers. The ``-O`` command line + option and the ``show_offsets`` argument were added. + Example: Given the function :func:`!myfunc`:: def myfunc(alist): @@ -62,12 +67,12 @@ the following command can be used to display the disassembly of .. doctest:: >>> dis.dis(myfunc) - 2 0 RESUME 0 + 2 RESUME 0 - 3 2 LOAD_GLOBAL 1 (len + NULL) - 12 LOAD_FAST 0 (alist) - 14 CALL 1 - 22 RETURN_VALUE + 3 LOAD_GLOBAL 1 (len + NULL) + LOAD_FAST 0 (alist) + CALL 1 + RETURN_VALUE (The "2" is a line number). @@ -80,7 +85,7 @@ The :mod:`dis` module can be invoked as a script from the command line: .. code-block:: sh - python -m dis [-h] [-C] [infile] + python -m dis [-h] [-C] [-O] [infile] The following options are accepted: @@ -94,6 +99,10 @@ The following options are accepted: Show inline caches. +.. cmdoption:: -O, --show-offsets + + Show offsets of instructions. + If :file:`infile` is specified, its disassembled code will be written to stdout. Otherwise, disassembly is performed on compiled source code recieved from stdin. @@ -107,7 +116,7 @@ The bytecode analysis API allows pieces of Python code to be wrapped in a code. .. class:: Bytecode(x, *, first_line=None, current_offset=None,\ - show_caches=False, adaptive=False) + show_caches=False, adaptive=False, show_offsets=False) Analyse the bytecode corresponding to a function, generator, asynchronous generator, coroutine, method, string of source code, or a code object (as @@ -132,6 +141,9 @@ code. If *adaptive* is ``True``, :meth:`.dis` will display specialized bytecode that may be different from the original bytecode. + If *show_offsets* is ``True``, :meth:`.dis` will include instruction + offsets in the output. + .. classmethod:: from_traceback(tb, *, show_caches=False) Construct a :class:`Bytecode` instance from the given traceback, setting @@ -254,7 +266,8 @@ operation is being performed, so the intermediate analysis object isn't useful: Added the *show_caches* and *adaptive* parameters. -.. function:: distb(tb=None, *, file=None, show_caches=False, adaptive=False) +.. function:: distb(tb=None, *, file=None, show_caches=False, adaptive=False, + show_offset=False) Disassemble the top-of-stack function of a traceback, using the last traceback if none was passed. The instruction causing the exception is @@ -269,9 +282,12 @@ operation is being performed, so the intermediate analysis object isn't useful: .. versionchanged:: 3.11 Added the *show_caches* and *adaptive* parameters. + .. versionchanged:: 3.13 + Added the *show_offsets* parameter. .. function:: disassemble(code, lasti=-1, *, file=None, show_caches=False, adaptive=False) - disco(code, lasti=-1, *, file=None, show_caches=False, adaptive=False) + disco(code, lasti=-1, *, file=None, show_caches=False, adaptive=False, + show_offsets=False) Disassemble a code object, indicating the last instruction if *lasti* was provided. The output is divided in the following columns: @@ -296,6 +312,8 @@ operation is being performed, so the intermediate analysis object isn't useful: .. versionchanged:: 3.11 Added the *show_caches* and *adaptive* parameters. + .. versionchanged:: 3.13 + Added the *show_offsets* parameter. .. function:: get_instructions(x, *, first_line=None, show_caches=False, adaptive=False) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index fc5ae13abe7961..3fd0f5e165f018 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -168,6 +168,15 @@ copy any user classes which define the :meth:`!__replace__` method. (Contributed by Serhiy Storchaka in :gh:`108751`.) +dis +--- + +* Change the output of :mod:`dis` module functions to show logical + labels for jump targets and exception handlers, rather than offsets. + The offsets can be added with the new ``-O`` command line option or + the ``show_offsets`` parameter. + (Contributed by Irit Katriel in :gh:`112137`.) + dbm --- diff --git a/Lib/dis.py b/Lib/dis.py index 965a0268635ebc..c05b8e0dd8617a 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -72,7 +72,8 @@ def _try_compile(source, name): pass return compile(source, name, 'exec') -def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False): +def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False, + show_offsets=False): """Disassemble classes, methods, functions, and other compiled objects. With no argument, disassemble the last traceback. @@ -82,7 +83,8 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False): in a special attribute. """ if x is None: - distb(file=file, show_caches=show_caches, adaptive=adaptive) + distb(file=file, show_caches=show_caches, adaptive=adaptive, + show_offsets=show_offsets) return # Extract functions from methods. if hasattr(x, '__func__'): @@ -103,21 +105,21 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False): if isinstance(x1, _have_code): print("Disassembly of %s:" % name, file=file) try: - dis(x1, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive) + dis(x1, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets) except TypeError as msg: print("Sorry:", msg, file=file) print(file=file) elif hasattr(x, 'co_code'): # Code object - _disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive) + _disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets) elif isinstance(x, (bytes, bytearray)): # Raw bytecode - _disassemble_bytes(x, file=file, show_caches=show_caches) + _disassemble_bytes(x, file=file, show_caches=show_caches, show_offsets=show_offsets) elif isinstance(x, str): # Source code - _disassemble_str(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive) + _disassemble_str(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets) else: raise TypeError("don't know how to disassemble %s objects" % type(x).__name__) -def distb(tb=None, *, file=None, show_caches=False, adaptive=False): +def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets=False): """Disassemble a traceback (default: last traceback).""" if tb is None: try: @@ -128,7 +130,7 @@ def distb(tb=None, *, file=None, show_caches=False, adaptive=False): except AttributeError: raise RuntimeError("no last traceback to disassemble") from None while tb.tb_next: tb = tb.tb_next - disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file, show_caches=show_caches, adaptive=adaptive) + disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets) # The inspect module interrogates this dictionary to build its # list of CO_* constants. It is also used by pretty_flags to @@ -263,10 +265,10 @@ def show_code(co, *, file=None): 'start_offset', 'starts_line', 'line_number', - 'is_jump_target', + 'label', 'positions' ], - defaults=[None] + defaults=[None, None] ) _Instruction.opname.__doc__ = "Human readable name for operation" @@ -281,12 +283,15 @@ def show_code(co, *, file=None): ) _Instruction.starts_line.__doc__ = "True if this opcode starts a source line, otherwise False" _Instruction.line_number.__doc__ = "source line number associated with this opcode (if any), otherwise None" -_Instruction.is_jump_target.__doc__ = "True if other code jumps to here, otherwise False" +_Instruction.label.__doc__ = "A label (int > 0) if this instruction is a jump target, otherwise None" _Instruction.positions.__doc__ = "dis.Positions object holding the span of source code covered by this instruction" -_ExceptionTableEntry = collections.namedtuple("_ExceptionTableEntry", +_ExceptionTableEntryBase = collections.namedtuple("_ExceptionTableEntryBase", "start end target depth lasti") +class _ExceptionTableEntry(_ExceptionTableEntryBase): + pass + _OPNAME_WIDTH = 20 _OPARG_WIDTH = 5 @@ -325,13 +330,14 @@ class Instruction(_Instruction): otherwise equal to Instruction.offset starts_line - True if this opcode starts a source line, otherwise False line_number - source line number associated with this opcode (if any), otherwise None - is_jump_target - True if other code jumps to here, otherwise False + label - A label if this instruction is a jump target, otherwise None positions - Optional dis.Positions object holding the span of source code covered by this instruction """ @staticmethod - def _get_argval_argrepr(op, arg, offset, co_consts, names, varname_from_oparg): + def _get_argval_argrepr(op, arg, offset, co_consts, names, varname_from_oparg, + labels_map): get_name = None if names is None else names.__getitem__ argval = None argrepr = '' @@ -361,13 +367,13 @@ def _get_argval_argrepr(op, arg, offset, co_consts, names, varname_from_oparg): argval, argrepr = _get_name_info(arg, get_name) elif deop in hasjabs: argval = arg*2 - argrepr = "to " + repr(argval) + argrepr = f"to L{labels_map[argval]}" elif deop in hasjrel: signed_arg = -arg if _is_backward_jump(deop) else arg argval = offset + 2 + signed_arg*2 caches = _get_cache_size(_all_opname[deop]) argval += 2 * caches - argrepr = "to " + repr(argval) + argrepr = f"to L{labels_map[argval]}" elif deop in (LOAD_FAST_LOAD_FAST, STORE_FAST_LOAD_FAST, STORE_FAST_STORE_FAST): arg1 = arg >> 4 arg2 = arg & 15 @@ -399,14 +405,21 @@ def _get_argval_argrepr(op, arg, offset, co_consts, names, varname_from_oparg): @classmethod def _create(cls, op, arg, offset, start_offset, starts_line, line_number, - is_jump_target, positions, - co_consts=None, varname_from_oparg=None, names=None): + positions, + co_consts=None, varname_from_oparg=None, names=None, + labels_map=None, exceptions_map=None): + + label_width = 4 + len(str(len(labels_map))) argval, argrepr = cls._get_argval_argrepr( op, arg, offset, - co_consts, names, varname_from_oparg) - return Instruction(_all_opname[op], op, arg, argval, argrepr, - offset, start_offset, starts_line, line_number, - is_jump_target, positions) + co_consts, names, varname_from_oparg, labels_map) + label = labels_map.get(offset, None) + instr = Instruction(_all_opname[op], op, arg, argval, argrepr, + offset, start_offset, starts_line, line_number, + label, positions) + instr.label_width = label_width + instr.exc_handler = exceptions_map.get(offset, None) + return instr @property def oparg(self): @@ -447,7 +460,12 @@ def jump_target(self): """ return _get_jump_target(self.opcode, self.arg, self.offset) - def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=4): + @property + def is_jump_target(self): + """True if other code jumps to here, otherwise False""" + return self.label is not None + + def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=0): """Format instruction details for inclusion in disassembly output. *lineno_width* sets the width of the line number field (0 omits it) @@ -463,18 +481,20 @@ def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=4): fields.append(lineno_fmt % self.line_number) else: fields.append(' ' * lineno_width) + # Column: Label + if self.label is not None: + lbl = f"L{self.label}:" + fields.append(f"{lbl:>{self.label_width}}") + else: + fields.append(' ' * self.label_width) + # Column: Instruction offset from start of code sequence + if offset_width > 0: + fields.append(f"{repr(self.offset):>{offset_width}} ") # Column: Current instruction indicator if mark_as_current: fields.append('-->') else: fields.append(' ') - # Column: Jump target marker - if self.is_jump_target: - fields.append('>>') - else: - fields.append(' ') - # Column: Instruction offset from start of code sequence - fields.append(repr(self.offset).rjust(offset_width)) # Column: Opcode name fields.append(self.opname.ljust(_OPNAME_WIDTH)) # Column: Opcode argument @@ -605,10 +625,29 @@ def _get_instructions_bytes(code, varname_from_oparg=None, original_code = original_code or code co_positions = co_positions or iter(()) get_name = None if names is None else names.__getitem__ - labels = set(findlabels(original_code)) - for start, end, target, _, _ in exception_entries: - for i in range(start, end): + + def make_labels_map(original_code, exception_entries): + jump_targets = set(findlabels(original_code)) + labels = set(jump_targets) + for start, end, target, _, _ in exception_entries: + labels.add(start) + labels.add(end) labels.add(target) + labels = sorted(labels) + labels_map = {offset: i+1 for (i, offset) in enumerate(sorted(labels))} + for e in exception_entries: + e.start_label = labels_map[e.start] + e.end_label = labels_map[e.end] + e.target_label = labels_map[e.target] + return labels_map + + labels_map = make_labels_map(original_code, exception_entries) + + exceptions_map = {} + for start, end, target, _, _ in exception_entries: + exceptions_map[start] = labels_map[target] + exceptions_map[end] = -1 + starts_line = False local_line_number = None line_number = None @@ -621,14 +660,14 @@ def _get_instructions_bytes(code, varname_from_oparg=None, line_number = local_line_number + line_offset else: line_number = None - is_jump_target = offset in labels positions = Positions(*next(co_positions, ())) deop = _deoptop(op) op = code[offset] yield Instruction._create(op, arg, offset, start_offset, starts_line, line_number, - is_jump_target, positions, co_consts=co_consts, - varname_from_oparg=varname_from_oparg, names=names) + positions, co_consts=co_consts, + varname_from_oparg=varname_from_oparg, names=names, + labels_map=labels_map, exceptions_map=exceptions_map) caches = _get_cache_size(_all_opname[deop]) if not caches: @@ -649,11 +688,12 @@ def _get_instructions_bytes(code, varname_from_oparg=None, else: argrepr = "" yield Instruction( - "CACHE", CACHE, 0, None, argrepr, offset, offset, False, None, False, + "CACHE", CACHE, 0, None, argrepr, offset, offset, False, None, None, Positions(*next(co_positions, ())) ) -def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False): +def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False, + show_offsets=False): """Disassemble a code object.""" linestarts = dict(findlinestarts(co)) exception_entries = _parse_exception_table(co) @@ -662,10 +702,10 @@ def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False): co.co_names, co.co_consts, linestarts, file=file, exception_entries=exception_entries, co_positions=co.co_positions(), show_caches=show_caches, - original_code=co.co_code) + original_code=co.co_code, show_offsets=show_offsets) -def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False): - disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive) +def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False, show_offsets=False): + disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets) if depth is None or depth > 0: if depth is not None: depth = depth - 1 @@ -674,13 +714,15 @@ def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adap print(file=file) print("Disassembly of %r:" % (x,), file=file) _disassemble_recursive( - x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive + x, file=file, depth=depth, show_caches=show_caches, + adaptive=adaptive, show_offsets=show_offsets ) def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None, names=None, co_consts=None, linestarts=None, *, file=None, line_offset=0, exception_entries=(), - co_positions=None, show_caches=False, original_code=None): + co_positions=None, show_caches=False, original_code=None, + show_offsets=False): # Omit the line number column entirely if we have no line number info if bool(linestarts): linestarts_ints = [line for line in linestarts.values() if line is not None] @@ -699,11 +741,15 @@ def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None, lineno_width = len(str(None)) else: lineno_width = 0 - maxoffset = len(code) - 2 - if maxoffset >= 10000: - offset_width = len(str(maxoffset)) + if show_offsets: + maxoffset = len(code) - 2 + if maxoffset >= 10000: + offset_width = len(str(maxoffset)) + else: + offset_width = 4 else: - offset_width = 4 + offset_width = 0 + for instr in _get_instructions_bytes(code, varname_from_oparg, names, co_consts, linestarts, line_offset=line_offset, @@ -728,8 +774,10 @@ def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None, print("ExceptionTable:", file=file) for entry in exception_entries: lasti = " lasti" if entry.lasti else "" - end = entry.end-2 - print(f" {entry.start} to {end} -> {entry.target} [{entry.depth}]{lasti}", file=file) + start = entry.start_label + end = entry.end_label + target = entry.target_label + print(f" L{start} to L{end} -> L{target} [{entry.depth}]{lasti}", file=file) def _disassemble_str(source, **kwargs): """Compile the source string, then disassemble the code object.""" @@ -850,7 +898,7 @@ class Bytecode: Iterating over this yields the bytecode operations as Instruction instances. """ - def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False): + def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False, show_offsets=False): self.codeobj = co = _get_code_object(x) if first_line is None: self.first_line = co.co_firstlineno @@ -864,6 +912,7 @@ def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False self.exception_entries = _parse_exception_table(co) self.show_caches = show_caches self.adaptive = adaptive + self.show_offsets = show_offsets def __iter__(self): co = self.codeobj @@ -912,7 +961,8 @@ def dis(self): exception_entries=self.exception_entries, co_positions=co.co_positions(), show_caches=self.show_caches, - original_code=co.co_code) + original_code=co.co_code, + show_offsets=self.show_offsets) return output.getvalue() @@ -922,12 +972,14 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument('-C', '--show-caches', action='store_true', help='show inline caches') + parser.add_argument('-O', '--show-offsets', action='store_true', + help='show instruction offsets') parser.add_argument('infile', type=argparse.FileType('rb'), nargs='?', default='-') args = parser.parse_args() with args.infile as infile: source = infile.read() code = compile(source, args.infile.name, "exec") - dis(code, show_caches=args.show_caches) + dis(code, show_caches=args.show_caches, show_offsets=args.show_offsets) if __name__ == "__main__": main() diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 43debdcf6e1607..0e7c59c5797f6c 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -42,45 +42,45 @@ def cm(cls, x): cls.x = x == 1 dis_c_instance_method = """\ -%3d RESUME 0 - -%3d LOAD_FAST 1 (x) - LOAD_CONST 1 (1) - COMPARE_OP 72 (==) - LOAD_FAST 0 (self) - STORE_ATTR 0 (x) - RETURN_CONST 0 (None) +%3d RESUME 0 + +%3d LOAD_FAST 1 (x) + LOAD_CONST 1 (1) + COMPARE_OP 72 (==) + LOAD_FAST 0 (self) + STORE_ATTR 0 (x) + RETURN_CONST 0 (None) """ % (_C.__init__.__code__.co_firstlineno, _C.__init__.__code__.co_firstlineno + 1,) dis_c_instance_method_bytes = """\ - RESUME 0 - LOAD_FAST 1 - LOAD_CONST 1 - COMPARE_OP 72 (==) - LOAD_FAST 0 - STORE_ATTR 0 - RETURN_CONST 0 + RESUME 0 + LOAD_FAST 1 + LOAD_CONST 1 + COMPARE_OP 72 (==) + LOAD_FAST 0 + STORE_ATTR 0 + RETURN_CONST 0 """ dis_c_class_method = """\ -%3d RESUME 0 - -%3d LOAD_FAST 1 (x) - LOAD_CONST 1 (1) - COMPARE_OP 72 (==) - LOAD_FAST 0 (cls) - STORE_ATTR 0 (x) - RETURN_CONST 0 (None) +%3d RESUME 0 + +%3d LOAD_FAST 1 (x) + LOAD_CONST 1 (1) + COMPARE_OP 72 (==) + LOAD_FAST 0 (cls) + STORE_ATTR 0 (x) + RETURN_CONST 0 (None) """ % (_C.cm.__code__.co_firstlineno, _C.cm.__code__.co_firstlineno + 2,) dis_c_static_method = """\ -%3d RESUME 0 +%3d RESUME 0 -%3d LOAD_FAST 0 (x) - LOAD_CONST 1 (1) - COMPARE_OP 72 (==) - STORE_FAST 0 (x) - RETURN_CONST 0 (None) +%3d LOAD_FAST 0 (x) + LOAD_CONST 1 (1) + COMPARE_OP 72 (==) + STORE_FAST 0 (x) + RETURN_CONST 0 (None) """ % (_C.sm.__code__.co_firstlineno, _C.sm.__code__.co_firstlineno + 2,) # Class disassembling info has an extra newline at end. @@ -100,51 +100,63 @@ def _f(a): return 1 dis_f = """\ -%3d RESUME 0 +%3d RESUME 0 -%3d LOAD_GLOBAL 1 (print + NULL) - LOAD_FAST 0 (a) - CALL 1 - POP_TOP +%3d LOAD_GLOBAL 1 (print + NULL) + LOAD_FAST 0 (a) + CALL 1 + POP_TOP -%3d RETURN_CONST 1 (1) +%3d RETURN_CONST 1 (1) +""" % (_f.__code__.co_firstlineno, + _f.__code__.co_firstlineno + 1, + _f.__code__.co_firstlineno + 2) + +dis_f_with_offsets = """\ +%3d 0 RESUME 0 + +%3d 2 LOAD_GLOBAL 1 (print + NULL) + 12 LOAD_FAST 0 (a) + 14 CALL 1 + 22 POP_TOP + +%3d 24 RETURN_CONST 1 (1) """ % (_f.__code__.co_firstlineno, _f.__code__.co_firstlineno + 1, _f.__code__.co_firstlineno + 2) dis_f_co_code = """\ - RESUME 0 - LOAD_GLOBAL 1 - LOAD_FAST 0 - CALL 1 - POP_TOP - RETURN_CONST 1 + RESUME 0 + LOAD_GLOBAL 1 + LOAD_FAST 0 + CALL 1 + POP_TOP + RETURN_CONST 1 """ - def bug708901(): for res in range(1, 10): pass dis_bug708901 = """\ -%3d RESUME 0 +%3d RESUME 0 -%3d LOAD_GLOBAL 1 (range + NULL) - LOAD_CONST 1 (1) +%3d LOAD_GLOBAL 1 (range + NULL) + LOAD_CONST 1 (1) -%3d LOAD_CONST 2 (10) +%3d LOAD_CONST 2 (10) -%3d CALL 2 - GET_ITER - >> FOR_ITER 3 (to 36) - STORE_FAST 0 (res) +%3d CALL 2 + GET_ITER + L1: FOR_ITER 3 (to L2) + STORE_FAST 0 (res) -%3d JUMP_BACKWARD 5 (to 26) +%3d JUMP_BACKWARD 5 (to L1) -%3d >> END_FOR - RETURN_CONST 0 (None) +%3d L2: END_FOR + RETURN_CONST 0 (None) """ % (bug708901.__code__.co_firstlineno, bug708901.__code__.co_firstlineno + 1, bug708901.__code__.co_firstlineno + 2, @@ -159,20 +171,20 @@ def bug1333982(x=[]): pass dis_bug1333982 = """\ -%3d RESUME 0 +%3d RESUME 0 -%3d LOAD_ASSERTION_ERROR - LOAD_CONST 1 ( at 0x..., file "%s", line %d>) - MAKE_FUNCTION - LOAD_FAST 0 (x) - GET_ITER - CALL 0 +%3d LOAD_ASSERTION_ERROR + LOAD_CONST 1 ( at 0x..., file "%s", line %d>) + MAKE_FUNCTION + LOAD_FAST 0 (x) + GET_ITER + CALL 0 -%3d LOAD_CONST 2 (1) +%3d LOAD_CONST 2 (1) -%3d BINARY_OP 0 (+) - CALL 0 - RAISE_VARARGS 1 +%3d BINARY_OP 0 (+) + CALL 0 + RAISE_VARARGS 1 """ % (bug1333982.__code__.co_firstlineno, bug1333982.__code__.co_firstlineno + 1, __file__, @@ -190,8 +202,8 @@ def bug42562(): dis_bug42562 = """\ - RESUME 0 - RETURN_CONST 0 (None) + RESUME 0 + RETURN_CONST 0 (None) """ # Extended arg followed by NOP @@ -204,11 +216,11 @@ def bug42562(): ]) dis_bug_45757 = """\ - EXTENDED_ARG 1 - NOP - EXTENDED_ARG 1 - LOAD_CONST 297 - RETURN_VALUE + EXTENDED_ARG 1 + NOP + EXTENDED_ARG 1 + LOAD_CONST 297 + RETURN_VALUE """ # [255, 255, 255, 252] is -4 in a 4 byte signed integer @@ -221,10 +233,10 @@ def bug42562(): dis_bug46724 = """\ - >> EXTENDED_ARG 255 - EXTENDED_ARG 65535 - EXTENDED_ARG 16777215 - JUMP_FORWARD -4 (to 0) + L1: EXTENDED_ARG 255 + EXTENDED_ARG 65535 + EXTENDED_ARG 16777215 + JUMP_FORWARD -4 (to L1) """ def func_w_kwargs(a, b, **c): @@ -234,96 +246,96 @@ def wrap_func_w_kwargs(): func_w_kwargs(1, 2, c=5) dis_kw_names = """\ -%3d RESUME 0 - -%3d LOAD_GLOBAL 1 (func_w_kwargs + NULL) - LOAD_CONST 1 (1) - LOAD_CONST 2 (2) - LOAD_CONST 3 (5) - LOAD_CONST 4 (('c',)) - CALL_KW 3 - POP_TOP - RETURN_CONST 0 (None) +%3d RESUME 0 + +%3d LOAD_GLOBAL 1 (func_w_kwargs + NULL) + LOAD_CONST 1 (1) + LOAD_CONST 2 (2) + LOAD_CONST 3 (5) + LOAD_CONST 4 (('c',)) + CALL_KW 3 + POP_TOP + RETURN_CONST 0 (None) """ % (wrap_func_w_kwargs.__code__.co_firstlineno, wrap_func_w_kwargs.__code__.co_firstlineno + 1) dis_intrinsic_1_2 = """\ - 0 RESUME 0 - - 1 LOAD_CONST 0 (0) - LOAD_CONST 1 (('*',)) - IMPORT_NAME 0 (math) - CALL_INTRINSIC_1 2 (INTRINSIC_IMPORT_STAR) - POP_TOP - RETURN_CONST 2 (None) + 0 RESUME 0 + + 1 LOAD_CONST 0 (0) + LOAD_CONST 1 (('*',)) + IMPORT_NAME 0 (math) + CALL_INTRINSIC_1 2 (INTRINSIC_IMPORT_STAR) + POP_TOP + RETURN_CONST 2 (None) """ dis_intrinsic_1_5 = """\ - 0 RESUME 0 + 0 RESUME 0 - 1 LOAD_NAME 0 (a) - CALL_INTRINSIC_1 5 (INTRINSIC_UNARY_POSITIVE) - RETURN_VALUE + 1 LOAD_NAME 0 (a) + CALL_INTRINSIC_1 5 (INTRINSIC_UNARY_POSITIVE) + RETURN_VALUE """ dis_intrinsic_1_6 = """\ - 0 RESUME 0 + 0 RESUME 0 - 1 BUILD_LIST 0 - LOAD_NAME 0 (a) - LIST_EXTEND 1 - CALL_INTRINSIC_1 6 (INTRINSIC_LIST_TO_TUPLE) - RETURN_VALUE + 1 BUILD_LIST 0 + LOAD_NAME 0 (a) + LIST_EXTEND 1 + CALL_INTRINSIC_1 6 (INTRINSIC_LIST_TO_TUPLE) + RETURN_VALUE """ _BIG_LINENO_FORMAT = """\ - 1 RESUME 0 + 1 RESUME 0 -%3d LOAD_GLOBAL 0 (spam) - POP_TOP - RETURN_CONST 0 (None) +%3d LOAD_GLOBAL 0 (spam) + POP_TOP + RETURN_CONST 0 (None) """ _BIG_LINENO_FORMAT2 = """\ - 1 RESUME 0 + 1 RESUME 0 -%4d LOAD_GLOBAL 0 (spam) - POP_TOP - RETURN_CONST 0 (None) +%4d LOAD_GLOBAL 0 (spam) + POP_TOP + RETURN_CONST 0 (None) """ dis_module_expected_results = """\ Disassembly of f: - 4 RESUME 0 - RETURN_CONST 0 (None) + 4 RESUME 0 + RETURN_CONST 0 (None) Disassembly of g: - 5 RESUME 0 - RETURN_CONST 0 (None) + 5 RESUME 0 + RETURN_CONST 0 (None) """ expr_str = "x + 1" dis_expr_str = """\ - 0 RESUME 0 + 0 RESUME 0 - 1 LOAD_NAME 0 (x) - LOAD_CONST 0 (1) - BINARY_OP 0 (+) - RETURN_VALUE + 1 LOAD_NAME 0 (x) + LOAD_CONST 0 (1) + BINARY_OP 0 (+) + RETURN_VALUE """ simple_stmt_str = "x = x + 1" dis_simple_stmt_str = """\ - 0 RESUME 0 + 0 RESUME 0 - 1 LOAD_NAME 0 (x) - LOAD_CONST 0 (1) - BINARY_OP 0 (+) - STORE_NAME 0 (x) - RETURN_CONST 1 (None) + 1 LOAD_NAME 0 (x) + LOAD_CONST 0 (1) + BINARY_OP 0 (+) + STORE_NAME 0 (x) + RETURN_CONST 1 (None) """ annot_stmt_str = """\ @@ -335,34 +347,34 @@ def wrap_func_w_kwargs(): # leading newline is for a reason (tests lineno) dis_annot_stmt_str = """\ - 0 RESUME 0 - - 2 SETUP_ANNOTATIONS - LOAD_CONST 0 (1) - STORE_NAME 0 (x) - LOAD_NAME 1 (int) - LOAD_NAME 2 (__annotations__) - LOAD_CONST 1 ('x') - STORE_SUBSCR - - 3 LOAD_NAME 3 (fun) - PUSH_NULL - LOAD_CONST 0 (1) - CALL 1 - LOAD_NAME 2 (__annotations__) - LOAD_CONST 2 ('y') - STORE_SUBSCR - - 4 LOAD_CONST 0 (1) - LOAD_NAME 4 (lst) - LOAD_NAME 3 (fun) - PUSH_NULL - LOAD_CONST 3 (0) - CALL 1 - STORE_SUBSCR - LOAD_NAME 1 (int) - POP_TOP - RETURN_CONST 4 (None) + 0 RESUME 0 + + 2 SETUP_ANNOTATIONS + LOAD_CONST 0 (1) + STORE_NAME 0 (x) + LOAD_NAME 1 (int) + LOAD_NAME 2 (__annotations__) + LOAD_CONST 1 ('x') + STORE_SUBSCR + + 3 LOAD_NAME 3 (fun) + PUSH_NULL + LOAD_CONST 0 (1) + CALL 1 + LOAD_NAME 2 (__annotations__) + LOAD_CONST 2 ('y') + STORE_SUBSCR + + 4 LOAD_CONST 0 (1) + LOAD_NAME 4 (lst) + LOAD_NAME 3 (fun) + PUSH_NULL + LOAD_CONST 3 (0) + CALL 1 + STORE_SUBSCR + LOAD_NAME 1 (int) + POP_TOP + RETURN_CONST 4 (None) """ compound_stmt_str = """\ @@ -372,64 +384,67 @@ def wrap_func_w_kwargs(): # Trailing newline has been deliberately omitted dis_compound_stmt_str = """\ - 0 RESUME 0 + 0 RESUME 0 - 1 LOAD_CONST 0 (0) - STORE_NAME 0 (x) + 1 LOAD_CONST 0 (0) + STORE_NAME 0 (x) - 2 NOP + 2 NOP - 3 >> LOAD_NAME 0 (x) - LOAD_CONST 1 (1) - BINARY_OP 13 (+=) - STORE_NAME 0 (x) + 3 L1: LOAD_NAME 0 (x) + LOAD_CONST 1 (1) + BINARY_OP 13 (+=) + STORE_NAME 0 (x) - 2 JUMP_BACKWARD 7 (to 8) + 2 JUMP_BACKWARD 7 (to L1) """ dis_traceback = """\ -%4d RESUME 0 +%4d RESUME 0 -%4d NOP +%4d NOP -%4d LOAD_CONST 1 (1) - LOAD_CONST 2 (0) - --> BINARY_OP 11 (/) - POP_TOP +%4d L1: LOAD_CONST 1 (1) + LOAD_CONST 2 (0) + --> BINARY_OP 11 (/) + POP_TOP -%4d LOAD_FAST_CHECK 1 (tb) - RETURN_VALUE +%4d L2: LOAD_FAST_CHECK 1 (tb) + RETURN_VALUE -None >> PUSH_EXC_INFO +None L3: PUSH_EXC_INFO -%4d LOAD_GLOBAL 0 (Exception) - CHECK_EXC_MATCH - POP_JUMP_IF_FALSE 23 (to 82) - STORE_FAST 0 (e) +%4d LOAD_GLOBAL 0 (Exception) + CHECK_EXC_MATCH + POP_JUMP_IF_FALSE 23 (to L7) + STORE_FAST 0 (e) -%4d LOAD_FAST 0 (e) - LOAD_ATTR 2 (__traceback__) - STORE_FAST 1 (tb) - POP_EXCEPT - LOAD_CONST 0 (None) - STORE_FAST 0 (e) - DELETE_FAST 0 (e) +%4d L4: LOAD_FAST 0 (e) + LOAD_ATTR 2 (__traceback__) + STORE_FAST 1 (tb) + L5: POP_EXCEPT + LOAD_CONST 0 (None) + STORE_FAST 0 (e) + DELETE_FAST 0 (e) -%4d LOAD_FAST 1 (tb) - RETURN_VALUE +%4d LOAD_FAST 1 (tb) + RETURN_VALUE -None >> LOAD_CONST 0 (None) - STORE_FAST 0 (e) - DELETE_FAST 0 (e) - RERAISE 1 +None L6: LOAD_CONST 0 (None) + STORE_FAST 0 (e) + DELETE_FAST 0 (e) + RERAISE 1 -%4d >> RERAISE 0 +%4d L7: RERAISE 0 -None >> COPY 3 - POP_EXCEPT - RERAISE 1 +None L8: COPY 3 + POP_EXCEPT + RERAISE 1 ExceptionTable: -4 rows + L1 to L2 -> L3 [0] + L3 to L4 -> L8 [1] lasti + L4 to L5 -> L6 [1] lasti + L6 to L8 -> L8 [1] lasti """ % (TRACEBACK_CODE.co_firstlineno, TRACEBACK_CODE.co_firstlineno + 1, TRACEBACK_CODE.co_firstlineno + 2, @@ -443,25 +458,25 @@ def _fstring(a, b, c, d): return f'{a} {b:4} {c!r} {d!r:4}' dis_fstring = """\ -%3d RESUME 0 - -%3d LOAD_FAST 0 (a) - FORMAT_SIMPLE - LOAD_CONST 1 (' ') - LOAD_FAST 1 (b) - LOAD_CONST 2 ('4') - FORMAT_WITH_SPEC - LOAD_CONST 1 (' ') - LOAD_FAST 2 (c) - CONVERT_VALUE 2 (repr) - FORMAT_SIMPLE - LOAD_CONST 1 (' ') - LOAD_FAST 3 (d) - CONVERT_VALUE 2 (repr) - LOAD_CONST 2 ('4') - FORMAT_WITH_SPEC - BUILD_STRING 7 - RETURN_VALUE +%3d RESUME 0 + +%3d LOAD_FAST 0 (a) + FORMAT_SIMPLE + LOAD_CONST 1 (' ') + LOAD_FAST 1 (b) + LOAD_CONST 2 ('4') + FORMAT_WITH_SPEC + LOAD_CONST 1 (' ') + LOAD_FAST 2 (c) + CONVERT_VALUE 2 (repr) + FORMAT_SIMPLE + LOAD_CONST 1 (' ') + LOAD_FAST 3 (d) + CONVERT_VALUE 2 (repr) + LOAD_CONST 2 ('4') + FORMAT_WITH_SPEC + BUILD_STRING 7 + RETURN_VALUE """ % (_fstring.__code__.co_firstlineno, _fstring.__code__.co_firstlineno + 1) def _with(c): @@ -470,44 +485,45 @@ def _with(c): y = 2 dis_with = """\ -%4d RESUME 0 - -%4d LOAD_FAST 0 (c) - BEFORE_WITH - POP_TOP - -%4d LOAD_CONST 1 (1) - STORE_FAST 1 (x) - -%4d LOAD_CONST 0 (None) - LOAD_CONST 0 (None) - LOAD_CONST 0 (None) - CALL 2 - POP_TOP - -%4d LOAD_CONST 2 (2) - STORE_FAST 2 (y) - RETURN_CONST 0 (None) - -%4d >> PUSH_EXC_INFO - WITH_EXCEPT_START - TO_BOOL - POP_JUMP_IF_TRUE 1 (to 52) - RERAISE 2 - >> POP_TOP - POP_EXCEPT - POP_TOP - POP_TOP - -%4d LOAD_CONST 2 (2) - STORE_FAST 2 (y) - RETURN_CONST 0 (None) - -None >> COPY 3 - POP_EXCEPT - RERAISE 1 +%4d RESUME 0 + +%4d LOAD_FAST 0 (c) + BEFORE_WITH + L1: POP_TOP + +%4d LOAD_CONST 1 (1) + STORE_FAST 1 (x) + +%4d L2: LOAD_CONST 0 (None) + LOAD_CONST 0 (None) + LOAD_CONST 0 (None) + CALL 2 + POP_TOP + +%4d LOAD_CONST 2 (2) + STORE_FAST 2 (y) + RETURN_CONST 0 (None) + +%4d L3: PUSH_EXC_INFO + WITH_EXCEPT_START + TO_BOOL + POP_JUMP_IF_TRUE 1 (to L4) + RERAISE 2 + L4: POP_TOP + L5: POP_EXCEPT + POP_TOP + POP_TOP + +%4d LOAD_CONST 2 (2) + STORE_FAST 2 (y) + RETURN_CONST 0 (None) + +None L6: COPY 3 + POP_EXCEPT + RERAISE 1 ExceptionTable: -2 rows + L1 to L2 -> L3 [1] lasti + L3 to L5 -> L6 [3] lasti """ % (_with.__code__.co_firstlineno, _with.__code__.co_firstlineno + 1, _with.__code__.co_firstlineno + 2, @@ -523,78 +539,89 @@ async def _asyncwith(c): y = 2 dis_asyncwith = """\ -%4d RETURN_GENERATOR - POP_TOP - RESUME 0 - -%4d LOAD_FAST 0 (c) - BEFORE_ASYNC_WITH - GET_AWAITABLE 1 - LOAD_CONST 0 (None) - >> SEND 3 (to 24) - YIELD_VALUE 1 - RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 5 (to 14) - >> END_SEND - POP_TOP - -%4d LOAD_CONST 1 (1) - STORE_FAST 1 (x) - -%4d LOAD_CONST 0 (None) - LOAD_CONST 0 (None) - LOAD_CONST 0 (None) - CALL 2 - GET_AWAITABLE 2 - LOAD_CONST 0 (None) - >> SEND 3 (to 60) - YIELD_VALUE 1 - RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 5 (to 50) - >> END_SEND - POP_TOP - -%4d LOAD_CONST 2 (2) - STORE_FAST 2 (y) - RETURN_CONST 0 (None) - -%4d >> CLEANUP_THROW - -None JUMP_BACKWARD 26 (to 24) - -%4d >> CLEANUP_THROW - -None JUMP_BACKWARD 11 (to 60) - -%4d >> PUSH_EXC_INFO - WITH_EXCEPT_START - GET_AWAITABLE 2 - LOAD_CONST 0 (None) - >> SEND 4 (to 102) - YIELD_VALUE 1 - RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 5 (to 90) - >> CLEANUP_THROW - >> END_SEND - TO_BOOL - POP_JUMP_IF_TRUE 1 (to 118) - RERAISE 2 - >> POP_TOP - POP_EXCEPT - POP_TOP - POP_TOP - -%4d LOAD_CONST 2 (2) - STORE_FAST 2 (y) - RETURN_CONST 0 (None) - -None >> COPY 3 - POP_EXCEPT - RERAISE 1 - >> CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR) - RERAISE 1 +%4d RETURN_GENERATOR + POP_TOP + L1: RESUME 0 + +%4d LOAD_FAST 0 (c) + BEFORE_ASYNC_WITH + GET_AWAITABLE 1 + LOAD_CONST 0 (None) + L2: SEND 3 (to L5) + L3: YIELD_VALUE 1 + L4: RESUME 3 + JUMP_BACKWARD_NO_INTERRUPT 5 (to L2) + L5: END_SEND + L6: POP_TOP + +%4d LOAD_CONST 1 (1) + STORE_FAST 1 (x) + +%4d L7: LOAD_CONST 0 (None) + LOAD_CONST 0 (None) + LOAD_CONST 0 (None) + CALL 2 + GET_AWAITABLE 2 + LOAD_CONST 0 (None) + L8: SEND 3 (to L11) + L9: YIELD_VALUE 1 + L10: RESUME 3 + JUMP_BACKWARD_NO_INTERRUPT 5 (to L8) + L11: END_SEND + POP_TOP + +%4d LOAD_CONST 2 (2) + STORE_FAST 2 (y) + RETURN_CONST 0 (None) + +%4d L12: CLEANUP_THROW + +None L13: JUMP_BACKWARD 26 (to L5) + +%4d L14: CLEANUP_THROW + +None L15: JUMP_BACKWARD 11 (to L11) + +%4d L16: PUSH_EXC_INFO + WITH_EXCEPT_START + GET_AWAITABLE 2 + LOAD_CONST 0 (None) + L17: SEND 4 (to L21) + L18: YIELD_VALUE 1 + L19: RESUME 3 + JUMP_BACKWARD_NO_INTERRUPT 5 (to L17) + L20: CLEANUP_THROW + L21: END_SEND + TO_BOOL + POP_JUMP_IF_TRUE 1 (to L22) + RERAISE 2 + L22: POP_TOP + L23: POP_EXCEPT + POP_TOP + POP_TOP + +%4d LOAD_CONST 2 (2) + STORE_FAST 2 (y) + RETURN_CONST 0 (None) + +None L24: COPY 3 + POP_EXCEPT + RERAISE 1 + L25: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR) + RERAISE 1 ExceptionTable: -12 rows + L1 to L3 -> L25 [0] lasti + L3 to L4 -> L12 [3] + L4 to L6 -> L25 [0] lasti + L6 to L7 -> L16 [1] lasti + L7 to L9 -> L25 [0] lasti + L9 to L10 -> L14 [2] + L10 to L13 -> L25 [0] lasti + L14 to L15 -> L25 [0] lasti + L16 to L18 -> L24 [3] lasti + L18 to L19 -> L20 [6] + L19 to L23 -> L24 [3] lasti + L23 to L25 -> L25 [0] lasti """ % (_asyncwith.__code__.co_firstlineno, _asyncwith.__code__.co_firstlineno + 1, _asyncwith.__code__.co_firstlineno + 2, @@ -620,31 +647,32 @@ def _tryfinallyconst(b): b() dis_tryfinally = """\ -%4d RESUME 0 +%4d RESUME 0 -%4d NOP +%4d NOP -%4d LOAD_FAST 0 (a) +%4d L1: LOAD_FAST 0 (a) -%4d LOAD_FAST 1 (b) - PUSH_NULL - CALL 0 - POP_TOP - RETURN_VALUE +%4d L2: LOAD_FAST 1 (b) + PUSH_NULL + CALL 0 + POP_TOP + RETURN_VALUE -None >> PUSH_EXC_INFO +None L3: PUSH_EXC_INFO -%4d LOAD_FAST 1 (b) - PUSH_NULL - CALL 0 - POP_TOP - RERAISE 0 +%4d LOAD_FAST 1 (b) + PUSH_NULL + CALL 0 + POP_TOP + RERAISE 0 -None >> COPY 3 - POP_EXCEPT - RERAISE 1 +None L4: COPY 3 + POP_EXCEPT + RERAISE 1 ExceptionTable: -2 rows + L1 to L2 -> L3 [0] + L3 to L4 -> L4 [1] lasti """ % (_tryfinally.__code__.co_firstlineno, _tryfinally.__code__.co_firstlineno + 1, _tryfinally.__code__.co_firstlineno + 2, @@ -653,31 +681,31 @@ def _tryfinallyconst(b): ) dis_tryfinallyconst = """\ -%4d RESUME 0 +%4d RESUME 0 -%4d NOP +%4d NOP -%4d NOP +%4d NOP -%4d LOAD_FAST 0 (b) - PUSH_NULL - CALL 0 - POP_TOP - RETURN_CONST 1 (1) +%4d LOAD_FAST 0 (b) + PUSH_NULL + CALL 0 + POP_TOP + RETURN_CONST 1 (1) -None PUSH_EXC_INFO +None L1: PUSH_EXC_INFO -%4d LOAD_FAST 0 (b) - PUSH_NULL - CALL 0 - POP_TOP - RERAISE 0 +%4d LOAD_FAST 0 (b) + PUSH_NULL + CALL 0 + POP_TOP + RERAISE 0 -None >> COPY 3 - POP_EXCEPT - RERAISE 1 +None L2: COPY 3 + POP_EXCEPT + RERAISE 1 ExceptionTable: -1 row + L1 to L2 -> L2 [1] lasti """ % (_tryfinallyconst.__code__.co_firstlineno, _tryfinallyconst.__code__.co_firstlineno + 1, _tryfinallyconst.__code__.co_firstlineno + 2, @@ -702,19 +730,19 @@ def foo(x): return foo dis_nested_0 = """\ -None MAKE_CELL 0 (y) +None MAKE_CELL 0 (y) -%4d RESUME 0 +%4d RESUME 0 -%4d LOAD_FAST 0 (y) - BUILD_TUPLE 1 - LOAD_CONST 1 () - MAKE_FUNCTION - SET_FUNCTION_ATTRIBUTE 8 (closure) - STORE_FAST 1 (foo) +%4d LOAD_FAST 0 (y) + BUILD_TUPLE 1 + LOAD_CONST 1 () + MAKE_FUNCTION + SET_FUNCTION_ATTRIBUTE 8 (closure) + STORE_FAST 1 (foo) -%4d LOAD_FAST 1 (foo) - RETURN_VALUE +%4d LOAD_FAST 1 (foo) + RETURN_VALUE """ % (_h.__code__.co_firstlineno, _h.__code__.co_firstlineno + 1, __file__, @@ -724,22 +752,22 @@ def foo(x): dis_nested_1 = """%s Disassembly of : -None COPY_FREE_VARS 1 - MAKE_CELL 0 (x) - -%4d RESUME 0 - -%4d LOAD_GLOBAL 1 (list + NULL) - LOAD_FAST 0 (x) - BUILD_TUPLE 1 - LOAD_CONST 1 ( at 0x..., file "%s", line %d>) - MAKE_FUNCTION - SET_FUNCTION_ATTRIBUTE 8 (closure) - LOAD_DEREF 1 (y) - GET_ITER - CALL 0 - CALL 1 - RETURN_VALUE +None COPY_FREE_VARS 1 + MAKE_CELL 0 (x) + +%4d RESUME 0 + +%4d LOAD_GLOBAL 1 (list + NULL) + LOAD_FAST 0 (x) + BUILD_TUPLE 1 + LOAD_CONST 1 ( at 0x..., file "%s", line %d>) + MAKE_FUNCTION + SET_FUNCTION_ATTRIBUTE 8 (closure) + LOAD_DEREF 1 (y) + GET_ITER + CALL 0 + CALL 1 + RETURN_VALUE """ % (dis_nested_0, __file__, _h.__code__.co_firstlineno + 1, @@ -751,28 +779,28 @@ def foo(x): dis_nested_2 = """%s Disassembly of at 0x..., file "%s", line %d>: -None COPY_FREE_VARS 1 - -%4d RETURN_GENERATOR - POP_TOP - RESUME 0 - LOAD_FAST 0 (.0) - >> FOR_ITER 10 (to 34) - STORE_FAST 1 (z) - LOAD_DEREF 2 (x) - LOAD_FAST 1 (z) - BINARY_OP 0 (+) - YIELD_VALUE 0 - RESUME 5 - POP_TOP - JUMP_BACKWARD 12 (to 10) - >> END_FOR - RETURN_CONST 0 (None) - -None >> CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR) - RERAISE 1 +None COPY_FREE_VARS 1 + +%4d RETURN_GENERATOR + POP_TOP + L1: RESUME 0 + LOAD_FAST 0 (.0) + L2: FOR_ITER 10 (to L3) + STORE_FAST 1 (z) + LOAD_DEREF 2 (x) + LOAD_FAST 1 (z) + BINARY_OP 0 (+) + YIELD_VALUE 0 + RESUME 5 + POP_TOP + JUMP_BACKWARD 12 (to L2) + L3: END_FOR + RETURN_CONST 0 (None) + +None L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR) + RERAISE 1 ExceptionTable: -1 row + L1 to L4 -> L4 [0] lasti """ % (dis_nested_1, __file__, _h.__code__.co_firstlineno + 3, @@ -784,14 +812,14 @@ def load_test(x, y=0): return a, b dis_load_test_quickened_code = """\ -%3d 0 RESUME_CHECK 0 +%3d RESUME_CHECK 0 -%3d 2 LOAD_FAST_LOAD_FAST 1 (x, y) - 4 STORE_FAST_STORE_FAST 50 (b, a) +%3d LOAD_FAST_LOAD_FAST 1 (x, y) + STORE_FAST_STORE_FAST 50 (b, a) -%3d 6 LOAD_FAST_LOAD_FAST 35 (a, b) - 8 BUILD_TUPLE 2 - 10 RETURN_VALUE +%3d LOAD_FAST_LOAD_FAST 35 (a, b) + BUILD_TUPLE 2 + RETURN_VALUE """ % (load_test.__code__.co_firstlineno, load_test.__code__.co_firstlineno + 1, load_test.__code__.co_firstlineno + 2) @@ -801,25 +829,25 @@ def loop_test(): load_test(i) dis_loop_test_quickened_code = """\ -%3d RESUME_CHECK 0 - -%3d BUILD_LIST 0 - LOAD_CONST 1 ((1, 2, 3)) - LIST_EXTEND 1 - LOAD_CONST 2 (3) - BINARY_OP 5 (*) - GET_ITER - >> FOR_ITER_LIST 14 (to 48) - STORE_FAST 0 (i) - -%3d LOAD_GLOBAL_MODULE 1 (load_test + NULL) - LOAD_FAST 0 (i) - CALL_PY_WITH_DEFAULTS 1 - POP_TOP - JUMP_BACKWARD 16 (to 16) - -%3d >> END_FOR - RETURN_CONST 0 (None) +%3d RESUME_CHECK 0 + +%3d BUILD_LIST 0 + LOAD_CONST 1 ((1, 2, 3)) + LIST_EXTEND 1 + LOAD_CONST 2 (3) + BINARY_OP 5 (*) + GET_ITER + L1: FOR_ITER_LIST 14 (to L2) + STORE_FAST 0 (i) + +%3d LOAD_GLOBAL_MODULE 1 (load_test + NULL) + LOAD_FAST 0 (i) + CALL_PY_WITH_DEFAULTS 1 + POP_TOP + JUMP_BACKWARD 16 (to L1) + +%3d L2: END_FOR + RETURN_CONST 0 (None) """ % (loop_test.__code__.co_firstlineno, loop_test.__code__.co_firstlineno + 1, loop_test.__code__.co_firstlineno + 2, @@ -829,14 +857,14 @@ def extended_arg_quick(): *_, _ = ... dis_extended_arg_quick_code = """\ -%3d 0 RESUME 0 - -%3d 2 LOAD_CONST 1 (Ellipsis) - 4 EXTENDED_ARG 1 - 6 UNPACK_EX 256 - 8 POP_TOP - 10 STORE_FAST 0 (_) - 12 RETURN_CONST 0 (None) +%3d RESUME 0 + +%3d LOAD_CONST 1 (Ellipsis) + EXTENDED_ARG 1 + UNPACK_EX 256 + POP_TOP + STORE_FAST 0 (_) + RETURN_CONST 0 (None) """% (extended_arg_quick.__code__.co_firstlineno, extended_arg_quick.__code__.co_firstlineno + 1,) @@ -848,74 +876,19 @@ class DisTestBase(unittest.TestCase): def strip_addresses(self, text): return re.sub(r'\b0x[0-9A-Fa-f]+\b', '0x...', text) - def find_offset_column(self, lines): - for line in lines: - if line and not line.startswith("Disassembly"): - break - else: - return 0, 0 - offset = 5 - while (line[offset] == " "): - offset += 1 - if (line[offset] == ">"): - offset += 2 - while (line[offset] == " "): - offset += 1 - end = offset - while line[end] in "0123456789": - end += 1 - return end-5, end - - def assert_offsets_increasing(self, text, delta): - expected_offset = 0 - lines = text.splitlines() - start, end = self.find_offset_column(lines) - for line in lines: - if not line: - continue - if line.startswith("Disassembly"): - expected_offset = 0 - continue - if line.startswith("Exception"): - break - offset = int(line[start:end]) - self.assertGreaterEqual(offset, expected_offset, line) - expected_offset = offset + delta - def assert_exception_table_increasing(self, lines): prev_start, prev_end = -1, -1 count = 0 for line in lines: - m = re.match(r' (\d+) to (\d+) -> \d+ \[\d+\]', line) + m = re.match(r' L(\d+) to L(\d+) -> L\d+ \[\d+\]', line) start, end = [int(g) for g in m.groups()] self.assertGreaterEqual(end, start) - self.assertGreater(start, prev_end) + self.assertGreaterEqual(start, prev_end) prev_start, prev_end = start, end count += 1 return count - def strip_offsets(self, text): - lines = text.splitlines(True) - start, end = self.find_offset_column(lines) - res = [] - lines = iter(lines) - for line in lines: - if line.startswith("Exception"): - res.append(line) - break - if not line or line.startswith("Disassembly"): - res.append(line) - else: - res.append(line[:start] + line[end:]) - num_rows = self.assert_exception_table_increasing(lines) - if num_rows: - res.append(f"{num_rows} row{'s' if num_rows > 1 else ''}\n") - return "".join(res) - - def do_disassembly_compare(self, got, expected, with_offsets=False): - if not with_offsets: - self.assert_offsets_increasing(got, 2) - got = self.strip_offsets(got) + def do_disassembly_compare(self, got, expected): if got != expected: got = self.strip_addresses(got) self.assertEqual(got, expected) @@ -938,17 +911,16 @@ def get_disassembly(self, func, lasti=-1, wrapper=True, **kwargs): def get_disassemble_as_string(self, func, lasti=-1): return self.get_disassembly(func, lasti, False) - def do_disassembly_test(self, func, expected, with_offsets=False): + def do_disassembly_test(self, func, expected, **kwargs): self.maxDiff = None - got = self.get_disassembly(func, depth=0) - self.do_disassembly_compare(got, expected, with_offsets) + got = self.get_disassembly(func, depth=0, **kwargs) + self.do_disassembly_compare(got, expected) # Add checks for dis.disco if hasattr(func, '__code__'): got_disco = io.StringIO() with contextlib.redirect_stdout(got_disco): - dis.disco(func.__code__) - self.do_disassembly_compare(got_disco.getvalue(), expected, - with_offsets) + dis.disco(func.__code__, **kwargs) + self.do_disassembly_compare(got_disco.getvalue(), expected) def test_opmap(self): self.assertEqual(dis.opmap["CACHE"], 0) @@ -976,6 +948,9 @@ def test_widths(self): def test_dis(self): self.do_disassembly_test(_f, dis_f) + def test_dis_with_offsets(self): + self.do_disassembly_test(_f, dis_f_with_offsets, show_offsets=True) + def test_bug_708901(self): self.do_disassembly_test(bug708901, dis_bug708901) @@ -1036,41 +1011,6 @@ def func(count): from test import dis_module self.do_disassembly_test(dis_module, dis_module_expected_results) - def test_big_offsets(self): - self.maxDiff = None - def func(count): - namespace = {} - func = "def foo(x):\n " + ";".join(["x = x + 1"] * count) + "\n return x" - exec(func, namespace) - return namespace['foo'] - - def expected(count, w): - s = ['''\ - 1 %*d RESUME 0 - - 2 %*d LOAD_FAST 0 (x) - %*d LOAD_CONST 1 (1) - %*d BINARY_OP 0 (+) -''' % (w, 0, w, 2, w, 4, w, 6)] - s += ['''\ - %*d STORE_FAST_LOAD_FAST 0 (x, x) - %*d LOAD_CONST 1 (1) - %*d BINARY_OP 0 (+) -''' % (w, 8*i + 10, w, 8*i + 12, w, 8*i + 14) - for i in range(count-1)] - s += ['''\ - %*d STORE_FAST 0 (x) - - 3 %*d LOAD_FAST 0 (x) - %*d RETURN_VALUE -''' % (w, 8*count + 2, w, 8*count + 4, w, 8*count + 6)] - return ''.join(s) - - for i in range(1, 5): - self.do_disassembly_test(func(i), expected(i, 4), True) - self.do_disassembly_test(func(1200), expected(1200, 4), True) - self.do_disassembly_test(func(1300), expected(1300, 5), True) - def test_disassemble_str(self): self.do_disassembly_test(expr_str, dis_expr_str) self.do_disassembly_test(simple_stmt_str, dis_simple_stmt_str) @@ -1151,7 +1091,7 @@ def test_dis_traceback(self): sys.last_exc = e tb_dis = self.get_disassemble_as_string(tb.tb_frame.f_code, tb.tb_lasti) - self.do_disassembly_test(None, tb_dis, True) + self.do_disassembly_test(None, tb_dis) def test_dis_object(self): self.assertRaises(TypeError, dis.dis, object()) @@ -1160,7 +1100,6 @@ def test_disassemble_recursive(self): def check(expected, **kwargs): dis = self.get_disassembly(_h, **kwargs) dis = self.strip_addresses(dis) - dis = self.strip_offsets(dis) self.assertEqual(dis, expected) check(dis_nested_0, depth=0) @@ -1187,73 +1126,73 @@ def code_quicken(f, times=ADAPTIVE_WARMUP_DELAY): def test_super_instructions(self): self.code_quicken(lambda: load_test(0, 0)) got = self.get_disassembly(load_test, adaptive=True) - self.do_disassembly_compare(got, dis_load_test_quickened_code, True) + self.do_disassembly_compare(got, dis_load_test_quickened_code) @cpython_only @requires_specialization def test_binary_specialize(self): binary_op_quicken = """\ - 0 0 RESUME_CHECK 0 + 0 RESUME_CHECK 0 - 1 2 LOAD_NAME 0 (a) - 4 LOAD_NAME 1 (b) - 6 %s - 10 RETURN_VALUE + 1 LOAD_NAME 0 (a) + LOAD_NAME 1 (b) + %s + RETURN_VALUE """ co_int = compile('a + b', "", "eval") self.code_quicken(lambda: exec(co_int, {}, {'a': 1, 'b': 2})) got = self.get_disassembly(co_int, adaptive=True) - self.do_disassembly_compare(got, binary_op_quicken % "BINARY_OP_ADD_INT 0 (+)", True) + self.do_disassembly_compare(got, binary_op_quicken % "BINARY_OP_ADD_INT 0 (+)") co_unicode = compile('a + b', "", "eval") self.code_quicken(lambda: exec(co_unicode, {}, {'a': 'a', 'b': 'b'})) got = self.get_disassembly(co_unicode, adaptive=True) - self.do_disassembly_compare(got, binary_op_quicken % "BINARY_OP_ADD_UNICODE 0 (+)", True) + self.do_disassembly_compare(got, binary_op_quicken % "BINARY_OP_ADD_UNICODE 0 (+)") binary_subscr_quicken = """\ - 0 0 RESUME_CHECK 0 + 0 RESUME_CHECK 0 - 1 2 LOAD_NAME 0 (a) - 4 LOAD_CONST 0 (0) - 6 %s - 10 RETURN_VALUE + 1 LOAD_NAME 0 (a) + LOAD_CONST 0 (0) + %s + RETURN_VALUE """ co_list = compile('a[0]', "", "eval") self.code_quicken(lambda: exec(co_list, {}, {'a': [0]})) got = self.get_disassembly(co_list, adaptive=True) - self.do_disassembly_compare(got, binary_subscr_quicken % "BINARY_SUBSCR_LIST_INT", True) + self.do_disassembly_compare(got, binary_subscr_quicken % "BINARY_SUBSCR_LIST_INT") co_dict = compile('a[0]', "", "eval") self.code_quicken(lambda: exec(co_dict, {}, {'a': {0: '1'}})) got = self.get_disassembly(co_dict, adaptive=True) - self.do_disassembly_compare(got, binary_subscr_quicken % "BINARY_SUBSCR_DICT", True) + self.do_disassembly_compare(got, binary_subscr_quicken % "BINARY_SUBSCR_DICT") @cpython_only @requires_specialization def test_load_attr_specialize(self): load_attr_quicken = """\ - 0 0 RESUME_CHECK 0 + 0 RESUME_CHECK 0 - 1 2 LOAD_CONST 0 ('a') - 4 LOAD_ATTR_SLOT 0 (__class__) - 24 RETURN_VALUE + 1 LOAD_CONST 0 ('a') + LOAD_ATTR_SLOT 0 (__class__) + RETURN_VALUE """ co = compile("'a'.__class__", "", "eval") self.code_quicken(lambda: exec(co, {}, {})) got = self.get_disassembly(co, adaptive=True) - self.do_disassembly_compare(got, load_attr_quicken, True) + self.do_disassembly_compare(got, load_attr_quicken) @cpython_only @requires_specialization def test_call_specialize(self): call_quicken = """\ - 0 RESUME_CHECK 0 + 0 RESUME_CHECK 0 - 1 LOAD_NAME 0 (str) - PUSH_NULL - LOAD_CONST 0 (1) - CALL_STR_1 1 - RETURN_VALUE + 1 LOAD_NAME 0 (str) + PUSH_NULL + LOAD_CONST 0 (1) + CALL_STR_1 1 + RETURN_VALUE """ co = compile("str(1)", "", "eval") self.code_quicken(lambda: exec(co, {}, {})) @@ -1276,7 +1215,7 @@ def test_loop_quicken(self): @cpython_only def test_extended_arg_quick(self): got = self.get_disassembly(extended_arg_quick) - self.do_disassembly_compare(got, dis_extended_arg_quick_code, True) + self.do_disassembly_compare(got, dis_extended_arg_quick_code) def get_cached_values(self, quickened, adaptive): def f(): @@ -1328,14 +1267,16 @@ def f(): op_offset = inst.offset - 2 cache_offset = inst.offset break + else: + opname = inst.opname else: self.fail("Can't find a CACHE entry in the function provided to do the test") assem_op = self.get_disassembly(f.__code__, lasti=op_offset, wrapper=False) assem_cache = self.get_disassembly(f.__code__, lasti=cache_offset, wrapper=False) - # Make sure --> exists and points to the correct offset - self.assertRegex(assem_op, fr"-->\s+{op_offset}") + # Make sure --> exists and points to the correct op + self.assertRegex(assem_op, fr"--> {opname}") # Make sure when lasti points to cache, it shows the same disassembly self.assertEqual(assem_op, assem_cache) @@ -1637,197 +1578,197 @@ def _prepare_test_cases(): Instruction = dis.Instruction expected_opinfo_outer = [ - Instruction(opname='MAKE_CELL', opcode=94, arg=0, argval='a', argrepr='a', offset=0, start_offset=0, starts_line=True, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='MAKE_CELL', opcode=94, arg=1, argval='b', argrepr='b', offset=2, start_offset=2, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=4, start_offset=4, starts_line=True, line_number=1, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=5, argval=(3, 4), argrepr='(3, 4)', offset=6, start_offset=6, starts_line=True, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='a', argrepr='a', offset=8, start_offset=8, starts_line=False, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=1, argval='b', argrepr='b', offset=10, start_offset=10, starts_line=False, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='BUILD_TUPLE', opcode=52, arg=2, argval=2, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=1, argval=code_object_f, argrepr=repr(code_object_f), offset=14, start_offset=14, starts_line=False, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='MAKE_FUNCTION', opcode=26, arg=None, argval=None, argrepr='', offset=16, start_offset=16, starts_line=False, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=106, arg=8, argval=8, argrepr='closure', offset=18, start_offset=18, starts_line=False, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=106, arg=1, argval=1, argrepr='defaults', offset=20, start_offset=20, starts_line=False, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='STORE_FAST', opcode=110, arg=2, argval='f', argrepr='f', offset=22, start_offset=22, starts_line=False, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=1, argval='print', argrepr='print + NULL', offset=24, start_offset=24, starts_line=True, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=0, argval='a', argrepr='a', offset=34, start_offset=34, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=1, argval='b', argrepr='b', offset=36, start_offset=36, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=2, argval='', argrepr="''", offset=38, start_offset=38, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=3, argval=1, argrepr='1', offset=40, start_offset=40, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='BUILD_LIST', opcode=47, arg=0, argval=0, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='BUILD_MAP', opcode=48, arg=0, argval=0, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=4, argval='Hello world!', argrepr="'Hello world!'", offset=46, start_offset=46, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=7, argval=7, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=2, argval='f', argrepr='f', offset=58, start_offset=58, starts_line=True, line_number=8, is_jump_target=False, positions=None), - Instruction(opname='RETURN_VALUE', opcode=36, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=8, is_jump_target=False, positions=None), + Instruction(opname='MAKE_CELL', opcode=94, arg=0, argval='a', argrepr='a', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None), + Instruction(opname='MAKE_CELL', opcode=94, arg=1, argval='b', argrepr='b', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=4, start_offset=4, starts_line=True, line_number=1, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=5, argval=(3, 4), argrepr='(3, 4)', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='a', argrepr='a', offset=8, start_offset=8, starts_line=False, line_number=2, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=1, argval='b', argrepr='b', offset=10, start_offset=10, starts_line=False, line_number=2, label=None, positions=None), + Instruction(opname='BUILD_TUPLE', opcode=52, arg=2, argval=2, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=2, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=1, argval=code_object_f, argrepr=repr(code_object_f), offset=14, start_offset=14, starts_line=False, line_number=2, label=None, positions=None), + Instruction(opname='MAKE_FUNCTION', opcode=26, arg=None, argval=None, argrepr='', offset=16, start_offset=16, starts_line=False, line_number=2, label=None, positions=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=106, arg=8, argval=8, argrepr='closure', offset=18, start_offset=18, starts_line=False, line_number=2, label=None, positions=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=106, arg=1, argval=1, argrepr='defaults', offset=20, start_offset=20, starts_line=False, line_number=2, label=None, positions=None), + Instruction(opname='STORE_FAST', opcode=110, arg=2, argval='f', argrepr='f', offset=22, start_offset=22, starts_line=False, line_number=2, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=1, argval='print', argrepr='print + NULL', offset=24, start_offset=24, starts_line=True, line_number=7, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=0, argval='a', argrepr='a', offset=34, start_offset=34, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=1, argval='b', argrepr='b', offset=36, start_offset=36, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=2, argval='', argrepr="''", offset=38, start_offset=38, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=3, argval=1, argrepr='1', offset=40, start_offset=40, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='BUILD_LIST', opcode=47, arg=0, argval=0, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='BUILD_MAP', opcode=48, arg=0, argval=0, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=4, argval='Hello world!', argrepr="'Hello world!'", offset=46, start_offset=46, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=7, argval=7, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=2, argval='f', argrepr='f', offset=58, start_offset=58, starts_line=True, line_number=8, label=None, positions=None), + Instruction(opname='RETURN_VALUE', opcode=36, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=8, label=None, positions=None), ] expected_opinfo_f = [ - Instruction(opname='COPY_FREE_VARS', opcode=62, arg=2, argval=2, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='MAKE_CELL', opcode=94, arg=0, argval='c', argrepr='c', offset=2, start_offset=2, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='MAKE_CELL', opcode=94, arg=1, argval='d', argrepr='d', offset=4, start_offset=4, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=6, start_offset=6, starts_line=True, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=2, argval=(5, 6), argrepr='(5, 6)', offset=8, start_offset=8, starts_line=True, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=3, argval='a', argrepr='a', offset=10, start_offset=10, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=4, argval='b', argrepr='b', offset=12, start_offset=12, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='c', argrepr='c', offset=14, start_offset=14, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=1, argval='d', argrepr='d', offset=16, start_offset=16, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='BUILD_TUPLE', opcode=52, arg=4, argval=4, argrepr='', offset=18, start_offset=18, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=1, argval=code_object_inner, argrepr=repr(code_object_inner), offset=20, start_offset=20, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='MAKE_FUNCTION', opcode=26, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=106, arg=8, argval=8, argrepr='closure', offset=24, start_offset=24, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=106, arg=1, argval=1, argrepr='defaults', offset=26, start_offset=26, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='STORE_FAST', opcode=110, arg=2, argval='inner', argrepr='inner', offset=28, start_offset=28, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=1, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=3, argval='a', argrepr='a', offset=40, start_offset=40, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=4, argval='b', argrepr='b', offset=42, start_offset=42, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=0, argval='c', argrepr='c', offset=44, start_offset=44, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=1, argval='d', argrepr='d', offset=46, start_offset=46, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=4, argval=4, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=2, argval='inner', argrepr='inner', offset=58, start_offset=58, starts_line=True, line_number=6, is_jump_target=False, positions=None), - Instruction(opname='RETURN_VALUE', opcode=36, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=6, is_jump_target=False, positions=None), + Instruction(opname='COPY_FREE_VARS', opcode=62, arg=2, argval=2, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None), + Instruction(opname='MAKE_CELL', opcode=94, arg=0, argval='c', argrepr='c', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='MAKE_CELL', opcode=94, arg=1, argval='d', argrepr='d', offset=4, start_offset=4, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=2, argval=(5, 6), argrepr='(5, 6)', offset=8, start_offset=8, starts_line=True, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=3, argval='a', argrepr='a', offset=10, start_offset=10, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=4, argval='b', argrepr='b', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='c', argrepr='c', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=1, argval='d', argrepr='d', offset=16, start_offset=16, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='BUILD_TUPLE', opcode=52, arg=4, argval=4, argrepr='', offset=18, start_offset=18, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=1, argval=code_object_inner, argrepr=repr(code_object_inner), offset=20, start_offset=20, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='MAKE_FUNCTION', opcode=26, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=106, arg=8, argval=8, argrepr='closure', offset=24, start_offset=24, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=106, arg=1, argval=1, argrepr='defaults', offset=26, start_offset=26, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='STORE_FAST', opcode=110, arg=2, argval='inner', argrepr='inner', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=1, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=5, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=3, argval='a', argrepr='a', offset=40, start_offset=40, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=4, argval='b', argrepr='b', offset=42, start_offset=42, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=0, argval='c', argrepr='c', offset=44, start_offset=44, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=1, argval='d', argrepr='d', offset=46, start_offset=46, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=4, argval=4, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=2, argval='inner', argrepr='inner', offset=58, start_offset=58, starts_line=True, line_number=6, label=None, positions=None), + Instruction(opname='RETURN_VALUE', opcode=36, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=6, label=None, positions=None), ] expected_opinfo_inner = [ - Instruction(opname='COPY_FREE_VARS', opcode=62, arg=4, argval=4, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=2, start_offset=2, starts_line=True, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=1, argval='print', argrepr='print + NULL', offset=4, start_offset=4, starts_line=True, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=2, argval='a', argrepr='a', offset=14, start_offset=14, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=3, argval='b', argrepr='b', offset=16, start_offset=16, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=4, argval='c', argrepr='c', offset=18, start_offset=18, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=5, argval='d', argrepr='d', offset=20, start_offset=20, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST_LOAD_FAST', opcode=88, arg=1, argval=('e', 'f'), argrepr='e, f', offset=22, start_offset=22, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=6, argval=6, argrepr='', offset=24, start_offset=24, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=32, start_offset=32, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='RETURN_CONST', opcode=103, arg=0, argval=None, argrepr='None', offset=34, start_offset=34, starts_line=False, line_number=4, is_jump_target=False, positions=None), + Instruction(opname='COPY_FREE_VARS', opcode=62, arg=4, argval=4, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None), + Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=2, start_offset=2, starts_line=True, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=1, argval='print', argrepr='print + NULL', offset=4, start_offset=4, starts_line=True, line_number=4, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=2, argval='a', argrepr='a', offset=14, start_offset=14, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=3, argval='b', argrepr='b', offset=16, start_offset=16, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=4, argval='c', argrepr='c', offset=18, start_offset=18, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=5, argval='d', argrepr='d', offset=20, start_offset=20, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='LOAD_FAST_LOAD_FAST', opcode=88, arg=1, argval=('e', 'f'), argrepr='e, f', offset=22, start_offset=22, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=6, argval=6, argrepr='', offset=24, start_offset=24, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=32, start_offset=32, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='RETURN_CONST', opcode=103, arg=0, argval=None, argrepr='None', offset=34, start_offset=34, starts_line=False, line_number=4, label=None, positions=None), ] expected_opinfo_jumpy = [ - Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=1, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=1, argval='range', argrepr='range + NULL', offset=2, start_offset=2, starts_line=True, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=1, argval=10, argrepr='10', offset=12, start_offset=12, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='GET_ITER', opcode=19, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='FOR_ITER', opcode=72, arg=30, argval=88, argrepr='to 88', offset=24, start_offset=24, starts_line=False, line_number=3, is_jump_target=True, positions=None), - Instruction(opname='STORE_FAST', opcode=110, arg=0, argval='i', argrepr='i', offset=28, start_offset=28, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=40, start_offset=40, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=50, start_offset=50, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=52, start_offset=52, starts_line=True, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=2, argval=4, argrepr='4', offset=54, start_offset=54, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=58, arg=18, argval='<', argrepr='bool(<)', offset=56, start_offset=56, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=2, argval=68, argrepr='to 68', offset=60, start_offset=60, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=77, arg=22, argval=24, argrepr='to 24', offset=64, start_offset=64, starts_line=True, line_number=6, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=68, start_offset=68, starts_line=True, line_number=7, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=3, argval=6, argrepr='6', offset=70, start_offset=70, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=58, arg=148, argval='>', argrepr='bool(>)', offset=72, start_offset=72, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=2, argval=84, argrepr='to 84', offset=76, start_offset=76, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=77, arg=30, argval=24, argrepr='to 24', offset=80, start_offset=80, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=84, start_offset=84, starts_line=True, line_number=8, is_jump_target=True, positions=None), - Instruction(opname='JUMP_FORWARD', opcode=79, arg=12, argval=112, argrepr='to 112', offset=86, start_offset=86, starts_line=False, line_number=8, is_jump_target=False, positions=None), - Instruction(opname='END_FOR', opcode=11, arg=None, argval=None, argrepr='', offset=88, start_offset=88, starts_line=True, line_number=3, is_jump_target=True, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=90, start_offset=90, starts_line=True, line_number=10, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=4, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=100, start_offset=100, starts_line=False, line_number=10, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=102, start_offset=102, starts_line=False, line_number=10, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=110, start_offset=110, starts_line=False, line_number=10, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST_CHECK', opcode=87, arg=0, argval='i', argrepr='i', offset=112, start_offset=112, starts_line=True, line_number=11, is_jump_target=True, positions=None), - Instruction(opname='TO_BOOL', opcode=40, arg=None, argval=None, argrepr='', offset=114, start_offset=114, starts_line=False, line_number=11, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=40, argval=206, argrepr='to 206', offset=122, start_offset=122, starts_line=False, line_number=11, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=126, start_offset=126, starts_line=True, line_number=12, is_jump_target=True, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=136, start_offset=136, starts_line=False, line_number=12, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=138, start_offset=138, starts_line=False, line_number=12, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=146, start_offset=146, starts_line=False, line_number=12, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=148, start_offset=148, starts_line=True, line_number=13, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=5, argval=1, argrepr='1', offset=150, start_offset=150, starts_line=False, line_number=13, is_jump_target=False, positions=None), - Instruction(opname='BINARY_OP', opcode=45, arg=23, argval=23, argrepr='-=', offset=152, start_offset=152, starts_line=False, line_number=13, is_jump_target=False, positions=None), - Instruction(opname='STORE_FAST', opcode=110, arg=0, argval='i', argrepr='i', offset=156, start_offset=156, starts_line=False, line_number=13, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=158, start_offset=158, starts_line=True, line_number=14, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=3, argval=6, argrepr='6', offset=160, start_offset=160, starts_line=False, line_number=14, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=58, arg=148, argval='>', argrepr='bool(>)', offset=162, start_offset=162, starts_line=False, line_number=14, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=2, argval=174, argrepr='to 174', offset=166, start_offset=166, starts_line=False, line_number=14, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=77, arg=31, argval=112, argrepr='to 112', offset=170, start_offset=170, starts_line=True, line_number=15, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=174, start_offset=174, starts_line=True, line_number=16, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=2, argval=4, argrepr='4', offset=176, start_offset=176, starts_line=False, line_number=16, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=58, arg=18, argval='<', argrepr='bool(<)', offset=178, start_offset=178, starts_line=False, line_number=16, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=1, argval=188, argrepr='to 188', offset=182, start_offset=182, starts_line=False, line_number=16, is_jump_target=False, positions=None), - Instruction(opname='JUMP_FORWARD', opcode=79, arg=20, argval=228, argrepr='to 228', offset=186, start_offset=186, starts_line=True, line_number=17, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=188, start_offset=188, starts_line=True, line_number=11, is_jump_target=True, positions=None), - Instruction(opname='TO_BOOL', opcode=40, arg=None, argval=None, argrepr='', offset=190, start_offset=190, starts_line=False, line_number=11, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=2, argval=206, argrepr='to 206', offset=198, start_offset=198, starts_line=False, line_number=11, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=77, arg=40, argval=126, argrepr='to 126', offset=202, start_offset=202, starts_line=False, line_number=11, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=206, start_offset=206, starts_line=True, line_number=19, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=216, start_offset=216, starts_line=False, line_number=19, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=218, start_offset=218, starts_line=False, line_number=19, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=226, start_offset=226, starts_line=False, line_number=19, is_jump_target=False, positions=None), - Instruction(opname='NOP', opcode=30, arg=None, argval=None, argrepr='', offset=228, start_offset=228, starts_line=True, line_number=20, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=5, argval=1, argrepr='1', offset=230, start_offset=230, starts_line=True, line_number=21, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=7, argval=0, argrepr='0', offset=232, start_offset=232, starts_line=False, line_number=21, is_jump_target=False, positions=None), - Instruction(opname='BINARY_OP', opcode=45, arg=11, argval=11, argrepr='/', offset=234, start_offset=234, starts_line=False, line_number=21, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=238, start_offset=238, starts_line=False, line_number=21, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=240, start_offset=240, starts_line=True, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='BEFORE_WITH', opcode=2, arg=None, argval=None, argrepr='', offset=242, start_offset=242, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='STORE_FAST', opcode=110, arg=1, argval='dodgy', argrepr='dodgy', offset=244, start_offset=244, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=246, start_offset=246, starts_line=True, line_number=26, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=8, argval='Never reach this', argrepr="'Never reach this'", offset=256, start_offset=256, starts_line=False, line_number=26, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=258, start_offset=258, starts_line=False, line_number=26, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=266, start_offset=266, starts_line=False, line_number=26, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=0, argval=None, argrepr='None', offset=268, start_offset=268, starts_line=True, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=0, argval=None, argrepr='None', offset=270, start_offset=270, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=0, argval=None, argrepr='None', offset=272, start_offset=272, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=2, argval=2, argrepr='', offset=274, start_offset=274, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=282, start_offset=282, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=284, start_offset=284, starts_line=True, line_number=28, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=294, start_offset=294, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=296, start_offset=296, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=304, start_offset=304, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='RETURN_CONST', opcode=103, arg=0, argval=None, argrepr='None', offset=306, start_offset=306, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=33, arg=None, argval=None, argrepr='', offset=308, start_offset=308, starts_line=True, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='WITH_EXCEPT_START', opcode=44, arg=None, argval=None, argrepr='', offset=310, start_offset=310, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='TO_BOOL', opcode=40, arg=None, argval=None, argrepr='', offset=312, start_offset=312, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=1, argval=326, argrepr='to 326', offset=320, start_offset=320, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=102, arg=2, argval=2, argrepr='', offset=324, start_offset=324, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=326, start_offset=326, starts_line=False, line_number=25, is_jump_target=True, positions=None), - Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=328, start_offset=328, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=330, start_offset=330, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=332, start_offset=332, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=77, arg=27, argval=284, argrepr='to 284', offset=334, start_offset=334, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='COPY', opcode=61, arg=3, argval=3, argrepr='', offset=338, start_offset=338, starts_line=True, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=340, start_offset=340, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=342, start_offset=342, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=33, arg=None, argval=None, argrepr='', offset=344, start_offset=344, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=346, start_offset=346, starts_line=True, line_number=22, is_jump_target=False, positions=None), - Instruction(opname='CHECK_EXC_MATCH', opcode=7, arg=None, argval=None, argrepr='', offset=356, start_offset=356, starts_line=False, line_number=22, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=15, argval=392, argrepr='to 392', offset=358, start_offset=358, starts_line=False, line_number=22, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=362, start_offset=362, starts_line=False, line_number=22, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=364, start_offset=364, starts_line=True, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=9, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=374, start_offset=374, starts_line=False, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=376, start_offset=376, starts_line=False, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=384, start_offset=384, starts_line=False, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=386, start_offset=386, starts_line=False, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=77, arg=54, argval=284, argrepr='to 284', offset=388, start_offset=388, starts_line=False, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=392, start_offset=392, starts_line=True, line_number=22, is_jump_target=True, positions=None), - Instruction(opname='COPY', opcode=61, arg=3, argval=3, argrepr='', offset=394, start_offset=394, starts_line=True, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=396, start_offset=396, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=398, start_offset=398, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=33, arg=None, argval=None, argrepr='', offset=400, start_offset=400, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=402, start_offset=402, starts_line=True, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=412, start_offset=412, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=414, start_offset=414, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=422, start_offset=422, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=424, start_offset=424, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='COPY', opcode=61, arg=3, argval=3, argrepr='', offset=426, start_offset=426, starts_line=True, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=428, start_offset=428, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=430, start_offset=430, starts_line=False, line_number=None, is_jump_target=False, positions=None), + Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=1, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=1, argval='range', argrepr='range + NULL', offset=2, start_offset=2, starts_line=True, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=1, argval=10, argrepr='10', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='GET_ITER', opcode=19, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='FOR_ITER', opcode=72, arg=30, argval=88, argrepr='to L4', offset=24, start_offset=24, starts_line=False, line_number=3, label=1, positions=None), + Instruction(opname='STORE_FAST', opcode=110, arg=0, argval='i', argrepr='i', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=4, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=40, start_offset=40, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=50, start_offset=50, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=52, start_offset=52, starts_line=True, line_number=5, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=2, argval=4, argrepr='4', offset=54, start_offset=54, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='COMPARE_OP', opcode=58, arg=18, argval='<', argrepr='bool(<)', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=2, argval=68, argrepr='to L2', offset=60, start_offset=60, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=77, arg=22, argval=24, argrepr='to L1', offset=64, start_offset=64, starts_line=True, line_number=6, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=68, start_offset=68, starts_line=True, line_number=7, label=2, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=3, argval=6, argrepr='6', offset=70, start_offset=70, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='COMPARE_OP', opcode=58, arg=148, argval='>', argrepr='bool(>)', offset=72, start_offset=72, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=2, argval=84, argrepr='to L3', offset=76, start_offset=76, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=77, arg=30, argval=24, argrepr='to L1', offset=80, start_offset=80, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=84, start_offset=84, starts_line=True, line_number=8, label=3, positions=None), + Instruction(opname='JUMP_FORWARD', opcode=79, arg=12, argval=112, argrepr='to L5', offset=86, start_offset=86, starts_line=False, line_number=8, label=None, positions=None), + Instruction(opname='END_FOR', opcode=11, arg=None, argval=None, argrepr='', offset=88, start_offset=88, starts_line=True, line_number=3, label=4, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=90, start_offset=90, starts_line=True, line_number=10, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=4, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=100, start_offset=100, starts_line=False, line_number=10, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=102, start_offset=102, starts_line=False, line_number=10, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=110, start_offset=110, starts_line=False, line_number=10, label=None, positions=None), + Instruction(opname='LOAD_FAST_CHECK', opcode=87, arg=0, argval='i', argrepr='i', offset=112, start_offset=112, starts_line=True, line_number=11, label=5, positions=None), + Instruction(opname='TO_BOOL', opcode=40, arg=None, argval=None, argrepr='', offset=114, start_offset=114, starts_line=False, line_number=11, label=None, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=40, argval=206, argrepr='to L9', offset=122, start_offset=122, starts_line=False, line_number=11, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=126, start_offset=126, starts_line=True, line_number=12, label=6, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=136, start_offset=136, starts_line=False, line_number=12, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=138, start_offset=138, starts_line=False, line_number=12, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=146, start_offset=146, starts_line=False, line_number=12, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=148, start_offset=148, starts_line=True, line_number=13, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=5, argval=1, argrepr='1', offset=150, start_offset=150, starts_line=False, line_number=13, label=None, positions=None), + Instruction(opname='BINARY_OP', opcode=45, arg=23, argval=23, argrepr='-=', offset=152, start_offset=152, starts_line=False, line_number=13, label=None, positions=None), + Instruction(opname='STORE_FAST', opcode=110, arg=0, argval='i', argrepr='i', offset=156, start_offset=156, starts_line=False, line_number=13, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=158, start_offset=158, starts_line=True, line_number=14, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=3, argval=6, argrepr='6', offset=160, start_offset=160, starts_line=False, line_number=14, label=None, positions=None), + Instruction(opname='COMPARE_OP', opcode=58, arg=148, argval='>', argrepr='bool(>)', offset=162, start_offset=162, starts_line=False, line_number=14, label=None, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=2, argval=174, argrepr='to L7', offset=166, start_offset=166, starts_line=False, line_number=14, label=None, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=77, arg=31, argval=112, argrepr='to L5', offset=170, start_offset=170, starts_line=True, line_number=15, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=174, start_offset=174, starts_line=True, line_number=16, label=7, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=2, argval=4, argrepr='4', offset=176, start_offset=176, starts_line=False, line_number=16, label=None, positions=None), + Instruction(opname='COMPARE_OP', opcode=58, arg=18, argval='<', argrepr='bool(<)', offset=178, start_offset=178, starts_line=False, line_number=16, label=None, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=1, argval=188, argrepr='to L8', offset=182, start_offset=182, starts_line=False, line_number=16, label=None, positions=None), + Instruction(opname='JUMP_FORWARD', opcode=79, arg=20, argval=228, argrepr='to L10', offset=186, start_offset=186, starts_line=True, line_number=17, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=188, start_offset=188, starts_line=True, line_number=11, label=8, positions=None), + Instruction(opname='TO_BOOL', opcode=40, arg=None, argval=None, argrepr='', offset=190, start_offset=190, starts_line=False, line_number=11, label=None, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=2, argval=206, argrepr='to L9', offset=198, start_offset=198, starts_line=False, line_number=11, label=None, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=77, arg=40, argval=126, argrepr='to L6', offset=202, start_offset=202, starts_line=False, line_number=11, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=206, start_offset=206, starts_line=True, line_number=19, label=9, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=216, start_offset=216, starts_line=False, line_number=19, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=218, start_offset=218, starts_line=False, line_number=19, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=226, start_offset=226, starts_line=False, line_number=19, label=None, positions=None), + Instruction(opname='NOP', opcode=30, arg=None, argval=None, argrepr='', offset=228, start_offset=228, starts_line=True, line_number=20, label=10, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=5, argval=1, argrepr='1', offset=230, start_offset=230, starts_line=True, line_number=21, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=7, argval=0, argrepr='0', offset=232, start_offset=232, starts_line=False, line_number=21, label=None, positions=None), + Instruction(opname='BINARY_OP', opcode=45, arg=11, argval=11, argrepr='/', offset=234, start_offset=234, starts_line=False, line_number=21, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=238, start_offset=238, starts_line=False, line_number=21, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=240, start_offset=240, starts_line=True, line_number=25, label=None, positions=None), + Instruction(opname='BEFORE_WITH', opcode=2, arg=None, argval=None, argrepr='', offset=242, start_offset=242, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='STORE_FAST', opcode=110, arg=1, argval='dodgy', argrepr='dodgy', offset=244, start_offset=244, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=246, start_offset=246, starts_line=True, line_number=26, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=8, argval='Never reach this', argrepr="'Never reach this'", offset=256, start_offset=256, starts_line=False, line_number=26, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=258, start_offset=258, starts_line=False, line_number=26, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=266, start_offset=266, starts_line=False, line_number=26, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=0, argval=None, argrepr='None', offset=268, start_offset=268, starts_line=True, line_number=25, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=0, argval=None, argrepr='None', offset=270, start_offset=270, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=0, argval=None, argrepr='None', offset=272, start_offset=272, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=2, argval=2, argrepr='', offset=274, start_offset=274, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=282, start_offset=282, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=284, start_offset=284, starts_line=True, line_number=28, label=11, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=294, start_offset=294, starts_line=False, line_number=28, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=296, start_offset=296, starts_line=False, line_number=28, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=304, start_offset=304, starts_line=False, line_number=28, label=None, positions=None), + Instruction(opname='RETURN_CONST', opcode=103, arg=0, argval=None, argrepr='None', offset=306, start_offset=306, starts_line=False, line_number=28, label=None, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=33, arg=None, argval=None, argrepr='', offset=308, start_offset=308, starts_line=True, line_number=25, label=None, positions=None), + Instruction(opname='WITH_EXCEPT_START', opcode=44, arg=None, argval=None, argrepr='', offset=310, start_offset=310, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='TO_BOOL', opcode=40, arg=None, argval=None, argrepr='', offset=312, start_offset=312, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=1, argval=326, argrepr='to L12', offset=320, start_offset=320, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='RERAISE', opcode=102, arg=2, argval=2, argrepr='', offset=324, start_offset=324, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=326, start_offset=326, starts_line=False, line_number=25, label=12, positions=None), + Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=328, start_offset=328, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=330, start_offset=330, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=332, start_offset=332, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=77, arg=27, argval=284, argrepr='to L11', offset=334, start_offset=334, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='COPY', opcode=61, arg=3, argval=3, argrepr='', offset=338, start_offset=338, starts_line=True, line_number=None, label=None, positions=None), + Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=340, start_offset=340, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=342, start_offset=342, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=33, arg=None, argval=None, argrepr='', offset=344, start_offset=344, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=346, start_offset=346, starts_line=True, line_number=22, label=None, positions=None), + Instruction(opname='CHECK_EXC_MATCH', opcode=7, arg=None, argval=None, argrepr='', offset=356, start_offset=356, starts_line=False, line_number=22, label=None, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=15, argval=392, argrepr='to L13', offset=358, start_offset=358, starts_line=False, line_number=22, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=362, start_offset=362, starts_line=False, line_number=22, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=364, start_offset=364, starts_line=True, line_number=23, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=9, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=374, start_offset=374, starts_line=False, line_number=23, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=376, start_offset=376, starts_line=False, line_number=23, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=384, start_offset=384, starts_line=False, line_number=23, label=None, positions=None), + Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=386, start_offset=386, starts_line=False, line_number=23, label=None, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=77, arg=54, argval=284, argrepr='to L11', offset=388, start_offset=388, starts_line=False, line_number=23, label=None, positions=None), + Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=392, start_offset=392, starts_line=True, line_number=22, label=13, positions=None), + Instruction(opname='COPY', opcode=61, arg=3, argval=3, argrepr='', offset=394, start_offset=394, starts_line=True, line_number=None, label=None, positions=None), + Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=396, start_offset=396, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=398, start_offset=398, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=33, arg=None, argval=None, argrepr='', offset=400, start_offset=400, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=402, start_offset=402, starts_line=True, line_number=28, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=412, start_offset=412, starts_line=False, line_number=28, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=414, start_offset=414, starts_line=False, line_number=28, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=422, start_offset=422, starts_line=False, line_number=28, label=None, positions=None), + Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=424, start_offset=424, starts_line=False, line_number=28, label=None, positions=None), + Instruction(opname='COPY', opcode=61, arg=3, argval=3, argrepr='', offset=426, start_offset=426, starts_line=True, line_number=None, label=None, positions=None), + Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=428, start_offset=428, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=430, start_offset=430, starts_line=False, line_number=None, label=None, positions=None), ] # One last piece of inspect fodder to check the default line number handling def simple(): pass expected_opinfo_simple = [ - Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=simple.__code__.co_firstlineno, is_jump_target=False, positions=None), - Instruction(opname='RETURN_CONST', opcode=103, arg=0, argval=None, argrepr='None', offset=2, start_offset=2, starts_line=False, line_number=simple.__code__.co_firstlineno, is_jump_target=False), + Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=simple.__code__.co_firstlineno, label=None, positions=None), + Instruction(opname='RETURN_CONST', opcode=103, arg=0, argval=None, argrepr='None', offset=2, start_offset=2, starts_line=False, line_number=simple.__code__.co_firstlineno, label=None), ] @@ -1951,7 +1892,7 @@ def roots(a, b, c): def test_oparg_alias(self): instruction = Instruction(opname="NOP", opcode=dis.opmap["NOP"], arg=None, argval=None, - argrepr='', offset=10, start_offset=10, starts_line=True, line_number=1, is_jump_target=False, + argrepr='', offset=10, start_offset=10, starts_line=True, line_number=1, label=None, positions=None) self.assertEqual(instruction.arg, instruction.oparg) @@ -1959,7 +1900,7 @@ def test_baseopname_and_baseopcode(self): # Standard instructions for name, code in dis.opmap.items(): instruction = Instruction(opname=name, opcode=code, arg=None, argval=None, argrepr='', offset=0, - start_offset=0, starts_line=True, line_number=1, is_jump_target=False, positions=None) + start_offset=0, starts_line=True, line_number=1, label=None, positions=None) baseopname = instruction.baseopname baseopcode = instruction.baseopcode self.assertIsNotNone(baseopname) @@ -1970,7 +1911,7 @@ def test_baseopname_and_baseopcode(self): # Specialized instructions for name in opcode._specialized_opmap: instruction = Instruction(opname=name, opcode=dis._all_opmap[name], arg=None, argval=None, argrepr='', - offset=0, start_offset=0, starts_line=True, line_number=1, is_jump_target=False, positions=None) + offset=0, start_offset=0, starts_line=True, line_number=1, label=None, positions=None) baseopname = instruction.baseopname baseopcode = instruction.baseopcode self.assertIn(name, opcode._specializations[baseopname]) @@ -1979,30 +1920,32 @@ def test_baseopname_and_baseopcode(self): def test_jump_target(self): # Non-jump instructions should return None instruction = Instruction(opname="NOP", opcode=dis.opmap["NOP"], arg=None, argval=None, - argrepr='', offset=10, start_offset=10, starts_line=True, line_number=1, is_jump_target=False, + argrepr='', offset=10, start_offset=10, starts_line=True, line_number=1, label=None, positions=None) self.assertIsNone(instruction.jump_target) delta = 100 instruction = Instruction(opname="JUMP_FORWARD", opcode=dis.opmap["JUMP_FORWARD"], arg=delta, argval=delta, - argrepr='', offset=10, start_offset=10, starts_line=True, line_number=1, is_jump_target=False, + argrepr='', offset=10, start_offset=10, starts_line=True, line_number=1, label=None, positions=None) self.assertEqual(10 + 2 + 100*2, instruction.jump_target) # Test negative deltas instruction = Instruction(opname="JUMP_BACKWARD", opcode=dis.opmap["JUMP_BACKWARD"], arg=delta, argval=delta, - argrepr='', offset=200, start_offset=200, starts_line=True, line_number=1, is_jump_target=False, + argrepr='', offset=200, start_offset=200, starts_line=True, line_number=1, label=None, positions=None) self.assertEqual(200 + 2 - 100*2 + 2*1, instruction.jump_target) # Make sure cache entries are handled instruction = Instruction(opname="SEND", opcode=dis.opmap["SEND"], arg=delta, argval=delta, - argrepr='', offset=10, start_offset=10, starts_line=True, line_number=1, is_jump_target=False, + argrepr='', offset=10, start_offset=10, starts_line=True, line_number=1, label=None, positions=None) self.assertEqual(10 + 2 + 1*2 + 100*2, instruction.jump_target) def test_argval_argrepr(self): - f = dis.Instruction._get_argval_argrepr + def f(*args): + return dis.Instruction._get_argval_argrepr( + *args, labels_map={24: 1}) offset = 42 co_consts = (0, 1, 2, 3) @@ -2012,7 +1955,7 @@ def test_argval_argrepr(self): self.assertEqual(f(opcode.opmap["POP_TOP"], None, *args), (None, '')) self.assertEqual(f(opcode.opmap["LOAD_CONST"], 1, *args), (1, '1')) self.assertEqual(f(opcode.opmap["LOAD_GLOBAL"], 2, *args), ('a', 'a')) - self.assertEqual(f(opcode.opmap["JUMP_BACKWARD"], 11, *args), (24, 'to 24')) + self.assertEqual(f(opcode.opmap["JUMP_BACKWARD"], 11, *args), (24, 'to L1')) self.assertEqual(f(opcode.opmap["COMPARE_OP"], 3, *args), ('<', '<')) self.assertEqual(f(opcode.opmap["SET_FUNCTION_ATTRIBUTE"], 2, *args), (2, 'kwdefaults')) self.assertEqual(f(opcode.opmap["BINARY_OP"], 3, *args), (3, '<<')) @@ -2149,7 +2092,7 @@ def test_from_traceback_dis(self): self.maxDiff = None tb = get_tb() b = dis.Bytecode.from_traceback(tb) - self.assertEqual(self.strip_offsets(b.dis()), dis_traceback) + self.assertEqual(b.dis(), dis_traceback) @requires_debug_ranges() def test_bytecode_co_positions(self): diff --git a/Misc/NEWS.d/next/Library/2023-11-16-17-18-09.gh-issue-112137.QvjGjN.rst b/Misc/NEWS.d/next/Library/2023-11-16-17-18-09.gh-issue-112137.QvjGjN.rst new file mode 100644 index 00000000000000..6b61d051966846 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-16-17-18-09.gh-issue-112137.QvjGjN.rst @@ -0,0 +1 @@ +Change :mod:`dis` output to display logical labels for jump targets instead of offsets. From 14e539f0977aaf2768c58f1dcbbbab5ad0205ec5 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Thu, 23 Nov 2023 01:55:25 +0300 Subject: [PATCH 041/228] gh-111809: Fix `test_deep_repr` from `test_userdict` on WASI (GH-112229) --- Lib/test/test_userdict.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_userdict.py b/Lib/test/test_userdict.py index 483910aaa4620e..9a03f2d04ce970 100644 --- a/Lib/test/test_userdict.py +++ b/Lib/test/test_userdict.py @@ -1,6 +1,6 @@ # Check every path through every method of UserDict -from test import mapping_tests +from test import mapping_tests, support import unittest import collections @@ -213,6 +213,11 @@ class G(collections.UserDict): else: self.fail("g[42] didn't raise KeyError") + # Decorate existing test with recursion limit, because + # the test is for C structure, but `UserDict` is a Python structure. + test_repr_deep = support.infinite_recursion()( + mapping_tests.TestHashMappingProtocol.test_repr_deep, + ) if __name__ == "__main__": From 9e56eedd018e1a4681789e634016cbb7699dcb8a Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 22 Nov 2023 17:55:00 -0700 Subject: [PATCH 042/228] gh-76785: Return an "excinfo" Object From Interpreter.run() (gh-111573) --- Include/internal/pycore_crossinterp.h | 33 +- Lib/test/support/interpreters.py | 18 +- Lib/test/test__xxinterpchannels.py | 12 +- Lib/test/test__xxsubinterpreters.py | 19 +- Lib/test/test_import/__init__.py | 10 +- Lib/test/test_importlib/test_util.py | 22 +- Lib/test/test_interpreters.py | 5 + Modules/_xxsubinterpretersmodule.c | 94 ++---- Python/crossinterp.c | 448 +++++++++++++++++++------- 9 files changed, 418 insertions(+), 243 deletions(-) diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h index ee9ff0090c2484..ec9dac96292f35 100644 --- a/Include/internal/pycore_crossinterp.h +++ b/Include/internal/pycore_crossinterp.h @@ -170,9 +170,14 @@ extern void _PyXI_Fini(PyInterpreterState *interp); // of the exception in the calling interpreter. typedef struct _excinfo { - const char *type; + struct _excinfo_type { + PyTypeObject *builtin; + const char *name; + const char *qualname; + const char *module; + } type; const char *msg; -} _Py_excinfo; +} _PyXI_excinfo; typedef enum error_code { @@ -193,13 +198,13 @@ typedef struct _sharedexception { // The kind of error to propagate. _PyXI_errcode code; // The exception information to propagate, if applicable. - // This is populated only for _PyXI_ERR_UNCAUGHT_EXCEPTION. - _Py_excinfo uncaught; -} _PyXI_exception_info; + // This is populated only for some error codes, + // but always for _PyXI_ERR_UNCAUGHT_EXCEPTION. + _PyXI_excinfo uncaught; +} _PyXI_error; + +PyAPI_FUNC(PyObject *) _PyXI_ApplyError(_PyXI_error *err); -PyAPI_FUNC(void) _PyXI_ApplyExceptionInfo( - _PyXI_exception_info *info, - PyObject *exctype); typedef struct xi_session _PyXI_session; typedef struct _sharedns _PyXI_namespace; @@ -251,13 +256,13 @@ struct xi_session { // This is set if the interpreter is entered and raised an exception // that needs to be handled in some special way during exit. - _PyXI_errcode *exc_override; + _PyXI_errcode *error_override; // This is set if exit captured an exception to propagate. - _PyXI_exception_info *exc; + _PyXI_error *error; // -- pre-allocated memory -- - _PyXI_exception_info _exc; - _PyXI_errcode _exc_override; + _PyXI_error _error; + _PyXI_errcode _error_override; }; PyAPI_FUNC(int) _PyXI_Enter( @@ -266,9 +271,7 @@ PyAPI_FUNC(int) _PyXI_Enter( PyObject *nsupdates); PyAPI_FUNC(void) _PyXI_Exit(_PyXI_session *session); -PyAPI_FUNC(void) _PyXI_ApplyCapturedException( - _PyXI_session *session, - PyObject *excwrapper); +PyAPI_FUNC(PyObject *) _PyXI_ApplyCapturedException(_PyXI_session *session); PyAPI_FUNC(int) _PyXI_HasCapturedException(_PyXI_session *session); diff --git a/Lib/test/support/interpreters.py b/Lib/test/support/interpreters.py index ab9342b767dfae..089fe7ef56df78 100644 --- a/Lib/test/support/interpreters.py +++ b/Lib/test/support/interpreters.py @@ -14,6 +14,7 @@ __all__ = [ 'Interpreter', 'get_current', 'get_main', 'create', 'list_all', + 'RunFailedError', 'SendChannel', 'RecvChannel', 'create_channel', 'list_all_channels', 'is_shareable', 'ChannelError', 'ChannelNotFoundError', @@ -21,6 +22,19 @@ ] +class RunFailedError(RuntimeError): + + def __init__(self, excinfo): + msg = excinfo.formatted + if not msg: + if excinfo.type and snapshot.msg: + msg = f'{snapshot.type.__name__}: {snapshot.msg}' + else: + msg = snapshot.type.__name__ or snapshot.msg + super().__init__(msg) + self.snapshot = excinfo + + def create(*, isolated=True): """Return a new (idle) Python interpreter.""" id = _interpreters.create(isolated=isolated) @@ -110,7 +124,9 @@ def run(self, src_str, /, channels=None): that time, the previous interpreter is allowed to run in other threads. """ - _interpreters.exec(self._id, src_str, channels) + excinfo = _interpreters.exec(self._id, src_str, channels) + if excinfo is not None: + raise RunFailedError(excinfo) def create_channel(): diff --git a/Lib/test/test__xxinterpchannels.py b/Lib/test/test__xxinterpchannels.py index 1c1ef3fac9d65f..2b75e2f1916c82 100644 --- a/Lib/test/test__xxinterpchannels.py +++ b/Lib/test/test__xxinterpchannels.py @@ -1017,16 +1017,16 @@ def test_close_multiple_users(self): _channels.recv({cid}) """)) channels.close(cid) - with self.assertRaises(interpreters.RunFailedError) as cm: - interpreters.run_string(id1, dedent(f""" + + excsnap = interpreters.run_string(id1, dedent(f""" _channels.send({cid}, b'spam') """)) - self.assertIn('ChannelClosedError', str(cm.exception)) - with self.assertRaises(interpreters.RunFailedError) as cm: - interpreters.run_string(id2, dedent(f""" + self.assertEqual(excsnap.type.__name__, 'ChannelClosedError') + + excsnap = interpreters.run_string(id2, dedent(f""" _channels.send({cid}, b'spam') """)) - self.assertIn('ChannelClosedError', str(cm.exception)) + self.assertEqual(excsnap.type.__name__, 'ChannelClosedError') def test_close_multiple_times(self): cid = channels.create() diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index d2056c9f52287b..64a9db95e5eaf5 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -940,7 +940,6 @@ def add_module(self, modname, text): return script_helper.make_script(tempdir, modname, text) def run_script(self, text, *, fails=False): - excwrapper = interpreters.RunFailedError r, w = os.pipe() try: script = dedent(f""" @@ -956,11 +955,12 @@ class NeverError(Exception): pass raise NeverError # never raised """).format(dedent(text)) if fails: - with self.assertRaises(excwrapper) as caught: - interpreters.run_string(self.id, script) - return caught.exception + err = interpreters.run_string(self.id, script) + self.assertIsNot(err, None) + return err else: - interpreters.run_string(self.id, script) + err = interpreters.run_string(self.id, script) + self.assertIs(err, None) return None except: raise # re-raise @@ -979,17 +979,18 @@ def _assert_run_failed(self, exctype, msg, script): exctype_name = exctype.__name__ # Run the script. - exc = self.run_script(script, fails=True) + excinfo = self.run_script(script, fails=True) # Check the wrapper exception. + self.assertEqual(excinfo.type.__name__, exctype_name) if msg is None: - self.assertEqual(str(exc).split(':')[0], + self.assertEqual(excinfo.formatted.split(':')[0], exctype_name) else: - self.assertEqual(str(exc), + self.assertEqual(excinfo.formatted, '{}: {}'.format(exctype_name, msg)) - return exc + return excinfo def assert_run_failed(self, exctype, script): self._assert_run_failed(exctype, None, script) diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index aa465c70dfbcd0..1ecac4f37fe1c1 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -1968,10 +1968,12 @@ def test_disallowed_reimport(self): print(_testsinglephase) ''') interpid = _interpreters.create() - with self.assertRaises(_interpreters.RunFailedError): - _interpreters.run_string(interpid, script) - with self.assertRaises(_interpreters.RunFailedError): - _interpreters.run_string(interpid, script) + + excsnap = _interpreters.run_string(interpid, script) + self.assertIsNot(excsnap, None) + + excsnap = _interpreters.run_string(interpid, script) + self.assertIsNot(excsnap, None) class TestSinglePhaseSnapshot(ModuleSnapshot): diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py index 5da72a21f586ee..914176559806f4 100644 --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -655,25 +655,19 @@ def test_magic_number(self): @unittest.skipIf(_interpreters is None, 'subinterpreters required') class IncompatibleExtensionModuleRestrictionsTests(unittest.TestCase): - ERROR = re.compile("^ImportError: module (.*) does not support loading in subinterpreters") - def run_with_own_gil(self, script): interpid = _interpreters.create(isolated=True) - try: - _interpreters.run_string(interpid, script) - except _interpreters.RunFailedError as exc: - if m := self.ERROR.match(str(exc)): - modname, = m.groups() - raise ImportError(modname) + excsnap = _interpreters.run_string(interpid, script) + if excsnap is not None: + if excsnap.type.__name__ == 'ImportError': + raise ImportError(excsnap.msg) def run_with_shared_gil(self, script): interpid = _interpreters.create(isolated=False) - try: - _interpreters.run_string(interpid, script) - except _interpreters.RunFailedError as exc: - if m := self.ERROR.match(str(exc)): - modname, = m.groups() - raise ImportError(modname) + excsnap = _interpreters.run_string(interpid, script) + if excsnap is not None: + if excsnap.type.__name__ == 'ImportError': + raise ImportError(excsnap.msg) @unittest.skipIf(_testsinglephase is None, "test requires _testsinglephase module") def test_single_phase_init_module(self): diff --git a/Lib/test/test_interpreters.py b/Lib/test/test_interpreters.py index 7c030bcf0321cd..5663706c0ccfb7 100644 --- a/Lib/test/test_interpreters.py +++ b/Lib/test/test_interpreters.py @@ -478,6 +478,11 @@ def test_success(self): self.assertEqual(out, 'it worked!') + def test_failure(self): + interp = interpreters.create() + with self.assertRaises(interpreters.RunFailedError): + interp.run('raise Exception') + def test_in_thread(self): interp = interpreters.create() script, file = _captured_script('print("it worked!", end="")') diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 001fa887847cbd..02c2abed27ddfa 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -28,31 +28,11 @@ _get_current_interp(void) return PyInterpreterState_Get(); } -static PyObject * -add_new_exception(PyObject *mod, const char *name, PyObject *base) -{ - assert(!PyObject_HasAttrStringWithError(mod, name)); - PyObject *exctype = PyErr_NewException(name, base, NULL); - if (exctype == NULL) { - return NULL; - } - int res = PyModule_AddType(mod, (PyTypeObject *)exctype); - if (res < 0) { - Py_DECREF(exctype); - return NULL; - } - return exctype; -} - -#define ADD_NEW_EXCEPTION(MOD, NAME, BASE) \ - add_new_exception(MOD, MODULE_NAME "." Py_STRINGIFY(NAME), BASE) - /* module state *************************************************************/ typedef struct { - /* exceptions */ - PyObject *RunFailedError; + int _notused; } module_state; static inline module_state * @@ -67,18 +47,12 @@ get_module_state(PyObject *mod) static int traverse_module_state(module_state *state, visitproc visit, void *arg) { - /* exceptions */ - Py_VISIT(state->RunFailedError); - return 0; } static int clear_module_state(module_state *state) { - /* exceptions */ - Py_CLEAR(state->RunFailedError); - return 0; } @@ -177,30 +151,6 @@ get_code_str(PyObject *arg, Py_ssize_t *len_p, PyObject **bytes_p, int *flags_p) /* interpreter-specific code ************************************************/ -static int -exceptions_init(PyObject *mod) -{ - module_state *state = get_module_state(mod); - if (state == NULL) { - return -1; - } - -#define ADD(NAME, BASE) \ - do { \ - assert(state->NAME == NULL); \ - state->NAME = ADD_NEW_EXCEPTION(mod, NAME, BASE); \ - if (state->NAME == NULL) { \ - return -1; \ - } \ - } while (0) - - // An uncaught exception came out of interp_run_string(). - ADD(RunFailedError, PyExc_RuntimeError); -#undef ADD - - return 0; -} - static int _run_script(PyObject *ns, const char *codestr, Py_ssize_t codestrlen, int flags) { @@ -229,7 +179,7 @@ static int _run_in_interpreter(PyInterpreterState *interp, const char *codestr, Py_ssize_t codestrlen, PyObject *shareables, int flags, - PyObject *excwrapper) + PyObject **p_excinfo) { assert(!PyErr_Occurred()); _PyXI_session session = {0}; @@ -237,7 +187,10 @@ _run_in_interpreter(PyInterpreterState *interp, // Prep and switch interpreters. if (_PyXI_Enter(&session, interp, shareables) < 0) { assert(!PyErr_Occurred()); - _PyXI_ApplyExceptionInfo(session.exc, excwrapper); + PyObject *excinfo = _PyXI_ApplyError(session.error); + if (excinfo != NULL) { + *p_excinfo = excinfo; + } assert(PyErr_Occurred()); return -1; } @@ -251,7 +204,10 @@ _run_in_interpreter(PyInterpreterState *interp, // Propagate any exception out to the caller. assert(!PyErr_Occurred()); if (res < 0) { - _PyXI_ApplyCapturedException(&session, excwrapper); + PyObject *excinfo = _PyXI_ApplyCapturedException(&session); + if (excinfo != NULL) { + *p_excinfo = excinfo; + } } else { assert(!_PyXI_HasCapturedException(&session)); @@ -521,7 +477,8 @@ convert_code_arg(PyObject *arg, const char *fname, const char *displayname, static int _interp_exec(PyObject *self, - PyObject *id_arg, PyObject *code_arg, PyObject *shared_arg) + PyObject *id_arg, PyObject *code_arg, PyObject *shared_arg, + PyObject **p_excinfo) { // Look up the interpreter. PyInterpreterState *interp = PyInterpreterID_LookUp(id_arg); @@ -540,10 +497,8 @@ _interp_exec(PyObject *self, } // Run the code in the interpreter. - module_state *state = get_module_state(self); - assert(state != NULL); int res = _run_in_interpreter(interp, codestr, codestrlen, - shared_arg, flags, state->RunFailedError); + shared_arg, flags, p_excinfo); Py_XDECREF(bytes_obj); if (res < 0) { return -1; @@ -577,10 +532,12 @@ interp_exec(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } - int res = _interp_exec(self, id, code, shared); + PyObject *excinfo = NULL; + int res = _interp_exec(self, id, code, shared, &excinfo); Py_DECREF(code); if (res < 0) { - return NULL; + assert((excinfo == NULL) != (PyErr_Occurred() == NULL)); + return excinfo; } Py_RETURN_NONE; } @@ -620,10 +577,12 @@ interp_run_string(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } - int res = _interp_exec(self, id, script, shared); + PyObject *excinfo = NULL; + int res = _interp_exec(self, id, script, shared, &excinfo); Py_DECREF(script); if (res < 0) { - return NULL; + assert((excinfo == NULL) != (PyErr_Occurred() == NULL)); + return excinfo; } Py_RETURN_NONE; } @@ -654,10 +613,12 @@ interp_run_func(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } - int res = _interp_exec(self, id, (PyObject *)code, shared); + PyObject *excinfo = NULL; + int res = _interp_exec(self, id, (PyObject *)code, shared, &excinfo); Py_DECREF(code); if (res < 0) { - return NULL; + assert((excinfo == NULL) != (PyErr_Occurred() == NULL)); + return excinfo; } Py_RETURN_NONE; } @@ -759,11 +720,6 @@ The 'interpreters' module provides a more convenient interface."); static int module_exec(PyObject *mod) { - /* Add exception types */ - if (exceptions_init(mod) != 0) { - goto error; - } - // PyInterpreterID if (PyModule_AddType(mod, &PyInterpreterID_Type) < 0) { goto error; diff --git a/Python/crossinterp.c b/Python/crossinterp.c index a908f9ae340ee9..21b96ef05ed799 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -5,8 +5,10 @@ #include "pycore_ceval.h" // _Py_simple_func #include "pycore_crossinterp.h" // struct _xid #include "pycore_initconfig.h" // _PyStatus_OK() +#include "pycore_namespace.h" //_PyNamespace_New() #include "pycore_pyerrors.h" // _PyErr_Clear() #include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_typeobject.h" // _PyType_GetModuleName() #include "pycore_weakref.h" // _PyWeakref_GET_REF() @@ -564,6 +566,8 @@ _lookup_getdata_from_registry(PyInterpreterState *interp, PyObject *obj) /* cross-interpreter data for builtin types */ +// bytes + struct _shared_bytes_data { char *bytes; Py_ssize_t len; @@ -595,6 +599,8 @@ _bytes_shared(PyThreadState *tstate, PyObject *obj, return 0; } +// str + struct _shared_str_data { int kind; const void *buffer; @@ -626,6 +632,8 @@ _str_shared(PyThreadState *tstate, PyObject *obj, return 0; } +// int + static PyObject * _new_long_object(_PyCrossInterpreterData *data) { @@ -653,6 +661,8 @@ _long_shared(PyThreadState *tstate, PyObject *obj, return 0; } +// float + static PyObject * _new_float_object(_PyCrossInterpreterData *data) { @@ -676,6 +686,8 @@ _float_shared(PyThreadState *tstate, PyObject *obj, return 0; } +// None + static PyObject * _new_none_object(_PyCrossInterpreterData *data) { @@ -693,6 +705,8 @@ _none_shared(PyThreadState *tstate, PyObject *obj, return 0; } +// bool + static PyObject * _new_bool_object(_PyCrossInterpreterData *data) { @@ -713,6 +727,8 @@ _bool_shared(PyThreadState *tstate, PyObject *obj, return 0; } +// tuple + struct _shared_tuple_data { Py_ssize_t len; _PyCrossInterpreterData **data; @@ -806,6 +822,8 @@ _tuple_shared(PyThreadState *tstate, PyObject *obj, return -1; } +// registration + static void _register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry) { @@ -898,17 +916,6 @@ _xidregistry_fini(struct _xidregistry *registry) /* convenience utilities */ /*************************/ -static const char * -_copy_raw_string(const char *str) -{ - char *copied = PyMem_RawMalloc(strlen(str)+1); - if (copied == NULL) { - return NULL; - } - strcpy(copied, str); - return copied; -} - static const char * _copy_string_obj_raw(PyObject *strobj) { @@ -944,115 +951,309 @@ _release_xid_data(_PyCrossInterpreterData *data, int rawfree) } +/***********************/ /* exception snapshots */ +/***********************/ static int -_exc_type_name_as_utf8(PyObject *exc, const char **p_typename) +_excinfo_init_type(struct _excinfo_type *info, PyObject *exc) { - // XXX Use PyObject_GetAttrString(Py_TYPE(exc), '__name__')? - PyObject *nameobj = PyUnicode_FromString(Py_TYPE(exc)->tp_name); - if (nameobj == NULL) { - assert(PyErr_Occurred()); - *p_typename = "unable to format exception type name"; - return -1; + /* Note that this copies directly rather than into an intermediate + struct and does not clear on error. If we need that then we + should have a separate function to wrap this one + and do all that there. */ + PyObject *strobj = NULL; + + PyTypeObject *type = Py_TYPE(exc); + if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { + assert(_Py_IsImmortal((PyObject *)type)); + info->builtin = type; } - const char *name = PyUnicode_AsUTF8(nameobj); - if (name == NULL) { - assert(PyErr_Occurred()); - Py_DECREF(nameobj); - *p_typename = "unable to encode exception type name"; + else { + // Only builtin types are preserved. + info->builtin = NULL; + } + + // __name__ + strobj = PyType_GetName(type); + if (strobj == NULL) { return -1; } - name = _copy_raw_string(name); - Py_DECREF(nameobj); - if (name == NULL) { - *p_typename = "out of memory copying exception type name"; + info->name = _copy_string_obj_raw(strobj); + Py_DECREF(strobj); + if (info->name == NULL) { return -1; } - *p_typename = name; - return 0; -} -static int -_exc_msg_as_utf8(PyObject *exc, const char **p_msg) -{ - PyObject *msgobj = PyObject_Str(exc); - if (msgobj == NULL) { - assert(PyErr_Occurred()); - *p_msg = "unable to format exception message"; + // __qualname__ + strobj = PyType_GetQualName(type); + if (strobj == NULL) { return -1; } - const char *msg = PyUnicode_AsUTF8(msgobj); - if (msg == NULL) { - assert(PyErr_Occurred()); - Py_DECREF(msgobj); - *p_msg = "unable to encode exception message"; + info->qualname = _copy_string_obj_raw(strobj); + Py_DECREF(strobj); + if (info->name == NULL) { return -1; } - msg = _copy_raw_string(msg); - Py_DECREF(msgobj); - if (msg == NULL) { - assert(PyErr_ExceptionMatches(PyExc_MemoryError)); - *p_msg = "out of memory copying exception message"; + + // __module__ + strobj = _PyType_GetModuleName(type); + if (strobj == NULL) { + return -1; + } + info->module = _copy_string_obj_raw(strobj); + Py_DECREF(strobj); + if (info->name == NULL) { return -1; } - *p_msg = msg; + return 0; } static void -_Py_excinfo_Clear(_Py_excinfo *info) +_excinfo_clear_type(struct _excinfo_type *info) { - if (info->type != NULL) { - PyMem_RawFree((void *)info->type); + if (info->builtin != NULL) { + assert(info->builtin->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN); + assert(_Py_IsImmortal((PyObject *)info->builtin)); + } + if (info->name != NULL) { + PyMem_RawFree((void *)info->name); + } + if (info->qualname != NULL) { + PyMem_RawFree((void *)info->qualname); + } + if (info->module != NULL) { + PyMem_RawFree((void *)info->module); + } + *info = (struct _excinfo_type){NULL}; +} + +static void +_excinfo_normalize_type(struct _excinfo_type *info, + const char **p_module, const char **p_qualname) +{ + if (info->name == NULL) { + assert(info->builtin == NULL); + assert(info->qualname == NULL); + assert(info->module == NULL); + // This is inspired by TracebackException.format_exception_only(). + *p_module = NULL; + *p_qualname = NULL; + return; + } + + const char *module = info->module; + const char *qualname = info->qualname; + if (qualname == NULL) { + qualname = info->name; } + assert(module != NULL); + if (strcmp(module, "builtins") == 0) { + module = NULL; + } + else if (strcmp(module, "__main__") == 0) { + module = NULL; + } + *p_qualname = qualname; + *p_module = module; +} + +static void +_PyXI_excinfo_Clear(_PyXI_excinfo *info) +{ + _excinfo_clear_type(&info->type); if (info->msg != NULL) { PyMem_RawFree((void *)info->msg); } - *info = (_Py_excinfo){ NULL }; + *info = (_PyXI_excinfo){{NULL}}; +} + +static PyObject * +_PyXI_excinfo_format(_PyXI_excinfo *info) +{ + const char *module, *qualname; + _excinfo_normalize_type(&info->type, &module, &qualname); + if (qualname != NULL) { + if (module != NULL) { + if (info->msg != NULL) { + return PyUnicode_FromFormat("%s.%s: %s", + module, qualname, info->msg); + } + else { + return PyUnicode_FromFormat("%s.%s", module, qualname); + } + } + else { + if (info->msg != NULL) { + return PyUnicode_FromFormat("%s: %s", qualname, info->msg); + } + else { + return PyUnicode_FromString(qualname); + } + } + } + else if (info->msg != NULL) { + return PyUnicode_FromString(info->msg); + } + else { + Py_RETURN_NONE; + } } static const char * -_Py_excinfo_InitFromException(_Py_excinfo *info, PyObject *exc) +_PyXI_excinfo_InitFromException(_PyXI_excinfo *info, PyObject *exc) { assert(exc != NULL); - // Extract the exception type name. - const char *typename = NULL; - if (_exc_type_name_as_utf8(exc, &typename) < 0) { - assert(typename != NULL); - return typename; + if (PyErr_GivenExceptionMatches(exc, PyExc_MemoryError)) { + _PyXI_excinfo_Clear(info); + return NULL; + } + const char *failure = NULL; + + if (_excinfo_init_type(&info->type, exc) < 0) { + failure = "error while initializing exception type snapshot"; + goto error; } // Extract the exception message. - const char *msg = NULL; - if (_exc_msg_as_utf8(exc, &msg) < 0) { - assert(msg != NULL); - return msg; + PyObject *msgobj = PyObject_Str(exc); + if (msgobj == NULL) { + failure = "error while formatting exception"; + goto error; + } + info->msg = _copy_string_obj_raw(msgobj); + Py_DECREF(msgobj); + if (info->msg == NULL) { + failure = "error while copying exception message"; + goto error; } - info->type = typename; - info->msg = msg; return NULL; + +error: + assert(failure != NULL); + _PyXI_excinfo_Clear(info); + return failure; } static void -_Py_excinfo_Apply(_Py_excinfo *info, PyObject *exctype) +_PyXI_excinfo_Apply(_PyXI_excinfo *info, PyObject *exctype) +{ + PyObject *formatted = _PyXI_excinfo_format(info); + PyErr_SetObject(exctype, formatted); + Py_DECREF(formatted); +} + +static PyObject * +_PyXI_excinfo_TypeAsObject(_PyXI_excinfo *info) { - if (info->type != NULL) { - if (info->msg != NULL) { - PyErr_Format(exctype, "%s: %s", info->type, info->msg); + PyObject *ns = _PyNamespace_New(NULL); + if (ns == NULL) { + return NULL; + } + int empty = 1; + + if (info->type.name != NULL) { + PyObject *name = PyUnicode_FromString(info->type.name); + if (name == NULL) { + goto error; } - else { - PyErr_SetString(exctype, info->type); + int res = PyObject_SetAttrString(ns, "__name__", name); + Py_DECREF(name); + if (res < 0) { + goto error; } + empty = 0; } - else if (info->msg != NULL) { - PyErr_SetString(exctype, info->msg); + + if (info->type.qualname != NULL) { + PyObject *qualname = PyUnicode_FromString(info->type.qualname); + if (qualname == NULL) { + goto error; + } + int res = PyObject_SetAttrString(ns, "__qualname__", qualname); + Py_DECREF(qualname); + if (res < 0) { + goto error; + } + empty = 0; } - else { - PyErr_SetNone(exctype); + + if (info->type.module != NULL) { + PyObject *module = PyUnicode_FromString(info->type.module); + if (module == NULL) { + goto error; + } + int res = PyObject_SetAttrString(ns, "__module__", module); + Py_DECREF(module); + if (res < 0) { + goto error; + } + empty = 0; + } + + if (empty) { + Py_CLEAR(ns); + } + + return ns; + +error: + Py_DECREF(ns); + return NULL; +} + +static PyObject * +_PyXI_excinfo_AsObject(_PyXI_excinfo *info) +{ + PyObject *ns = _PyNamespace_New(NULL); + if (ns == NULL) { + return NULL; + } + int res; + + PyObject *type = _PyXI_excinfo_TypeAsObject(info); + if (type == NULL) { + if (PyErr_Occurred()) { + goto error; + } + type = Py_NewRef(Py_None); + } + res = PyObject_SetAttrString(ns, "type", type); + Py_DECREF(type); + if (res < 0) { + goto error; + } + + PyObject *msg = info->msg != NULL + ? PyUnicode_FromString(info->msg) + : Py_NewRef(Py_None); + if (msg == NULL) { + goto error; + } + res = PyObject_SetAttrString(ns, "msg", msg); + Py_DECREF(msg); + if (res < 0) { + goto error; } + + PyObject *formatted = _PyXI_excinfo_format(info); + if (formatted == NULL) { + goto error; + } + res = PyObject_SetAttrString(ns, "formatted", formatted); + Py_DECREF(formatted); + if (res < 0) { + goto error; + } + + return ns; + +error: + Py_DECREF(ns); + return NULL; } @@ -1111,72 +1312,69 @@ _PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp) /* shared exceptions */ static const char * -_PyXI_InitExceptionInfo(_PyXI_exception_info *info, - PyObject *excobj, _PyXI_errcode code) +_PyXI_InitError(_PyXI_error *error, PyObject *excobj, _PyXI_errcode code) { - if (info->interp == NULL) { - info->interp = PyInterpreterState_Get(); + if (error->interp == NULL) { + error->interp = PyInterpreterState_Get(); } const char *failure = NULL; if (code == _PyXI_ERR_UNCAUGHT_EXCEPTION) { // There is an unhandled exception we need to propagate. - failure = _Py_excinfo_InitFromException(&info->uncaught, excobj); + failure = _PyXI_excinfo_InitFromException(&error->uncaught, excobj); if (failure != NULL) { - // We failed to initialize info->uncaught. + // We failed to initialize error->uncaught. // XXX Print the excobj/traceback? Emit a warning? // XXX Print the current exception/traceback? if (PyErr_ExceptionMatches(PyExc_MemoryError)) { - info->code = _PyXI_ERR_NO_MEMORY; + error->code = _PyXI_ERR_NO_MEMORY; } else { - info->code = _PyXI_ERR_OTHER; + error->code = _PyXI_ERR_OTHER; } PyErr_Clear(); } else { - info->code = code; + error->code = code; } - assert(info->code != _PyXI_ERR_NO_ERROR); + assert(error->code != _PyXI_ERR_NO_ERROR); } else { // There is an error code we need to propagate. assert(excobj == NULL); assert(code != _PyXI_ERR_NO_ERROR); - info->code = code; - _Py_excinfo_Clear(&info->uncaught); + error->code = code; + _PyXI_excinfo_Clear(&error->uncaught); } return failure; } -void -_PyXI_ApplyExceptionInfo(_PyXI_exception_info *info, PyObject *exctype) +PyObject * +_PyXI_ApplyError(_PyXI_error *error) { - if (exctype == NULL) { - exctype = PyExc_RuntimeError; - } - if (info->code == _PyXI_ERR_UNCAUGHT_EXCEPTION) { + if (error->code == _PyXI_ERR_UNCAUGHT_EXCEPTION) { // Raise an exception that proxies the propagated exception. - _Py_excinfo_Apply(&info->uncaught, exctype); + return _PyXI_excinfo_AsObject(&error->uncaught); } - else if (info->code == _PyXI_ERR_NOT_SHAREABLE) { + else if (error->code == _PyXI_ERR_NOT_SHAREABLE) { // Propagate the exception directly. - _set_xid_lookup_failure(info->interp, NULL, info->uncaught.msg); + _set_xid_lookup_failure(error->interp, NULL, error->uncaught.msg); } else { // Raise an exception corresponding to the code. - assert(info->code != _PyXI_ERR_NO_ERROR); - (void)_PyXI_ApplyErrorCode(info->code, info->interp); - if (info->uncaught.type != NULL || info->uncaught.msg != NULL) { + assert(error->code != _PyXI_ERR_NO_ERROR); + (void)_PyXI_ApplyErrorCode(error->code, error->interp); + if (error->uncaught.type.name != NULL || error->uncaught.msg != NULL) { // __context__ will be set to a proxy of the propagated exception. PyObject *exc = PyErr_GetRaisedException(); - _Py_excinfo_Apply(&info->uncaught, exctype); + _PyXI_excinfo_Apply(&error->uncaught, PyExc_RuntimeError); PyObject *exc2 = PyErr_GetRaisedException(); PyException_SetContext(exc, exc2); PyErr_SetRaisedException(exc); } } assert(PyErr_Occurred()); + return NULL; } /* shared namespaces */ @@ -1603,7 +1801,7 @@ _PyXI_NamespaceFromDict(PyObject *nsobj, _PyXI_session *session) error: assert(PyErr_Occurred() - || (session != NULL && session->exc_override != NULL)); + || (session != NULL && session->error_override != NULL)); _sharedns_free(ns); return NULL; } @@ -1637,9 +1835,9 @@ _enter_session(_PyXI_session *session, PyInterpreterState *interp) assert(!session->running); assert(session->main_ns == NULL); // Set elsewhere and cleared in _capture_current_exception(). - assert(session->exc_override == NULL); + assert(session->error_override == NULL); // Set elsewhere and cleared in _PyXI_ApplyCapturedException(). - assert(session->exc == NULL); + assert(session->error == NULL); // Switch to interpreter. PyThreadState *tstate = PyThreadState_Get(); @@ -1708,23 +1906,23 @@ _propagate_not_shareable_error(_PyXI_session *session) PyInterpreterState *interp = _PyInterpreterState_GET(); if (PyErr_ExceptionMatches(_get_not_shareable_error_type(interp))) { // We want to propagate the exception directly. - session->_exc_override = _PyXI_ERR_NOT_SHAREABLE; - session->exc_override = &session->_exc_override; + session->_error_override = _PyXI_ERR_NOT_SHAREABLE; + session->error_override = &session->_error_override; } } static void _capture_current_exception(_PyXI_session *session) { - assert(session->exc == NULL); + assert(session->error == NULL); if (!PyErr_Occurred()) { - assert(session->exc_override == NULL); + assert(session->error_override == NULL); return; } // Handle the exception override. - _PyXI_errcode *override = session->exc_override; - session->exc_override = NULL; + _PyXI_errcode *override = session->error_override; + session->error_override = NULL; _PyXI_errcode errcode = override != NULL ? *override : _PyXI_ERR_UNCAUGHT_EXCEPTION; @@ -1747,19 +1945,18 @@ _capture_current_exception(_PyXI_session *session) } // Capture the exception. - _PyXI_exception_info *exc = &session->_exc; - *exc = (_PyXI_exception_info){ + _PyXI_error *err = &session->_error; + *err = (_PyXI_error){ .interp = session->init_tstate->interp, }; const char *failure; if (excval == NULL) { - failure = _PyXI_InitExceptionInfo(exc, NULL, errcode); + failure = _PyXI_InitError(err, NULL, errcode); } else { - failure = _PyXI_InitExceptionInfo(exc, excval, - _PyXI_ERR_UNCAUGHT_EXCEPTION); + failure = _PyXI_InitError(err, excval, _PyXI_ERR_UNCAUGHT_EXCEPTION); if (failure == NULL && override != NULL) { - exc->code = errcode; + err->code = errcode; } } @@ -1769,7 +1966,7 @@ _capture_current_exception(_PyXI_session *session) fprintf(stderr, "RunFailedError: script raised an uncaught exception (%s)", failure); - exc = NULL; + err = NULL; } // a temporary hack (famous last words) @@ -1786,23 +1983,24 @@ _capture_current_exception(_PyXI_session *session) // Finished! assert(!PyErr_Occurred()); - session->exc = exc; + session->error = err; } -void -_PyXI_ApplyCapturedException(_PyXI_session *session, PyObject *excwrapper) +PyObject * +_PyXI_ApplyCapturedException(_PyXI_session *session) { assert(!PyErr_Occurred()); - assert(session->exc != NULL); - _PyXI_ApplyExceptionInfo(session->exc, excwrapper); - assert(PyErr_Occurred()); - session->exc = NULL; + assert(session->error != NULL); + PyObject *res = _PyXI_ApplyError(session->error); + assert((res == NULL) != (PyErr_Occurred() == NULL)); + session->error = NULL; + return res; } int _PyXI_HasCapturedException(_PyXI_session *session) { - return session->exc != NULL; + return session->error != NULL; } int @@ -1814,7 +2012,7 @@ _PyXI_Enter(_PyXI_session *session, if (nsupdates != NULL) { sharedns = _PyXI_NamespaceFromDict(nsupdates, NULL); if (sharedns == NULL && PyErr_Occurred()) { - assert(session->exc == NULL); + assert(session->error == NULL); return -1; } } @@ -1864,7 +2062,7 @@ _PyXI_Enter(_PyXI_session *session, assert(PyErr_Occurred()); // We want to propagate all exceptions here directly (best effort). assert(errcode != _PyXI_ERR_UNCAUGHT_EXCEPTION); - session->exc_override = &errcode; + session->error_override = &errcode; _capture_current_exception(session); _exit_session(session); if (sharedns != NULL) { From 89ddea4886942b0c27a778a0ad3f0d5ac5f518f0 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Thu, 23 Nov 2023 14:34:27 +0000 Subject: [PATCH 043/228] gh-112137: change dis output to show no-lineno as -- instead of None (#112335) --- Lib/dis.py | 3 +- Lib/test/test_dis.py | 30 +++++++++---------- ...-11-23-12-37-22.gh-issue-112137.kM46Q6.rst | 1 + 3 files changed, 18 insertions(+), 16 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-23-12-37-22.gh-issue-112137.kM46Q6.rst diff --git a/Lib/dis.py b/Lib/dis.py index c05b8e0dd8617a..c8313ac15a05c1 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -478,7 +478,8 @@ def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=0): if self.starts_line: lineno_fmt = "%%%dd" if self.line_number is not None else "%%%ds" lineno_fmt = lineno_fmt % lineno_width - fields.append(lineno_fmt % self.line_number) + lineno = self.line_number if self.line_number is not None else '--' + fields.append(lineno_fmt % lineno) else: fields.append(' ' * lineno_width) # Column: Label diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 0e7c59c5797f6c..805cd4e4c30965 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -412,7 +412,7 @@ def wrap_func_w_kwargs(): %4d L2: LOAD_FAST_CHECK 1 (tb) RETURN_VALUE -None L3: PUSH_EXC_INFO + -- L3: PUSH_EXC_INFO %4d LOAD_GLOBAL 0 (Exception) CHECK_EXC_MATCH @@ -430,14 +430,14 @@ def wrap_func_w_kwargs(): %4d LOAD_FAST 1 (tb) RETURN_VALUE -None L6: LOAD_CONST 0 (None) + -- L6: LOAD_CONST 0 (None) STORE_FAST 0 (e) DELETE_FAST 0 (e) RERAISE 1 %4d L7: RERAISE 0 -None L8: COPY 3 + -- L8: COPY 3 POP_EXCEPT RERAISE 1 ExceptionTable: @@ -518,7 +518,7 @@ def _with(c): STORE_FAST 2 (y) RETURN_CONST 0 (None) -None L6: COPY 3 + -- L6: COPY 3 POP_EXCEPT RERAISE 1 ExceptionTable: @@ -576,11 +576,11 @@ async def _asyncwith(c): %4d L12: CLEANUP_THROW -None L13: JUMP_BACKWARD 26 (to L5) + -- L13: JUMP_BACKWARD 26 (to L5) %4d L14: CLEANUP_THROW -None L15: JUMP_BACKWARD 11 (to L11) + -- L15: JUMP_BACKWARD 11 (to L11) %4d L16: PUSH_EXC_INFO WITH_EXCEPT_START @@ -604,7 +604,7 @@ async def _asyncwith(c): STORE_FAST 2 (y) RETURN_CONST 0 (None) -None L24: COPY 3 + -- L24: COPY 3 POP_EXCEPT RERAISE 1 L25: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR) @@ -659,7 +659,7 @@ def _tryfinallyconst(b): POP_TOP RETURN_VALUE -None L3: PUSH_EXC_INFO + -- L3: PUSH_EXC_INFO %4d LOAD_FAST 1 (b) PUSH_NULL @@ -667,7 +667,7 @@ def _tryfinallyconst(b): POP_TOP RERAISE 0 -None L4: COPY 3 + -- L4: COPY 3 POP_EXCEPT RERAISE 1 ExceptionTable: @@ -693,7 +693,7 @@ def _tryfinallyconst(b): POP_TOP RETURN_CONST 1 (1) -None L1: PUSH_EXC_INFO + -- L1: PUSH_EXC_INFO %4d LOAD_FAST 0 (b) PUSH_NULL @@ -701,7 +701,7 @@ def _tryfinallyconst(b): POP_TOP RERAISE 0 -None L2: COPY 3 + -- L2: COPY 3 POP_EXCEPT RERAISE 1 ExceptionTable: @@ -730,7 +730,7 @@ def foo(x): return foo dis_nested_0 = """\ -None MAKE_CELL 0 (y) + -- MAKE_CELL 0 (y) %4d RESUME 0 @@ -752,7 +752,7 @@ def foo(x): dis_nested_1 = """%s Disassembly of : -None COPY_FREE_VARS 1 + -- COPY_FREE_VARS 1 MAKE_CELL 0 (x) %4d RESUME 0 @@ -779,7 +779,7 @@ def foo(x): dis_nested_2 = """%s Disassembly of at 0x..., file "%s", line %d>: -None COPY_FREE_VARS 1 + -- COPY_FREE_VARS 1 %4d RETURN_GENERATOR POP_TOP @@ -797,7 +797,7 @@ def foo(x): L3: END_FOR RETURN_CONST 0 (None) -None L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR) + -- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR) RERAISE 1 ExceptionTable: L1 to L4 -> L4 [0] lasti diff --git a/Misc/NEWS.d/next/Library/2023-11-23-12-37-22.gh-issue-112137.kM46Q6.rst b/Misc/NEWS.d/next/Library/2023-11-23-12-37-22.gh-issue-112137.kM46Q6.rst new file mode 100644 index 00000000000000..1b2e41ae96ff09 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-23-12-37-22.gh-issue-112137.kM46Q6.rst @@ -0,0 +1 @@ +Change :mod:`dis` output to display no-lineno as "--" instead of "None". From dc0adb44d8d4a33121deaad398f24b5d8ae36d19 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Thu, 23 Nov 2023 10:31:03 -0800 Subject: [PATCH 044/228] Add extra tests for `random.binomialvariate` (gh-112325) --- Lib/test/test_random.py | 4 ++++ .../Library/2023-11-22-23-08-47.gh-issue-81620.mfZ2Wf.rst | 1 + 2 files changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-11-22-23-08-47.gh-issue-81620.mfZ2Wf.rst diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index 50bea7be6d54c7..b1e4ef4197d130 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -1081,6 +1081,7 @@ def test_binomialvariate(self): B(n=1, p=-0.5) # Negative p with self.assertRaises(ValueError): B(n=1, p=1.5) # p > 1.0 + self.assertEqual(B(0, 0.5), 0) # n == 0 self.assertEqual(B(10, 0.0), 0) # p == 0.0 self.assertEqual(B(10, 1.0), 10) # p == 1.0 self.assertTrue(B(1, 0.3) in {0, 1}) # n == 1 fast path @@ -1088,6 +1089,9 @@ def test_binomialvariate(self): self.assertTrue(B(1, 0.0) in {0}) # n == 1 fast path self.assertTrue(B(1, 1.0) in {1}) # n == 1 fast path + # BG method very small p + self.assertEqual(B(5, 1e-18), 0) + # BG method p <= 0.5 and n*p=1.25 self.assertTrue(B(5, 0.25) in set(range(6))) diff --git a/Misc/NEWS.d/next/Library/2023-11-22-23-08-47.gh-issue-81620.mfZ2Wf.rst b/Misc/NEWS.d/next/Library/2023-11-22-23-08-47.gh-issue-81620.mfZ2Wf.rst new file mode 100644 index 00000000000000..ff35806e4d5ed6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-22-23-08-47.gh-issue-81620.mfZ2Wf.rst @@ -0,0 +1 @@ +Add extra tests for :func:`random.binomialvariate` From d9fc15222e96942e30ea8b0561dec5c82ecb4663 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 23 Nov 2023 21:16:00 +0000 Subject: [PATCH 045/228] Remove bogus annotations from the descriptor howto guide (#112349) --- Doc/howto/descriptor.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index 90d67d84d0ef08..69cf066190bc2f 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -521,11 +521,11 @@ everyday Python programs. Descriptor protocol ------------------- -``descr.__get__(self, obj, type=None) -> value`` +``descr.__get__(self, obj, type=None)`` -``descr.__set__(self, obj, value) -> None`` +``descr.__set__(self, obj, value)`` -``descr.__delete__(self, obj) -> None`` +``descr.__delete__(self, obj)`` That is all there is to it. Define any of these methods and an object is considered a descriptor and can override default behavior upon being looked up From e9d1360c9a1072aa23950b321491dc542c3a19b8 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Fri, 24 Nov 2023 10:46:08 +0100 Subject: [PATCH 046/228] gh-112345: `typing.Protocol`: Let failed subclasscheck show non-method members (#112344) Co-authored-by: Alex Waygood --- Lib/test/test_typing.py | 16 ++++++++++++++++ Lib/typing.py | 7 ++++++- ...023-11-23-17-25-27.gh-issue-112345.FFApHx.rst | 3 +++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 2b5f34b4b92e0c..31d7fda2f978da 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4091,6 +4091,22 @@ def method(self) -> None: ... self.assertIsInstance(Foo(), ProtocolWithMixedMembers) self.assertNotIsInstance(42, ProtocolWithMixedMembers) + def test_protocol_issubclass_error_message(self): + class Vec2D(Protocol): + x: float + y: float + + def square_norm(self) -> float: + return self.x ** 2 + self.y ** 2 + + self.assertEqual(Vec2D.__protocol_attrs__, {'x', 'y', 'square_norm'}) + expected_error_message = ( + "Protocols with non-method members don't support issubclass()." + " Non-method members: 'x', 'y'." + ) + with self.assertRaisesRegex(TypeError, re.escape(expected_error_message)): + issubclass(int, Vec2D) + class GenericTests(BaseTestCase): diff --git a/Lib/typing.py b/Lib/typing.py index a96c7083eb785e..872aca82c4e779 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1828,8 +1828,13 @@ def __subclasscheck__(cls, other): not cls.__callable_proto_members_only__ and cls.__dict__.get("__subclasshook__") is _proto_hook ): + non_method_attrs = sorted( + attr for attr in cls.__protocol_attrs__ + if not callable(getattr(cls, attr, None)) + ) raise TypeError( - "Protocols with non-method members don't support issubclass()" + "Protocols with non-method members don't support issubclass()." + f" Non-method members: {str(non_method_attrs)[1:-1]}." ) if not getattr(cls, '_is_runtime_protocol', False): raise TypeError( diff --git a/Misc/NEWS.d/next/Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst b/Misc/NEWS.d/next/Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst new file mode 100644 index 00000000000000..b2b9894e6bef3a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst @@ -0,0 +1,3 @@ +Improve error message when trying to call :func:`issubclass` against a +:class:`typing.Protocol` that has non-method members. +Patch by Randolf Scholz. From 4ec849bba8286b2ca979f557f633582174a8c4d8 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Fri, 24 Nov 2023 15:58:39 +0000 Subject: [PATCH 047/228] gh-112213: Add missing declaration of target_critical_section (gh-112374) Co-authored-by: Alex Waygood --- Tools/clinic/clinic.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index b1dfcfea92a0d0..c0830864175adf 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -5135,9 +5135,11 @@ class DSLParser: indent: IndentStack kind: FunctionKind coexist: bool + forced_text_signature: str | None parameter_continuation: str preserve_output: bool critical_section: bool + target_critical_section: list[str] from_version_re = re.compile(r'([*/]) +\[from +(.+)\]') def __init__(self, clinic: Clinic) -> None: @@ -5169,11 +5171,11 @@ def reset(self) -> None: self.indent = IndentStack() self.kind = CALLABLE self.coexist = False - self.forced_text_signature: str | None = None + self.forced_text_signature = None self.parameter_continuation = '' self.preserve_output = False self.critical_section = False - self.target_critical_section: list[str] = [] + self.target_critical_section = [] def directive_version(self, required: str) -> None: global version From fafae08cc7caa25f2bd6b29106b50ef76c3e296f Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Fri, 24 Nov 2023 17:59:41 +0000 Subject: [PATCH 048/228] gh-59254: mention in open() doc that line buffering is for writing (#112318) --- Doc/library/functions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index a72f779f69714a..b2dd32f925ef4d 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1221,7 +1221,7 @@ are always available. They are listed here in alphabetical order. *buffering* is an optional integer used to set the buffering policy. Pass 0 to switch buffering off (only allowed in binary mode), 1 to select line - buffering (only usable in text mode), and an integer > 1 to indicate the size + buffering (only usable when writing in text mode), and an integer > 1 to indicate the size in bytes of a fixed-size chunk buffer. Note that specifying a buffer size this way applies for binary buffered I/O, but ``TextIOWrapper`` (i.e., files opened with ``mode='r+'``) would have another buffering. To disable buffering in From 9eb3b35dd7d72ff73005abf20266a618215b9ae0 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Fri, 24 Nov 2023 18:13:25 +0000 Subject: [PATCH 049/228] gh-112355: fix calculation of jump target of ENTER_EXECUTOR in dis (#112377) --- Lib/dis.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Lib/dis.py b/Lib/dis.py index c8313ac15a05c1..e08e9a94057689 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -30,6 +30,7 @@ SET_FUNCTION_ATTRIBUTE = opmap['SET_FUNCTION_ATTRIBUTE'] FUNCTION_ATTR_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure') +ENTER_EXECUTOR = opmap['ENTER_EXECUTOR'] LOAD_CONST = opmap['LOAD_CONST'] RETURN_CONST = opmap['RETURN_CONST'] LOAD_GLOBAL = opmap['LOAD_GLOBAL'] @@ -373,6 +374,8 @@ def _get_argval_argrepr(op, arg, offset, co_consts, names, varname_from_oparg, argval = offset + 2 + signed_arg*2 caches = _get_cache_size(_all_opname[deop]) argval += 2 * caches + if deop == ENTER_EXECUTOR: + argval += 2 argrepr = f"to L{labels_map[argval]}" elif deop in (LOAD_FAST_LOAD_FAST, STORE_FAST_LOAD_FAST, STORE_FAST_STORE_FAST): arg1 = arg >> 4 @@ -605,7 +608,9 @@ def _parse_exception_table(code): return entries def _is_backward_jump(op): - return 'JUMP_BACKWARD' in opname[op] + return opname[op] in ('JUMP_BACKWARD', + 'JUMP_BACKWARD_NO_INTERRUPT', + 'ENTER_EXECUTOR') def _get_instructions_bytes(code, varname_from_oparg=None, names=None, co_consts=None, From d525d01e2794e7e736527eaa7ee309ca1252f5bd Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 25 Nov 2023 08:17:31 +0200 Subject: [PATCH 050/228] gh-101100: Define `test.regrtest` module to fix references (#112381) Define test.regrtest module to fix references --- Doc/library/test.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/library/test.rst b/Doc/library/test.rst index 7a8d38685b984c..6cbdc39b3c024d 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -159,6 +159,9 @@ guidelines to be followed: Running tests using the command-line interface ---------------------------------------------- +.. module:: test.regrtest + :synopsis: Drives the regression test suite. + The :mod:`test` package can be run as a script to drive Python's regression test suite, thanks to the :option:`-m` option: :program:`python -m test`. Under the hood, it uses :mod:`test.regrtest`; the call :program:`python -m From 6b961b8ceaba372b78d03feaceb4837bf7236694 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 25 Nov 2023 08:18:02 +0200 Subject: [PATCH 051/228] gh-101100: Define `_tkinter` module to fix references (#112382) Define _tkinter module to fix references --- Doc/library/tkinter.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst index 5fab26db67633c..a8c67b02a23e4d 100644 --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -232,6 +232,9 @@ The modules that provide Tk support include: Additional modules: +.. module:: _tkinter + :synopsis: A binary module that contains the low-level interface to Tcl/Tk. + :mod:`_tkinter` A binary module that contains the low-level interface to Tcl/Tk. It is automatically imported by the main :mod:`tkinter` module, From 19a1fc1b3df30f64450d157dc3a5d40c992e347f Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sat, 25 Nov 2023 17:19:38 +0000 Subject: [PATCH 052/228] GH-112361: Speed up pathlib by removing some temporary objects. (#112362) Construct only one new list object (using `list.copy()`) when creating a new path object with a modified tail. This slightly speeds up `with_name()` and `with_suffix()` --- Lib/pathlib.py | 32 +++++++------------ Lib/test/test_pathlib.py | 2 -- ...-11-24-09-27-01.gh-issue-112361.kYtnHW.rst | 2 ++ 3 files changed, 14 insertions(+), 22 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-24-09-27-01.gh-issue-112361.kYtnHW.rst diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 9bce5320ef68e9..32ccf818b157c5 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -400,13 +400,14 @@ def stem(self): def with_name(self, name): """Return a new path with the file name changed.""" - if not self.name: - raise ValueError("%r has an empty name" % (self,)) m = self.pathmod if not name or m.sep in name or (m.altsep and m.altsep in name) or name == '.': - raise ValueError("Invalid name %r" % (name)) - return self._from_parsed_parts(self.drive, self.root, - self._tail[:-1] + [name]) + raise ValueError(f"Invalid name {name!r}") + tail = self._tail.copy() + if not tail: + raise ValueError(f"{self!r} has an empty name") + tail[-1] = name + return self._from_parsed_parts(self.drive, self.root, tail) def with_stem(self, stem): """Return a new path with the stem changed.""" @@ -417,21 +418,12 @@ def with_suffix(self, suffix): has no suffix, add given suffix. If the given suffix is an empty string, remove the suffix from the path. """ - m = self.pathmod - if m.sep in suffix or m.altsep and m.altsep in suffix: - raise ValueError("Invalid suffix %r" % (suffix,)) - if suffix and not suffix.startswith('.') or suffix == '.': - raise ValueError("Invalid suffix %r" % (suffix)) - name = self.name - if not name: - raise ValueError("%r has an empty name" % (self,)) - old_suffix = self.suffix - if not old_suffix: - name = name + suffix + if not suffix: + return self.with_name(self.stem) + elif suffix.startswith('.') and len(suffix) > 1: + return self.with_name(self.stem + suffix) else: - name = name[:-len(old_suffix)] + suffix - return self._from_parsed_parts(self.drive, self.root, - self._tail[:-1] + [name]) + raise ValueError(f"Invalid suffix {suffix!r}") def relative_to(self, other, /, *_deprecated, walk_up=False): """Return the relative path to another path identified by the passed @@ -1029,7 +1021,7 @@ def _glob(self, pattern, case_sensitive, follow_symlinks): elif not path_pattern._tail: raise ValueError("Unacceptable pattern: {!r}".format(pattern)) - pattern_parts = list(path_pattern._tail) + pattern_parts = path_pattern._tail.copy() if pattern[-1] in (self.pathmod.sep, self.pathmod.altsep): # GH-65238: pathlib doesn't preserve trailing slash. Add it back. pattern_parts.append('') diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index e1121a9d76c040..427e082f3e16cb 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -575,8 +575,6 @@ def test_with_suffix_common(self): self.assertRaises(ValueError, P('a/b').with_suffix, '.c/.d') self.assertRaises(ValueError, P('a/b').with_suffix, './.d') self.assertRaises(ValueError, P('a/b').with_suffix, '.d/.') - self.assertRaises(ValueError, P('a/b').with_suffix, - (self.pathmod.sep, 'd')) def test_relative_to_common(self): P = self.cls diff --git a/Misc/NEWS.d/next/Library/2023-11-24-09-27-01.gh-issue-112361.kYtnHW.rst b/Misc/NEWS.d/next/Library/2023-11-24-09-27-01.gh-issue-112361.kYtnHW.rst new file mode 100644 index 00000000000000..5a83f93f9fbec8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-24-09-27-01.gh-issue-112361.kYtnHW.rst @@ -0,0 +1,2 @@ +Speed up a small handful of :mod:`pathlib` methods by removing some +temporary objects. From fbb9027a037ff1bfaf3f596df033ca45743ee980 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Sat, 25 Nov 2023 17:23:43 +0000 Subject: [PATCH 053/228] gh-94722: fix DocTest.__eq__ for case of no line number on one side (#112385) --- Lib/doctest.py | 6 ++++-- Lib/test/test_doctest.py | 17 +++++++++++++++++ ...023-11-24-21-00-24.gh-issue-94722.GMIQIn.rst | 2 ++ 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-24-21-00-24.gh-issue-94722.GMIQIn.rst diff --git a/Lib/doctest.py b/Lib/doctest.py index 2f14aa08334895..d109b6c9e37343 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -591,9 +591,11 @@ def __hash__(self): def __lt__(self, other): if not isinstance(other, DocTest): return NotImplemented - return ((self.name, self.filename, self.lineno, id(self)) + self_lno = self.lineno if self.lineno is not None else -1 + other_lno = other.lineno if other.lineno is not None else -1 + return ((self.name, self.filename, self_lno, id(self)) < - (other.name, other.filename, other.lineno, id(other))) + (other.name, other.filename, other_lno, id(other))) ###################################################################### ## 3. DocTestParser diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index cb4e2157bb228b..772dbd1d021305 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -413,6 +413,23 @@ def test_DocTest(): r""" False >>> test != other_test True + >>> test < other_test + False + >>> other_test < test + True + +Test comparison with lineno None on one side + + >>> no_lineno = parser.get_doctest(docstring, globs, 'some_test', + ... 'some_test', None) + >>> test.lineno is None + False + >>> no_lineno.lineno is None + True + >>> test < no_lineno + False + >>> no_lineno < test + True Compare `DocTestCase`: diff --git a/Misc/NEWS.d/next/Library/2023-11-24-21-00-24.gh-issue-94722.GMIQIn.rst b/Misc/NEWS.d/next/Library/2023-11-24-21-00-24.gh-issue-94722.GMIQIn.rst new file mode 100644 index 00000000000000..41bd57f46ed82a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-24-21-00-24.gh-issue-94722.GMIQIn.rst @@ -0,0 +1,2 @@ +Fix bug where comparison between instances of :class:`~doctest.DocTest` fails if +one of them has ``None`` as its lineno. From bbb4367b55ead1a0322d86b568c6c4607f539d3e Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sat, 25 Nov 2023 17:41:05 +0000 Subject: [PATCH 054/228] GH-77621: Delay some imports from pathlib (#112244) Import `contextlib`, `glob` and `re` only as required. Co-authored-by: Alex Waygood --- Lib/pathlib.py | 14 +++++++++----- .../2023-11-21-02-58-14.gh-issue-77621.MYv5XS.rst | 2 ++ 2 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-21-02-58-14.gh-issue-77621.MYv5XS.rst diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 32ccf818b157c5..0e01099d490a7e 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -5,14 +5,11 @@ operating systems. """ -import contextlib import functools -import glob import io import ntpath import os import posixpath -import re import sys import warnings from _collections_abc import Sequence @@ -75,17 +72,23 @@ def _is_case_sensitive(pathmod): # Globbing helpers # +re = glob = None + @functools.lru_cache(maxsize=256) def _compile_pattern(pat, sep, case_sensitive): """Compile given glob pattern to a re.Pattern object (observing case sensitivity).""" + global re, glob + if re is None: + import re, glob + flags = re.NOFLAG if case_sensitive else re.IGNORECASE regex = glob.translate(pat, recursive=True, include_hidden=True, seps=sep) # The string representation of an empty path is a single dot ('.'). Empty # paths shouldn't match wildcards, so we consume it with an atomic group. regex = r'(\.\Z)?+' + regex - return re.compile(regex, flags).match + return re.compile(regex, flags=flags).match def _select_children(parent_paths, dir_only, follow_symlinks, match): @@ -981,7 +984,8 @@ def iterdir(self): def _scandir(self): # Emulate os.scandir(), which returns an object that can be used as a # context manager. This method is called by walk() and glob(). - return contextlib.nullcontext(self.iterdir()) + from contextlib import nullcontext + return nullcontext(self.iterdir()) def _make_child_relpath(self, name): path_str = str(self) diff --git a/Misc/NEWS.d/next/Library/2023-11-21-02-58-14.gh-issue-77621.MYv5XS.rst b/Misc/NEWS.d/next/Library/2023-11-21-02-58-14.gh-issue-77621.MYv5XS.rst new file mode 100644 index 00000000000000..f3e6efc389afca --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-21-02-58-14.gh-issue-77621.MYv5XS.rst @@ -0,0 +1,2 @@ +Slightly improve the import time of the :mod:`pathlib` module by deferring +some imports. Patch by Barney Gale. From 0303a9fa79ab51def97346ae0b9e535de1e33503 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 25 Nov 2023 20:08:32 +0200 Subject: [PATCH 055/228] gh-101100 : Fix Sphinx warnings in `library/doctest.rst` (#112399) * Fix Sphinx warnings in library/doctest.rst * Don't link to self, and wrap a line Co-authored-by: Alex Waygood * Link to load_tests protocol * Link to option flags * Wrap line Co-authored-by: Alex Waygood --------- Co-authored-by: Alex Waygood --- Doc/library/doctest.rst | 47 +++++++++++++++++++++++----------------- Doc/library/unittest.rst | 2 ++ Doc/tools/.nitignore | 1 - 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/Doc/library/doctest.rst b/Doc/library/doctest.rst index ad013944ce3ca3..fa1b850c531346 100644 --- a/Doc/library/doctest.rst +++ b/Doc/library/doctest.rst @@ -143,13 +143,13 @@ Simple Usage: Checking Examples in Docstrings --------------------------------------------- The simplest way to start using doctest (but not necessarily the way you'll -continue to do it) is to end each module :mod:`M` with:: +continue to do it) is to end each module :mod:`!M` with:: if __name__ == "__main__": import doctest doctest.testmod() -:mod:`doctest` then examines docstrings in module :mod:`M`. +:mod:`!doctest` then examines docstrings in module :mod:`!M`. Running the module as a script causes the examples in the docstrings to get executed and verified:: @@ -403,10 +403,10 @@ What's the Execution Context? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ By default, each time :mod:`doctest` finds a docstring to test, it uses a -*shallow copy* of :mod:`M`'s globals, so that running tests doesn't change the -module's real globals, and so that one test in :mod:`M` can't leave behind +*shallow copy* of :mod:`!M`'s globals, so that running tests doesn't change the +module's real globals, and so that one test in :mod:`!M` can't leave behind crumbs that accidentally allow another test to work. This means examples can -freely use any names defined at top-level in :mod:`M`, and names defined earlier +freely use any names defined at top-level in :mod:`!M`, and names defined earlier in the docstring being run. Examples cannot see names defined in other docstrings. @@ -958,7 +958,8 @@ and :ref:`doctest-simple-testfile`. Optional argument *exclude_empty* defaults to false. If true, objects for which no doctests are found are excluded from consideration. The default is a backward - compatibility hack, so that code still using :meth:`doctest.master.summarize` in + compatibility hack, so that code still using + :meth:`doctest.master.summarize ` in conjunction with :func:`testmod` continues to get output for objects with no tests. The *exclude_empty* argument to the newer :class:`DocTestFinder` constructor defaults to true. @@ -997,7 +998,7 @@ As your collection of doctest'ed modules grows, you'll want a way to run all their doctests systematically. :mod:`doctest` provides two functions that can be used to create :mod:`unittest` test suites from modules and text files containing doctests. To integrate with :mod:`unittest` test discovery, include -a :func:`load_tests` function in your test module:: +a :ref:`load_tests ` function in your test module:: import unittest import doctest @@ -1111,19 +1112,24 @@ 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`. +.. exception:: failureException + + When doctests which have been converted to unit tests by :func:`DocFileSuite` + or :func:`DocTestSuite` fail, this exception is raised showing the name of + the file containing the test and a (sometimes approximate) line number. Under the covers, :func:`DocTestSuite` creates a :class:`unittest.TestSuite` out -of :class:`doctest.DocTestCase` instances, and :class:`DocTestCase` is a -subclass of :class:`unittest.TestCase`. :class:`DocTestCase` isn't documented +of :class:`!doctest.DocTestCase` instances, and :class:`!DocTestCase` is a +subclass of :class:`unittest.TestCase`. :class:`!DocTestCase` isn't documented here (it's an internal detail), but studying its code can answer questions about the exact details of :mod:`unittest` integration. Similarly, :func:`DocFileSuite` creates a :class:`unittest.TestSuite` out of -:class:`doctest.DocFileCase` instances, and :class:`DocFileCase` is a subclass -of :class:`DocTestCase`. +:class:`!doctest.DocFileCase` instances, and :class:`!DocFileCase` is a subclass +of :class:`!DocTestCase`. So both ways of creating a :class:`unittest.TestSuite` run instances of -:class:`DocTestCase`. This is important for a subtle reason: when you run +:class:`!DocTestCase`. This is important for a subtle reason: when you run :mod:`doctest` functions yourself, you can control the :mod:`doctest` options in use directly, by passing option flags to :mod:`doctest` functions. However, if you're writing a :mod:`unittest` framework, :mod:`unittest` ultimately controls @@ -1144,14 +1150,14 @@ reporting flags specific to :mod:`unittest` support, via this function: section :ref:`doctest-options`. Only "reporting flags" can be used. This is a module-global setting, and affects all future doctests run by module - :mod:`unittest`: the :meth:`runTest` method of :class:`DocTestCase` looks at - the option flags specified for the test case when the :class:`DocTestCase` + :mod:`unittest`: the :meth:`!runTest` method of :class:`!DocTestCase` looks at + the option flags specified for the test case when the :class:`!DocTestCase` instance was constructed. If no reporting flags were specified (which is the - typical and expected case), :mod:`doctest`'s :mod:`unittest` reporting flags are + typical and expected case), :mod:`!doctest`'s :mod:`unittest` reporting flags are :ref:`bitwise ORed ` into the option flags, and the option flags so augmented are passed to the :class:`DocTestRunner` instance created to run the doctest. If any reporting flags were specified when the - :class:`DocTestCase` instance was constructed, :mod:`doctest`'s + :class:`!DocTestCase` instance was constructed, :mod:`!doctest`'s :mod:`unittest` reporting flags are ignored. The value of the :mod:`unittest` reporting flags in effect before the function @@ -1321,7 +1327,8 @@ Example Objects A dictionary mapping from option flags to ``True`` or ``False``, which is used to override default options for this example. Any option flags not contained in this dictionary are left at their default value (as specified by the - :class:`DocTestRunner`'s :attr:`optionflags`). By default, no options are set. + :class:`DocTestRunner`'s :ref:`optionflags `). + By default, no options are set. .. _doctest-doctestfinder: @@ -1560,7 +1567,7 @@ DocTestRunner objects The output of each example is checked using the :class:`DocTestRunner`'s output checker, and the results are formatted by the - :meth:`DocTestRunner.report_\*` methods. + :meth:`!DocTestRunner.report_\*` methods. .. method:: summarize(verbose=None) @@ -1735,12 +1742,12 @@ code under the debugger: module) of the object with the doctests of interest. The result is a string, containing the object's docstring converted to a Python script, as described for :func:`script_from_examples` above. For example, if module :file:`a.py` - contains a top-level function :func:`f`, then :: + contains a top-level function :func:`!f`, then :: import a, doctest print(doctest.testsource(a, "a.f")) - prints a script version of function :func:`f`'s docstring, with doctests + prints a script version of function :func:`!f`'s docstring, with doctests converted to code, and the rest placed in comments. diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index c90c554591e748..02b72cb9f6b8aa 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -2326,6 +2326,8 @@ Loading and running tests test names. +.. _load_tests-protocol: + load_tests Protocol ################### diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 105e00db8e368a..fbf4c1adfb14b8 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -48,7 +48,6 @@ Doc/library/csv.rst Doc/library/datetime.rst Doc/library/dbm.rst Doc/library/decimal.rst -Doc/library/doctest.rst Doc/library/email.charset.rst Doc/library/email.compat32-message.rst Doc/library/email.errors.rst From f93a4ef7a9e8d6f831c62707c0d39e0be306c4e6 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 25 Nov 2023 16:18:00 -0600 Subject: [PATCH 056/228] Descriptor HowTo: Sync the error-messages with the C code. Add tests. (gh-112403) --- Doc/howto/descriptor.rst | 43 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index 69cf066190bc2f..f732aaea729d40 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -1013,17 +1013,23 @@ here is a pure Python equivalent: if obj is None: return self if self.fget is None: - raise AttributeError(f"property '{self._name}' has no getter") + raise AttributeError( + f'property {self._name!r} of {type(obj).__name__!r} object has no getter' + ) return self.fget(obj) def __set__(self, obj, value): if self.fset is None: - raise AttributeError(f"property '{self._name}' has no setter") + raise AttributeError( + f'property {self._name!r} of {type(obj).__name__!r} object has no setter' + ) self.fset(obj, value) def __delete__(self, obj): if self.fdel is None: - raise AttributeError(f"property '{self._name}' has no deleter") + raise AttributeError( + f'property {self._name!r} of {type(obj).__name__!r} object has no deleter' + ) self.fdel(obj) def getter(self, fget): @@ -1054,6 +1060,11 @@ here is a pure Python equivalent: def delx(self): del self.__x x = Property(getx, setx, delx, "I'm the 'x' property.") + no_getter = Property(None, setx, delx, "I'm the 'x' property.") + no_setter = Property(getx, None, delx, "I'm the 'x' property.") + no_deleter = Property(getx, setx, None, "I'm the 'x' property.") + no_doc = Property(getx, setx, delx, None) + # Now do it again but use the decorator style @@ -1092,6 +1103,32 @@ here is a pure Python equivalent: >>> hasattr(ccc, 'x') False + >>> cc = CC() + >>> cc.x = 33 + >>> try: + ... cc.no_getter + ... except AttributeError as e: + ... e.args[0] + ... + "property 'no_getter' of 'CC' object has no getter" + + >>> try: + ... cc.no_setter = 33 + ... except AttributeError as e: + ... e.args[0] + ... + "property 'no_setter' of 'CC' object has no setter" + + >>> try: + ... del cc.no_deleter + ... except AttributeError as e: + ... e.args[0] + ... + "property 'no_deleter' of 'CC' object has no deleter" + + >>> CC.no_doc.__doc__ is None + True + The :func:`property` builtin helps whenever a user interface has granted attribute access and then subsequent changes require the intervention of a method. From 97f8f28b3e50196f6713faceccc2e15039117470 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 25 Nov 2023 16:20:53 -0600 Subject: [PATCH 057/228] gh-112331: Fix reference manual description of attribute lookup mechanics (gh-112375) --- Doc/reference/expressions.rst | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 14c2afa15ad7fb..3f6d5bfafee9d1 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -823,12 +823,18 @@ An attribute reference is a primary followed by a period and a name: The primary must evaluate to an object of a type that supports attribute references, which most objects do. This object is then asked to produce the -attribute whose name is the identifier. This production can be customized by -overriding the :meth:`__getattr__` method. If this attribute is not available, -the exception :exc:`AttributeError` is raised. Otherwise, the type and value of -the object produced is determined by the object. Multiple evaluations of the -same attribute reference may yield different objects. - +attribute whose name is the identifier. The type and value produced is +determined by the object. Multiple evaluations of the same attribute +reference may yield different objects. + +This production can be customized by overriding the +:meth:`~object.__getattribute__` method or the :meth:`~object.__getattr__` +method. The :meth:`!__getattribute__` method is called first and either +returns a value or raises :exc:`AttributeError` if the attribute is not +available. + +If an :exc:`AttributeError` is raised and the object has a :meth:`!__getattr__` +method, that method is called as a fallback. .. _subscriptions: From 3faf8e586d36e73faba13d9b61663afed6a24cb4 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 26 Nov 2023 01:40:19 +0200 Subject: [PATCH 058/228] gh-101100: Fix Sphinx reference warnings (GH-112416) * Fix Sphinx warning in library/xml.rst Direct use of the pyexpat module is deprecated, but this is how to check the version for security purposes * Fix Sphinx warning in library/importlib.resources.rst * Use italics for parameters * Link to the exception * Fix Sphinx warning in library/gzip.rst * Document message and header defect base classes to fix Sphinx warning in library/email.headerregistry.rst * Restore feed_eof() doc to fix Sphinx warning in library/asyncio-stream.rst * Fix Sphinx warning in extending/newtypes.rst * Fix Sphinx warning in c-api/set.rst On stdtypes.rst, set and frozenset are documented together and the frozenset has the working refs --- Doc/c-api/set.rst | 2 +- Doc/extending/newtypes.rst | 2 +- Doc/library/asyncio-stream.rst | 4 ++++ Doc/library/email.errors.rst | 9 +++++++++ Doc/library/gzip.rst | 2 +- Doc/library/importlib.resources.rst | 12 ++++++------ Doc/library/xml.rst | 2 +- Doc/tools/.nitignore | 7 ------- 8 files changed, 23 insertions(+), 17 deletions(-) diff --git a/Doc/c-api/set.rst b/Doc/c-api/set.rst index 09c0fb6b9c5f23..cba823aa027bd6 100644 --- a/Doc/c-api/set.rst +++ b/Doc/c-api/set.rst @@ -147,7 +147,7 @@ subtypes but not for instances of :class:`frozenset` or its subtypes. Return ``1`` if found and removed, ``0`` if not found (no action taken), and ``-1`` if an error is encountered. Does not raise :exc:`KeyError` for missing keys. Raise a - :exc:`TypeError` if the *key* is unhashable. Unlike the Python :meth:`~set.discard` + :exc:`TypeError` if the *key* is unhashable. Unlike the Python :meth:`~frozenset.discard` method, this function does not automatically convert unhashable sets into temporary frozensets. Raise :exc:`SystemError` if *set* is not an instance of :class:`set` or its subtype. diff --git a/Doc/extending/newtypes.rst b/Doc/extending/newtypes.rst index 9f166eb8a4c3ff..7a92b3257c6cd3 100644 --- a/Doc/extending/newtypes.rst +++ b/Doc/extending/newtypes.rst @@ -296,7 +296,7 @@ An interesting advantage of using the :c:member:`~PyTypeObject.tp_members` table descriptors that are used at runtime is that any attribute defined this way can have an associated doc string simply by providing the text in the table. An application can use the introspection API to retrieve the descriptor from the -class object, and get the doc string using its :attr:`__doc__` attribute. +class object, and get the doc string using its :attr:`!__doc__` attribute. As with the :c:member:`~PyTypeObject.tp_methods` table, a sentinel entry with a :c:member:`~PyMethodDef.ml_name` value of ``NULL`` is required. diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst index d8186b6ce75c79..0736e783bbc8c8 100644 --- a/Doc/library/asyncio-stream.rst +++ b/Doc/library/asyncio-stream.rst @@ -204,6 +204,10 @@ StreamReader directly; use :func:`open_connection` and :func:`start_server` instead. + .. method:: feed_eof() + + Acknowledge the EOF. + .. coroutinemethod:: read(n=-1) Read up to *n* bytes from the stream. diff --git a/Doc/library/email.errors.rst b/Doc/library/email.errors.rst index 194a98696f437d..56aea6598b8615 100644 --- a/Doc/library/email.errors.rst +++ b/Doc/library/email.errors.rst @@ -58,6 +58,15 @@ The following exception classes are defined in the :mod:`email.errors` module: :class:`~email.mime.nonmultipart.MIMENonMultipart` (e.g. :class:`~email.mime.image.MIMEImage`). +.. exception:: MessageDefect() + + This is the base class for all defects found when parsing email messages. + It is derived from :exc:`ValueError`. + +.. exception:: HeaderDefect() + + This is the base class for all defects found when parsing email headers. + It is derived from :exc:`MessageDefect`. Here is the list of the defects that the :class:`~email.parser.FeedParser` can find while parsing messages. Note that the defects are added to the message diff --git a/Doc/library/gzip.rst b/Doc/library/gzip.rst index f931d0e399c9f2..50cde09fa10a9d 100644 --- a/Doc/library/gzip.rst +++ b/Doc/library/gzip.rst @@ -105,7 +105,7 @@ The module defines the following items: should only be provided in compression mode. If omitted or ``None``, the current time is used. See the :attr:`mtime` attribute for more details. - Calling a :class:`GzipFile` object's :meth:`close` method does not close + Calling a :class:`GzipFile` object's :meth:`!close` method does not close *fileobj*, since you might wish to append more material after the compressed data. This also allows you to pass an :class:`io.BytesIO` object opened for writing as *fileobj*, and retrieve the resulting memory buffer using the diff --git a/Doc/library/importlib.resources.rst b/Doc/library/importlib.resources.rst index 3de97e80311a17..a5adf0b8546dbf 100644 --- a/Doc/library/importlib.resources.rst +++ b/Doc/library/importlib.resources.rst @@ -50,7 +50,7 @@ for example, a package and its resources can be imported from a zip file using ``get_resource_reader(fullname)`` method as specified by :class:`importlib.resources.abc.ResourceReader`. -.. data:: Anchor +.. class:: Anchor Represents an anchor for resources, either a :class:`module object ` or a module name as a string. Defined as @@ -63,7 +63,7 @@ for example, a package and its resources can be imported from a zip file using (think files). A Traversable may contain other containers (think subdirectories). - *anchor* is an optional :data:`Anchor`. If the anchor is a + *anchor* is an optional :class:`Anchor`. If the anchor is a package, resources are resolved from that package. If a module, resources are resolved adjacent to that module (in the same package or the package root). If the anchor is omitted, the caller's module @@ -72,10 +72,10 @@ 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 + *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 DeprecationWarning. Consider passing the anchor positionally or + 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. @@ -96,4 +96,4 @@ for example, a package and its resources can be imported from a zip file using .. versionadded:: 3.9 .. versionchanged:: 3.12 - Added support for ``traversable`` representing a directory. + Added support for *traversable* representing a directory. diff --git a/Doc/library/xml.rst b/Doc/library/xml.rst index 1e49b6568dfc28..909022ea4ba6a4 100644 --- a/Doc/library/xml.rst +++ b/Doc/library/xml.rst @@ -73,7 +73,7 @@ decompression bomb Safe Safe Safe 1. Expat 2.4.1 and newer is not vulnerable to the "billion laughs" and "quadratic blowup" vulnerabilities. Items still listed as vulnerable due to potential reliance on system-provided libraries. Check - :const:`pyexpat.EXPAT_VERSION`. + :const:`!pyexpat.EXPAT_VERSION`. 2. :mod:`xml.etree.ElementTree` doesn't expand external entities and raises a :exc:`~xml.etree.ElementTree.ParseError` when an entity occurs. 3. :mod:`xml.dom.minidom` doesn't expand external entities and simply returns diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index fbf4c1adfb14b8..1e3e367460147a 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -14,14 +14,12 @@ Doc/c-api/memory.rst Doc/c-api/memoryview.rst Doc/c-api/module.rst Doc/c-api/object.rst -Doc/c-api/set.rst Doc/c-api/stable.rst Doc/c-api/structures.rst Doc/c-api/sys.rst Doc/c-api/type.rst Doc/c-api/typeobj.rst Doc/extending/extending.rst -Doc/extending/newtypes.rst Doc/glossary.rst Doc/howto/descriptor.rst Doc/howto/enum.rst @@ -32,7 +30,6 @@ Doc/library/abc.rst Doc/library/ast.rst Doc/library/asyncio-extending.rst Doc/library/asyncio-policy.rst -Doc/library/asyncio-stream.rst Doc/library/asyncio-subprocess.rst Doc/library/asyncio-task.rst Doc/library/bdb.rst @@ -51,7 +48,6 @@ Doc/library/decimal.rst Doc/library/email.charset.rst Doc/library/email.compat32-message.rst Doc/library/email.errors.rst -Doc/library/email.headerregistry.rst Doc/library/email.mime.rst Doc/library/email.parser.rst Doc/library/email.policy.rst @@ -63,12 +59,10 @@ Doc/library/ftplib.rst Doc/library/functions.rst Doc/library/functools.rst Doc/library/gettext.rst -Doc/library/gzip.rst Doc/library/http.client.rst Doc/library/http.cookiejar.rst Doc/library/http.cookies.rst Doc/library/http.server.rst -Doc/library/importlib.resources.rst Doc/library/importlib.rst Doc/library/inspect.rst Doc/library/locale.rst @@ -122,7 +116,6 @@ Doc/library/wsgiref.rst Doc/library/xml.dom.minidom.rst Doc/library/xml.dom.pulldom.rst Doc/library/xml.dom.rst -Doc/library/xml.rst Doc/library/xml.sax.handler.rst Doc/library/xml.sax.reader.rst Doc/library/xml.sax.rst From 9fe60340d7e8dc22b3aec205c557bc69a1b2d18c Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 26 Nov 2023 14:29:52 +0000 Subject: [PATCH 059/228] gh-112358: Fix Python 3.12 regression with subclassing struct.Struct. (#112424) Revert commit c8c0afc7137ab9f22bf59d591084948ca967c97c (PR #94532), which moved `struct.Struct` initialisation from `Struct.__init__` to `Struct.__new__`. This caused issues with code in the wild that subclasses `struct.Struct`. --- Lib/test/test_struct.py | 23 +++----- ...-11-26-13-26-56.gh-issue-112358.smhaeZ.rst | 2 + Modules/_struct.c | 59 +++++++++++-------- Modules/clinic/_struct.c.h | 16 ++--- 4 files changed, 52 insertions(+), 48 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-26-13-26-56.gh-issue-112358.smhaeZ.rst diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index c76649cdcd9cce..15f6ee06ffe19b 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -700,20 +700,6 @@ def test__struct_types_immutable(self): with self.assertRaises(TypeError): cls.x = 1 - @support.cpython_only - def test__struct_Struct__new__initialized(self): - # See https://github.com/python/cpython/issues/78724 - - s = struct.Struct.__new__(struct.Struct, "b") - s.unpack_from(b"abcd") - - @support.cpython_only - def test__struct_Struct_subclassing(self): - class Bob(struct.Struct): - pass - - s = Bob("b") - s.unpack_from(b"abcd") def test_issue35714(self): # Embedded null characters should not be allowed in format strings. @@ -774,6 +760,15 @@ def test_error_propagation(fmt_str): test_error_propagation('N') test_error_propagation('n') + def test_struct_subclass_instantiation(self): + # Regression test for https://github.com/python/cpython/issues/112358 + class MyStruct(struct.Struct): + def __init__(self): + super().__init__('>h') + + my_struct = MyStruct() + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + def test_repr(self): s = struct.Struct('=i2H') self.assertEqual(repr(s), f'Struct({s.format!r})') diff --git a/Misc/NEWS.d/next/Library/2023-11-26-13-26-56.gh-issue-112358.smhaeZ.rst b/Misc/NEWS.d/next/Library/2023-11-26-13-26-56.gh-issue-112358.smhaeZ.rst new file mode 100644 index 00000000000000..e473ded46a1309 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-26-13-26-56.gh-issue-112358.smhaeZ.rst @@ -0,0 +1,2 @@ +Revert change to :class:`struct.Struct` initialization that broke some cases +of subclassing. diff --git a/Modules/_struct.c b/Modules/_struct.c index 0116b03ea95115..24a4cb3b6413f1 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -1553,9 +1553,28 @@ prepare_s(PyStructObject *self) return -1; } +static PyObject * +s_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *self; + + assert(type != NULL); + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + assert(alloc_func != NULL); + + self = alloc_func(type, 0); + if (self != NULL) { + PyStructObject *s = (PyStructObject*)self; + s->s_format = Py_NewRef(Py_None); + s->s_codes = NULL; + s->s_size = -1; + s->s_len = -1; + } + return self; +} + /*[clinic input] -@classmethod -Struct.__new__ +Struct.__init__ format: object @@ -1567,24 +1586,16 @@ the format string. See help(struct) for more on format strings. [clinic start generated code]*/ -static PyObject * -Struct_impl(PyTypeObject *type, PyObject *format) -/*[clinic end generated code: output=49468b044e334308 input=8b91868eb1df0e28]*/ +static int +Struct___init___impl(PyStructObject *self, PyObject *format) +/*[clinic end generated code: output=b8e80862444e92d0 input=192a4575a3dde802]*/ { - allocfunc alloc = PyType_GetSlot(type, Py_tp_alloc); - assert(alloc != NULL); - PyStructObject *self = (PyStructObject *)alloc(type, 0); - - if (self == NULL) { - return NULL; - } + int ret = 0; if (PyUnicode_Check(format)) { format = PyUnicode_AsASCIIString(format); - if (format == NULL) { - Py_DECREF(self); - return NULL; - } + if (format == NULL) + return -1; } else { Py_INCREF(format); @@ -1592,24 +1603,19 @@ Struct_impl(PyTypeObject *type, PyObject *format) if (!PyBytes_Check(format)) { Py_DECREF(format); - Py_DECREF(self); PyErr_Format(PyExc_TypeError, "Struct() argument 1 must be a str or bytes object, " "not %.200s", _PyType_Name(Py_TYPE(format))); - return NULL; + return -1; } - self->s_format = format; + Py_SETREF(self->s_format, format); - if (prepare_s(self) < 0) { - Py_DECREF(self); - return NULL; - } - return (PyObject *)self; + ret = prepare_s(self); + return ret; } - static int s_clear(PyStructObject *s) { @@ -2219,8 +2225,9 @@ static PyType_Slot PyStructType_slots[] = { {Py_tp_methods, s_methods}, {Py_tp_members, s_members}, {Py_tp_getset, s_getsetlist}, - {Py_tp_new, Struct}, + {Py_tp_init, Struct___init__}, {Py_tp_alloc, PyType_GenericAlloc}, + {Py_tp_new, s_new}, {Py_tp_free, PyObject_GC_Del}, {0, 0}, }; diff --git a/Modules/clinic/_struct.c.h b/Modules/clinic/_struct.c.h index e5118fbdb3b9d3..1a07532bdd75ad 100644 --- a/Modules/clinic/_struct.c.h +++ b/Modules/clinic/_struct.c.h @@ -9,7 +9,7 @@ preserve #include "pycore_abstract.h" // _PyNumber_Index() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() -PyDoc_STRVAR(Struct__doc__, +PyDoc_STRVAR(Struct___init____doc__, "Struct(format)\n" "--\n" "\n" @@ -20,13 +20,13 @@ PyDoc_STRVAR(Struct__doc__, "\n" "See help(struct) for more on format strings."); -static PyObject * -Struct_impl(PyTypeObject *type, PyObject *format); +static int +Struct___init___impl(PyStructObject *self, PyObject *format); -static PyObject * -Struct(PyTypeObject *type, PyObject *args, PyObject *kwargs) +static int +Struct___init__(PyObject *self, PyObject *args, PyObject *kwargs) { - PyObject *return_value = NULL; + int return_value = -1; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) #define NUM_KEYWORDS 1 @@ -62,7 +62,7 @@ Struct(PyTypeObject *type, PyObject *args, PyObject *kwargs) goto exit; } format = fastargs[0]; - return_value = Struct_impl(type, format); + return_value = Struct___init___impl((PyStructObject *)self, format); exit: return return_value; @@ -436,4 +436,4 @@ iter_unpack(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } -/*[clinic end generated code: output=6a20e87f9b298b14 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=67bd299e5d72fee0 input=a9049054013a1b77]*/ From 418d585febd280e779274f313f910613ac1a1c30 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sun, 26 Nov 2023 15:56:03 +0000 Subject: [PATCH 060/228] gh-112405: Optimise `pathlib.Path.relative_to` (#112406) --- Lib/pathlib.py | 3 ++- .../Library/2023-11-25-20-29-28.gh-issue-112405.cOtzxC.rst | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-25-20-29-28.gh-issue-112405.cOtzxC.rst diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 0e01099d490a7e..81f75cd47ed087 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -14,6 +14,7 @@ import warnings from _collections_abc import Sequence from errno import ENOENT, ENOTDIR, EBADF, ELOOP, EINVAL +from itertools import chain from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO try: @@ -445,7 +446,7 @@ def relative_to(self, other, /, *_deprecated, walk_up=False): other = self.with_segments(other, *_deprecated) elif not isinstance(other, PurePath): other = self.with_segments(other) - for step, path in enumerate([other] + list(other.parents)): + for step, path in enumerate(chain([other], other.parents)): if path == self or path in self.parents: break elif not walk_up: diff --git a/Misc/NEWS.d/next/Library/2023-11-25-20-29-28.gh-issue-112405.cOtzxC.rst b/Misc/NEWS.d/next/Library/2023-11-25-20-29-28.gh-issue-112405.cOtzxC.rst new file mode 100644 index 00000000000000..f6f1bee2a0c38f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-25-20-29-28.gh-issue-112405.cOtzxC.rst @@ -0,0 +1 @@ +Optimize :meth:`pathlib.PurePath.relative_to`. Patch by Alex Waygood. From fb202af4470d6051a69bb9d2f44d7e8a1c99eb4f Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Sun, 26 Nov 2023 17:13:57 +0000 Subject: [PATCH 061/228] gh-99606: Make code generated for an empty f-string identical to that of a normal empty string (#112407) --- Lib/test/test_fstring.py | 10 ++++++++++ .../2023-11-25-20-36-38.gh-issue-99606.fDY5hK.rst | 2 ++ Python/compile.c | 8 ++++++-- 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-25-20-36-38.gh-issue-99606.fDY5hK.rst diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index da0160d2382cc6..27c7f70cef32e3 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -8,6 +8,7 @@ # Unicode identifiers in tests is allowed by PEP 3131. import ast +import dis import os import re import types @@ -1738,5 +1739,14 @@ def test_syntax_warning_infinite_recursion_in_file(self): self.assertIn(rb'\1', stdout) self.assertEqual(len(stderr.strip().splitlines()), 2) + def test_fstring_without_formatting_bytecode(self): + # f-string without any formatting should emit the same bytecode + # as a normal string. See gh-99606. + def get_code(s): + return [(i.opname, i.oparg) for i in dis.get_instructions(s)] + + for s in ["", "some string"]: + self.assertEqual(get_code(f"'{s}'"), get_code(f"f'{s}'")) + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-25-20-36-38.gh-issue-99606.fDY5hK.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-25-20-36-38.gh-issue-99606.fDY5hK.rst new file mode 100644 index 00000000000000..adc0e3a6bbc89a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-25-20-36-38.gh-issue-99606.fDY5hK.rst @@ -0,0 +1,2 @@ +Make code generated for an empty f-string identical to the code of an empty +normal string. diff --git a/Python/compile.c b/Python/compile.c index 8b1eef79a79eae..8b9e2f02048f11 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -5042,8 +5042,12 @@ compiler_joined_str(struct compiler *c, expr_ty e) } else { VISIT_SEQ(c, expr, e->v.JoinedStr.values); - if (asdl_seq_LEN(e->v.JoinedStr.values) != 1) { - ADDOP_I(c, loc, BUILD_STRING, asdl_seq_LEN(e->v.JoinedStr.values)); + if (value_count > 1) { + ADDOP_I(c, loc, BUILD_STRING, value_count); + } + else if (value_count == 0) { + _Py_DECLARE_STR(empty, ""); + ADDOP_LOAD_CONST_NEW(c, loc, Py_NewRef(&_Py_STR(empty))); } } return SUCCESS; From e954ac7205d7a6e356c1736eb372d2b50dbd9f69 Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Mon, 27 Nov 2023 17:01:44 +1300 Subject: [PATCH 062/228] gh-63284: Add support for TLS-PSK (pre-shared key) to the ssl module (#103181) Add support for TLS-PSK (pre-shared key) to the ssl module. --------- Co-authored-by: Oleg Iarygin Co-authored-by: Gregory P. Smith --- Doc/library/ssl.rst | 88 +++++++ .../pycore_global_objects_fini_generated.h | 2 + Include/internal/pycore_global_strings.h | 2 + .../internal/pycore_runtime_init_generated.h | 2 + .../internal/pycore_unicodeobject_generated.h | 6 + Lib/test/test_ssl.py | 99 ++++++++ Misc/ACKS | 1 + ...3-11-27-12-41-23.gh-issue-63284.q2Qi9q.rst | 1 + Modules/_ssl.c | 224 ++++++++++++++++++ Modules/clinic/_ssl.c.h | 137 ++++++++++- 10 files changed, 561 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-27-12-41-23.gh-issue-63284.q2Qi9q.rst diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 21b38ae62fe02f..206294528e0016 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -2006,6 +2006,94 @@ to speed up repeated connections from the same clients. >>> ssl.create_default_context().verify_mode # doctest: +SKIP +.. method:: SSLContext.set_psk_client_callback(callback) + + Enables TLS-PSK (pre-shared key) authentication on a client-side connection. + + In general, certificate based authentication should be preferred over this method. + + The parameter ``callback`` is a callable object with the signature: + ``def callback(hint: str | None) -> tuple[str | None, bytes]``. + The ``hint`` parameter is an optional identity hint sent by the server. + The return value is a tuple in the form (client-identity, psk). + Client-identity is an optional string which may be used by the server to + select a corresponding PSK for the client. The string must be less than or + equal to ``256`` octets when UTF-8 encoded. PSK is a + :term:`bytes-like object` representing the pre-shared key. Return a zero + length PSK to reject the connection. + + Setting ``callback`` to :const:`None` removes any existing callback. + + .. note:: + When using TLS 1.3: + + - the ``hint`` parameter is always :const:`None`. + - client-identity must be a non-empty string. + + Example usage:: + + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + context.maximum_version = ssl.TLSVersion.TLSv1_2 + context.set_ciphers('PSK') + + # A simple lambda: + psk = bytes.fromhex('c0ffee') + context.set_psk_client_callback(lambda hint: (None, psk)) + + # A table using the hint from the server: + psk_table = { 'ServerId_1': bytes.fromhex('c0ffee'), + 'ServerId_2': bytes.fromhex('facade') + } + def callback(hint): + return 'ClientId_1', psk_table.get(hint, b'') + context.set_psk_client_callback(callback) + + .. versionadded:: 3.13 + +.. method:: SSLContext.set_psk_server_callback(callback, identity_hint=None) + + Enables TLS-PSK (pre-shared key) authentication on a server-side connection. + + In general, certificate based authentication should be preferred over this method. + + The parameter ``callback`` is a callable object with the signature: + ``def callback(identity: str | None) -> bytes``. + The ``identity`` parameter is an optional identity sent by the client which can + be used to select a corresponding PSK. + The return value is a :term:`bytes-like object` representing the pre-shared key. + Return a zero length PSK to reject the connection. + + Setting ``callback`` to :const:`None` removes any existing callback. + + The parameter ``identity_hint`` is an optional identity hint string sent to + the client. The string must be less than or equal to ``256`` octets when + UTF-8 encoded. + + .. note:: + When using TLS 1.3 the ``identity_hint`` parameter is not sent to the client. + + Example usage:: + + context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + context.maximum_version = ssl.TLSVersion.TLSv1_2 + context.set_ciphers('PSK') + + # A simple lambda: + psk = bytes.fromhex('c0ffee') + context.set_psk_server_callback(lambda identity: psk) + + # A table using the identity of the client: + psk_table = { 'ClientId_1': bytes.fromhex('c0ffee'), + 'ClientId_2': bytes.fromhex('facade') + } + def callback(identity): + return psk_table.get(identity, b'') + context.set_psk_server_callback(callback, 'ServerId_1') + + .. versionadded:: 3.13 + .. index:: single: certificates .. index:: single: X509 certificate diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 0808076f44de31..89ec8cbbbcd649 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -826,6 +826,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(call)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(call_exception_handler)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(call_soon)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(callback)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(cancel)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(capath)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(category)); @@ -971,6 +972,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(hook)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(id)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ident)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(identity_hint)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ignore)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(imag)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(importlib)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 8d22a9ba261010..62c3ee3ae2a0bd 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -315,6 +315,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(call) STRUCT_FOR_ID(call_exception_handler) STRUCT_FOR_ID(call_soon) + STRUCT_FOR_ID(callback) STRUCT_FOR_ID(cancel) STRUCT_FOR_ID(capath) STRUCT_FOR_ID(category) @@ -460,6 +461,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(hook) STRUCT_FOR_ID(id) STRUCT_FOR_ID(ident) + STRUCT_FOR_ID(identity_hint) STRUCT_FOR_ID(ignore) STRUCT_FOR_ID(imag) STRUCT_FOR_ID(importlib) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index d41a7478db663f..1defa39f816e78 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -824,6 +824,7 @@ extern "C" { INIT_ID(call), \ INIT_ID(call_exception_handler), \ INIT_ID(call_soon), \ + INIT_ID(callback), \ INIT_ID(cancel), \ INIT_ID(capath), \ INIT_ID(category), \ @@ -969,6 +970,7 @@ extern "C" { INIT_ID(hook), \ INIT_ID(id), \ INIT_ID(ident), \ + INIT_ID(identity_hint), \ INIT_ID(ignore), \ INIT_ID(imag), \ INIT_ID(importlib), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 0c02e902b308e3..be9baa3eebecfc 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -786,6 +786,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(call_soon); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(callback); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(cancel); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); @@ -1221,6 +1224,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(ident); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(identity_hint); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(ignore); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index d8ae7b75e18150..9ade595ef8ae7e 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -4236,6 +4236,105 @@ def test_session_handling(self): self.assertEqual(str(e.exception), 'Session refers to a different SSLContext.') + @requires_tls_version('TLSv1_2') + def test_psk(self): + psk = bytes.fromhex('deadbeef') + + client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + client_context.check_hostname = False + client_context.verify_mode = ssl.CERT_NONE + client_context.maximum_version = ssl.TLSVersion.TLSv1_2 + client_context.set_ciphers('PSK') + client_context.set_psk_client_callback(lambda hint: (None, psk)) + + server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + server_context.maximum_version = ssl.TLSVersion.TLSv1_2 + server_context.set_ciphers('PSK') + server_context.set_psk_server_callback(lambda identity: psk) + + # correct PSK should connect + server = ThreadedEchoServer(context=server_context) + with server: + with client_context.wrap_socket(socket.socket()) as s: + s.connect((HOST, server.port)) + + # incorrect PSK should fail + incorrect_psk = bytes.fromhex('cafebabe') + client_context.set_psk_client_callback(lambda hint: (None, incorrect_psk)) + server = ThreadedEchoServer(context=server_context) + with server: + with client_context.wrap_socket(socket.socket()) as s: + with self.assertRaises(ssl.SSLError): + s.connect((HOST, server.port)) + + # identity_hint and client_identity should be sent to the other side + identity_hint = 'identity-hint' + client_identity = 'client-identity' + + def client_callback(hint): + self.assertEqual(hint, identity_hint) + return client_identity, psk + + def server_callback(identity): + self.assertEqual(identity, client_identity) + return psk + + client_context.set_psk_client_callback(client_callback) + server_context.set_psk_server_callback(server_callback, identity_hint) + server = ThreadedEchoServer(context=server_context) + with server: + with client_context.wrap_socket(socket.socket()) as s: + s.connect((HOST, server.port)) + + # adding client callback to server or vice versa raises an exception + with self.assertRaisesRegex(ssl.SSLError, 'Cannot add PSK server callback'): + client_context.set_psk_server_callback(server_callback, identity_hint) + with self.assertRaisesRegex(ssl.SSLError, 'Cannot add PSK client callback'): + server_context.set_psk_client_callback(client_callback) + + # test with UTF-8 identities + identity_hint = '身份暗示' # Translation: "Identity hint" + client_identity = '客户身份' # Translation: "Customer identity" + + client_context.set_psk_client_callback(client_callback) + server_context.set_psk_server_callback(server_callback, identity_hint) + server = ThreadedEchoServer(context=server_context) + with server: + with client_context.wrap_socket(socket.socket()) as s: + s.connect((HOST, server.port)) + + @requires_tls_version('TLSv1_3') + def test_psk_tls1_3(self): + psk = bytes.fromhex('deadbeef') + identity_hint = 'identity-hint' + client_identity = 'client-identity' + + def client_callback(hint): + # identity_hint is not sent to the client in TLS 1.3 + self.assertIsNone(hint) + return client_identity, psk + + def server_callback(identity): + self.assertEqual(identity, client_identity) + return psk + + client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + client_context.check_hostname = False + client_context.verify_mode = ssl.CERT_NONE + client_context.minimum_version = ssl.TLSVersion.TLSv1_3 + client_context.set_ciphers('PSK') + client_context.set_psk_client_callback(client_callback) + + server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + server_context.minimum_version = ssl.TLSVersion.TLSv1_3 + server_context.set_ciphers('PSK') + server_context.set_psk_server_callback(server_callback, identity_hint) + + server = ThreadedEchoServer(context=server_context) + with server: + with client_context.wrap_socket(socket.socket()) as s: + s.connect((HOST, server.port)) + @unittest.skipUnless(has_tls_version('TLSv1_3'), "Test needs TLS 1.3") class TestPostHandshakeAuth(unittest.TestCase): diff --git a/Misc/ACKS b/Misc/ACKS index 6d3a4e3fdb8fe7..5fe3a177a26292 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1482,6 +1482,7 @@ Ajith Ramachandran Dhushyanth Ramasamy Ashwin Ramaswami Jeff Ramnani +Grant Ramsay Bayard Randel Varpu Rantala Brodie Rao diff --git a/Misc/NEWS.d/next/Library/2023-11-27-12-41-23.gh-issue-63284.q2Qi9q.rst b/Misc/NEWS.d/next/Library/2023-11-27-12-41-23.gh-issue-63284.q2Qi9q.rst new file mode 100644 index 00000000000000..abb57dccd5a91a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-27-12-41-23.gh-issue-63284.q2Qi9q.rst @@ -0,0 +1 @@ +Added support for TLS-PSK (pre-shared key) mode to the :mod:`ssl` module. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 7bc30cb3529d47..707e7ad9543acb 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -301,6 +301,8 @@ typedef struct { BIO *keylog_bio; /* Cached module state, also used in SSLSocket and SSLSession code. */ _sslmodulestate *state; + PyObject *psk_client_callback; + PyObject *psk_server_callback; } PySSLContext; typedef struct { @@ -3123,6 +3125,8 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version) self->alpn_protocols = NULL; self->set_sni_cb = NULL; self->state = get_ssl_state(module); + self->psk_client_callback = NULL; + self->psk_server_callback = NULL; /* Don't check host name by default */ if (proto_version == PY_SSL_VERSION_TLS_CLIENT) { @@ -3235,6 +3239,8 @@ context_clear(PySSLContext *self) Py_CLEAR(self->set_sni_cb); Py_CLEAR(self->msg_cb); Py_CLEAR(self->keylog_filename); + Py_CLEAR(self->psk_client_callback); + Py_CLEAR(self->psk_server_callback); if (self->keylog_bio != NULL) { PySSL_BEGIN_ALLOW_THREADS BIO_free_all(self->keylog_bio); @@ -4662,6 +4668,222 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form) return NULL; } +static unsigned int psk_client_callback(SSL *s, + const char *hint, + char *identity, + unsigned int max_identity_len, + unsigned char *psk, + unsigned int max_psk_len) +{ + PyGILState_STATE gstate = PyGILState_Ensure(); + PyObject *callback = NULL; + + PySSLSocket *ssl = SSL_get_app_data(s); + if (ssl == NULL || ssl->ctx == NULL) { + goto error; + } + callback = ssl->ctx->psk_client_callback; + if (callback == NULL) { + goto error; + } + + PyObject *hint_str = (hint != NULL && hint[0] != '\0') ? + PyUnicode_DecodeUTF8(hint, strlen(hint), "strict") : + Py_NewRef(Py_None); + if (hint_str == NULL) { + /* The remote side has sent an invalid UTF-8 string + * (breaking the standard), drop the connection without + * raising a decode exception. */ + PyErr_Clear(); + goto error; + } + PyObject *result = PyObject_CallFunctionObjArgs(callback, hint_str, NULL); + Py_DECREF(hint_str); + + if (result == NULL) { + goto error; + } + + const char *psk_; + const char *identity_; + Py_ssize_t psk_len_; + Py_ssize_t identity_len_ = 0; + if (!PyArg_ParseTuple(result, "z#y#", &identity_, &identity_len_, &psk_, &psk_len_)) { + Py_DECREF(result); + goto error; + } + + if (identity_len_ + 1 > max_identity_len || psk_len_ > max_psk_len) { + Py_DECREF(result); + goto error; + } + memcpy(psk, psk_, psk_len_); + if (identity_ != NULL) { + memcpy(identity, identity_, identity_len_); + } + identity[identity_len_] = 0; + + Py_DECREF(result); + + PyGILState_Release(gstate); + return (unsigned int)psk_len_; + +error: + if (PyErr_Occurred()) { + PyErr_WriteUnraisable(callback); + } + PyGILState_Release(gstate); + return 0; +} + +/*[clinic input] +_ssl._SSLContext.set_psk_client_callback + callback: object + +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLContext_set_psk_client_callback_impl(PySSLContext *self, + PyObject *callback) +/*[clinic end generated code: output=0aba86f6ed75119e input=7627bae0e5ee7635]*/ +{ + if (self->protocol == PY_SSL_VERSION_TLS_SERVER) { + _setSSLError(get_state_ctx(self), + "Cannot add PSK client callback to a " + "PROTOCOL_TLS_SERVER context", 0, __FILE__, __LINE__); + return NULL; + } + + SSL_psk_client_cb_func ssl_callback; + if (callback == Py_None) { + callback = NULL; + // Delete the existing callback + ssl_callback = NULL; + } else { + if (!PyCallable_Check(callback)) { + PyErr_SetString(PyExc_TypeError, "callback must be callable"); + return NULL; + } + ssl_callback = psk_client_callback; + } + + Py_XDECREF(self->psk_client_callback); + Py_XINCREF(callback); + + self->psk_client_callback = callback; + SSL_CTX_set_psk_client_callback(self->ctx, ssl_callback); + + Py_RETURN_NONE; +} + +static unsigned int psk_server_callback(SSL *s, + const char *identity, + unsigned char *psk, + unsigned int max_psk_len) +{ + PyGILState_STATE gstate = PyGILState_Ensure(); + PyObject *callback = NULL; + + PySSLSocket *ssl = SSL_get_app_data(s); + if (ssl == NULL || ssl->ctx == NULL) { + goto error; + } + callback = ssl->ctx->psk_server_callback; + if (callback == NULL) { + goto error; + } + + PyObject *identity_str = (identity != NULL && identity[0] != '\0') ? + PyUnicode_DecodeUTF8(identity, strlen(identity), "strict") : + Py_NewRef(Py_None); + if (identity_str == NULL) { + /* The remote side has sent an invalid UTF-8 string + * (breaking the standard), drop the connection without + * raising a decode exception. */ + PyErr_Clear(); + goto error; + } + PyObject *result = PyObject_CallFunctionObjArgs(callback, identity_str, NULL); + Py_DECREF(identity_str); + + if (result == NULL) { + goto error; + } + + char *psk_; + Py_ssize_t psk_len_; + if (PyBytes_AsStringAndSize(result, &psk_, &psk_len_) < 0) { + Py_DECREF(result); + goto error; + } + + if (psk_len_ > max_psk_len) { + Py_DECREF(result); + goto error; + } + memcpy(psk, psk_, psk_len_); + + Py_DECREF(result); + + PyGILState_Release(gstate); + return (unsigned int)psk_len_; + +error: + if (PyErr_Occurred()) { + PyErr_WriteUnraisable(callback); + } + PyGILState_Release(gstate); + return 0; +} + +/*[clinic input] +_ssl._SSLContext.set_psk_server_callback + callback: object + identity_hint: str(accept={str, NoneType}) = None + +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLContext_set_psk_server_callback_impl(PySSLContext *self, + PyObject *callback, + const char *identity_hint) +/*[clinic end generated code: output=1f4d6a4e09a92b03 input=65d4b6022aa85ea3]*/ +{ + if (self->protocol == PY_SSL_VERSION_TLS_CLIENT) { + _setSSLError(get_state_ctx(self), + "Cannot add PSK server callback to a " + "PROTOCOL_TLS_CLIENT context", 0, __FILE__, __LINE__); + return NULL; + } + + SSL_psk_server_cb_func ssl_callback; + if (callback == Py_None) { + callback = NULL; + // Delete the existing callback and hint + ssl_callback = NULL; + identity_hint = NULL; + } else { + if (!PyCallable_Check(callback)) { + PyErr_SetString(PyExc_TypeError, "callback must be callable"); + return NULL; + } + ssl_callback = psk_server_callback; + } + + if (SSL_CTX_use_psk_identity_hint(self->ctx, identity_hint) != 1) { + PyErr_SetString(PyExc_ValueError, "failed to set identity hint"); + return NULL; + } + + Py_XDECREF(self->psk_server_callback); + Py_XINCREF(callback); + + self->psk_server_callback = callback; + SSL_CTX_set_psk_server_callback(self->ctx, ssl_callback); + + Py_RETURN_NONE; +} + static PyGetSetDef context_getsetlist[] = { {"check_hostname", (getter) get_check_hostname, @@ -4716,6 +4938,8 @@ static struct PyMethodDef context_methods[] = { _SSL__SSLCONTEXT_CERT_STORE_STATS_METHODDEF _SSL__SSLCONTEXT_GET_CA_CERTS_METHODDEF _SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF + _SSL__SSLCONTEXT_SET_PSK_CLIENT_CALLBACK_METHODDEF + _SSL__SSLCONTEXT_SET_PSK_SERVER_CALLBACK_METHODDEF {NULL, NULL} /* sentinel */ }; diff --git a/Modules/clinic/_ssl.c.h b/Modules/clinic/_ssl.c.h index 88401b0490a1bb..19c0f619b92f45 100644 --- a/Modules/clinic/_ssl.c.h +++ b/Modules/clinic/_ssl.c.h @@ -1014,6 +1014,141 @@ _ssl__SSLContext_get_ca_certs(PySSLContext *self, PyObject *const *args, Py_ssiz return return_value; } +PyDoc_STRVAR(_ssl__SSLContext_set_psk_client_callback__doc__, +"set_psk_client_callback($self, /, callback)\n" +"--\n" +"\n"); + +#define _SSL__SSLCONTEXT_SET_PSK_CLIENT_CALLBACK_METHODDEF \ + {"set_psk_client_callback", _PyCFunction_CAST(_ssl__SSLContext_set_psk_client_callback), METH_FASTCALL|METH_KEYWORDS, _ssl__SSLContext_set_psk_client_callback__doc__}, + +static PyObject * +_ssl__SSLContext_set_psk_client_callback_impl(PySSLContext *self, + PyObject *callback); + +static PyObject * +_ssl__SSLContext_set_psk_client_callback(PySSLContext *self, 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 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(callback), }, + }; + #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[] = {"callback", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "set_psk_client_callback", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *callback; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + callback = args[0]; + return_value = _ssl__SSLContext_set_psk_client_callback_impl(self, callback); + +exit: + return return_value; +} + +PyDoc_STRVAR(_ssl__SSLContext_set_psk_server_callback__doc__, +"set_psk_server_callback($self, /, callback, identity_hint=None)\n" +"--\n" +"\n"); + +#define _SSL__SSLCONTEXT_SET_PSK_SERVER_CALLBACK_METHODDEF \ + {"set_psk_server_callback", _PyCFunction_CAST(_ssl__SSLContext_set_psk_server_callback), METH_FASTCALL|METH_KEYWORDS, _ssl__SSLContext_set_psk_server_callback__doc__}, + +static PyObject * +_ssl__SSLContext_set_psk_server_callback_impl(PySSLContext *self, + PyObject *callback, + const char *identity_hint); + +static PyObject * +_ssl__SSLContext_set_psk_server_callback(PySSLContext *self, 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 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(callback), &_Py_ID(identity_hint), }, + }; + #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[] = {"callback", "identity_hint", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "set_psk_server_callback", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *callback; + const char *identity_hint = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + if (!args) { + goto exit; + } + callback = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[1] == Py_None) { + identity_hint = NULL; + } + else if (PyUnicode_Check(args[1])) { + Py_ssize_t identity_hint_length; + identity_hint = PyUnicode_AsUTF8AndSize(args[1], &identity_hint_length); + if (identity_hint == NULL) { + goto exit; + } + if (strlen(identity_hint) != (size_t)identity_hint_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + goto exit; + } + } + else { + _PyArg_BadArgument("set_psk_server_callback", "argument 'identity_hint'", "str or None", args[1]); + goto exit; + } +skip_optional_pos: + return_value = _ssl__SSLContext_set_psk_server_callback_impl(self, callback, identity_hint); + +exit: + return return_value; +} + static PyObject * _ssl_MemoryBIO_impl(PyTypeObject *type); @@ -1527,4 +1662,4 @@ _ssl_enum_crls(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje #ifndef _SSL_ENUM_CRLS_METHODDEF #define _SSL_ENUM_CRLS_METHODDEF #endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */ -/*[clinic end generated code: output=aa6b0a898b6077fe input=a9049054013a1b77]*/ +/*[clinic end generated code: output=6342ea0062ab16c7 input=a9049054013a1b77]*/ From 0622839cfedacbb48eba27180fd0f0586fe97771 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 27 Nov 2023 08:19:29 +0000 Subject: [PATCH 063/228] gh-112414: Fix `AttributeError` when calling `repr()` on a namespace package imported with a custom loader (#112425) --- Lib/importlib/_bootstrap.py | 10 ++++++++-- Lib/test/test_importlib/import_/test___loader__.py | 4 ++++ Lib/test/test_importlib/test_namespace_pkgs.py | 2 +- .../2023-11-26-13-44-19.gh-issue-112414.kx2E7S.rst | 3 +++ 4 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-26-13-44-19.gh-issue-112414.kx2E7S.rst diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index ec2e56f6ea9ca1..d942045f3de666 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -824,10 +824,16 @@ def _module_repr_from_spec(spec): """Return the repr to use for the module.""" name = '?' if spec.name is None else spec.name if spec.origin is None: - if spec.loader is None: + loader = spec.loader + if loader is None: return f'' + elif ( + _bootstrap_external is not None + and isinstance(loader, _bootstrap_external.NamespaceLoader) + ): + return f'' else: - return f'' + return f'' else: if spec.has_location: return f'' diff --git a/Lib/test/test_importlib/import_/test___loader__.py b/Lib/test/test_importlib/import_/test___loader__.py index 858b37effc64bd..c6996a42534676 100644 --- a/Lib/test/test_importlib/import_/test___loader__.py +++ b/Lib/test/test_importlib/import_/test___loader__.py @@ -23,6 +23,10 @@ def test___loader__(self): with util.uncache('blah'), util.import_state(meta_path=[loader]): module = self.__import__('blah') self.assertEqual(loader, module.__loader__) + expected_repr_pattern = ( + r"\)>" + ) + self.assertRegex(repr(module), expected_repr_pattern) (Frozen_SpecTests, diff --git a/Lib/test/test_importlib/test_namespace_pkgs.py b/Lib/test/test_importlib/test_namespace_pkgs.py index 9b3bef02c66820..072e198795d394 100644 --- a/Lib/test/test_importlib/test_namespace_pkgs.py +++ b/Lib/test/test_importlib/test_namespace_pkgs.py @@ -80,7 +80,7 @@ def test_cant_import_other(self): def test_simple_repr(self): import foo.one - assert repr(foo).startswith(" Date: Mon, 27 Nov 2023 22:27:47 +1300 Subject: [PATCH 064/228] Docs: fix typo in doc for sqlite3.Cursor.execute (#112442) --- Doc/library/sqlite3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index a5b3474f4bd39a..6dbb34a84a4c40 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1502,7 +1502,7 @@ Cursor objects .. method:: execute(sql, parameters=(), /) - Execute SQL a single SQL statement, + Execute a single SQL statement, optionally binding Python values using :ref:`placeholders `. From 7ac49e74c3db35365968cd2cbd395cf063d2050d Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 27 Nov 2023 13:01:26 +0300 Subject: [PATCH 065/228] gh-111147: Fix `test_set_of_sets_reprs` in `test_pprint` (GH-111148) Make it stable and not depending on implementation details. --- Lib/test/test_pprint.py | 292 +++++++++++++--------------------------- 1 file changed, 95 insertions(+), 197 deletions(-) diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index 4d1766fb16ce9f..4e6fed1ab969ac 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -9,7 +9,6 @@ import random import re import test.support -import test.test_set import types import unittest @@ -623,9 +622,6 @@ def test_set_reprs(self): self.assertEqual(pprint.pformat(frozenset3(range(7)), width=20), 'frozenset3({0, 1, 2, 3, 4, 5, 6})') - @unittest.expectedFailure - #See http://bugs.python.org/issue13907 - @test.support.cpython_only def test_set_of_sets_reprs(self): # This test creates a complex arrangement of frozensets and # compares the pretty-printed repr against a string hard-coded in @@ -636,204 +632,106 @@ def test_set_of_sets_reprs(self): # partial ordering (subset relationships), the output of the # list.sort() method is undefined for lists of sets." # - # In a nutshell, the test assumes frozenset({0}) will always - # sort before frozenset({1}), but: - # # >>> frozenset({0}) < frozenset({1}) # False # >>> frozenset({1}) < frozenset({0}) # False # - # Consequently, this test is fragile and - # implementation-dependent. Small changes to Python's sort - # algorithm cause the test to fail when it should pass. - # XXX Or changes to the dictionary implementation... - - cube_repr_tgt = """\ -{frozenset(): frozenset({frozenset({2}), frozenset({0}), frozenset({1})}), - frozenset({0}): frozenset({frozenset(), - frozenset({0, 2}), - frozenset({0, 1})}), - frozenset({1}): frozenset({frozenset(), - frozenset({1, 2}), - frozenset({0, 1})}), - frozenset({2}): frozenset({frozenset(), - frozenset({1, 2}), - frozenset({0, 2})}), - frozenset({1, 2}): frozenset({frozenset({2}), - frozenset({1}), - frozenset({0, 1, 2})}), - frozenset({0, 2}): frozenset({frozenset({2}), - frozenset({0}), - frozenset({0, 1, 2})}), - frozenset({0, 1}): frozenset({frozenset({0}), - frozenset({1}), - frozenset({0, 1, 2})}), - frozenset({0, 1, 2}): frozenset({frozenset({1, 2}), - frozenset({0, 2}), - frozenset({0, 1})})}""" - cube = test.test_set.cube(3) - self.assertEqual(pprint.pformat(cube), cube_repr_tgt) - cubo_repr_tgt = """\ -{frozenset({frozenset({0, 2}), frozenset({0})}): frozenset({frozenset({frozenset({0, - 2}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({0}), - frozenset({0, - 1})}), - frozenset({frozenset(), - frozenset({0})}), - frozenset({frozenset({2}), - frozenset({0, - 2})})}), - frozenset({frozenset({0, 1}), frozenset({1})}): frozenset({frozenset({frozenset({0, - 1}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({0}), - frozenset({0, - 1})}), - frozenset({frozenset({1}), - frozenset({1, - 2})}), - frozenset({frozenset(), - frozenset({1})})}), - frozenset({frozenset({1, 2}), frozenset({1})}): frozenset({frozenset({frozenset({1, - 2}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({2}), - frozenset({1, - 2})}), - frozenset({frozenset(), - frozenset({1})}), - frozenset({frozenset({1}), - frozenset({0, - 1})})}), - frozenset({frozenset({1, 2}), frozenset({2})}): frozenset({frozenset({frozenset({1, - 2}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({1}), - frozenset({1, - 2})}), - frozenset({frozenset({2}), - frozenset({0, - 2})}), - frozenset({frozenset(), - frozenset({2})})}), - frozenset({frozenset(), frozenset({0})}): frozenset({frozenset({frozenset({0}), - frozenset({0, - 1})}), - frozenset({frozenset({0}), - frozenset({0, - 2})}), - frozenset({frozenset(), - frozenset({1})}), - frozenset({frozenset(), - frozenset({2})})}), - frozenset({frozenset(), frozenset({1})}): frozenset({frozenset({frozenset(), - frozenset({0})}), - frozenset({frozenset({1}), - frozenset({1, - 2})}), - frozenset({frozenset(), - frozenset({2})}), - frozenset({frozenset({1}), - frozenset({0, - 1})})}), - frozenset({frozenset({2}), frozenset()}): frozenset({frozenset({frozenset({2}), - frozenset({1, - 2})}), - frozenset({frozenset(), - frozenset({0})}), - frozenset({frozenset(), - frozenset({1})}), - frozenset({frozenset({2}), - frozenset({0, - 2})})}), - frozenset({frozenset({0, 1, 2}), frozenset({0, 1})}): frozenset({frozenset({frozenset({1, - 2}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({0, - 2}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({0}), - frozenset({0, - 1})}), - frozenset({frozenset({1}), - frozenset({0, - 1})})}), - frozenset({frozenset({0}), frozenset({0, 1})}): frozenset({frozenset({frozenset(), - frozenset({0})}), - frozenset({frozenset({0, - 1}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({0}), - frozenset({0, - 2})}), - frozenset({frozenset({1}), - frozenset({0, - 1})})}), - frozenset({frozenset({2}), frozenset({0, 2})}): frozenset({frozenset({frozenset({0, - 2}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({2}), - frozenset({1, - 2})}), - frozenset({frozenset({0}), - frozenset({0, - 2})}), - frozenset({frozenset(), - frozenset({2})})}), - frozenset({frozenset({0, 1, 2}), frozenset({0, 2})}): frozenset({frozenset({frozenset({1, - 2}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({0, - 1}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({0}), - frozenset({0, - 2})}), - frozenset({frozenset({2}), - frozenset({0, - 2})})}), - frozenset({frozenset({1, 2}), frozenset({0, 1, 2})}): frozenset({frozenset({frozenset({0, - 2}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({0, - 1}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({2}), - frozenset({1, - 2})}), - frozenset({frozenset({1}), - frozenset({1, - 2})})})}""" - - cubo = test.test_set.linegraph(cube) - self.assertEqual(pprint.pformat(cubo), cubo_repr_tgt) + # In this test we list all possible invariants of the result + # for unordered frozensets. + # + # This test has a long history, see: + # - https://github.com/python/cpython/commit/969fe57baa0eb80332990f9cda936a33e13fabef + # - https://github.com/python/cpython/issues/58115 + # - https://github.com/python/cpython/issues/111147 + + import textwrap + + # Single-line, always ordered: + fs0 = frozenset() + fs1 = frozenset(('abc', 'xyz')) + data = frozenset((fs0, fs1)) + self.assertEqual(pprint.pformat(data), + 'frozenset({%r, %r})' % (fs0, fs1)) + self.assertEqual(pprint.pformat(data), repr(data)) + + fs2 = frozenset(('one', 'two')) + data = {fs2: frozenset((fs0, fs1))} + self.assertEqual(pprint.pformat(data), + "{%r: frozenset({%r, %r})}" % (fs2, fs0, fs1)) + self.assertEqual(pprint.pformat(data), repr(data)) + + # Single-line, unordered: + fs1 = frozenset(("xyz", "qwerty")) + fs2 = frozenset(("abcd", "spam")) + fs = frozenset((fs1, fs2)) + self.assertEqual(pprint.pformat(fs), repr(fs)) + + # Multiline, unordered: + def check(res, invariants): + self.assertIn(res, [textwrap.dedent(i).strip() for i in invariants]) + + # Inner-most frozensets are singleline, result is multiline, unordered: + fs1 = frozenset(('regular string', 'other string')) + fs2 = frozenset(('third string', 'one more string')) + check( + pprint.pformat(frozenset((fs1, fs2))), + [ + """ + frozenset({%r, + %r}) + """ % (fs1, fs2), + """ + frozenset({%r, + %r}) + """ % (fs2, fs1), + ], + ) + + # Everything is multiline, unordered: + check( + pprint.pformat( + frozenset(( + frozenset(( + "xyz very-very long string", + "qwerty is also absurdly long", + )), + frozenset(( + "abcd is even longer that before", + "spam is not so long", + )), + )), + ), + [ + """ + frozenset({frozenset({'abcd is even longer that before', + 'spam is not so long'}), + frozenset({'qwerty is also absurdly long', + 'xyz very-very long string'})}) + """, + + """ + frozenset({frozenset({'abcd is even longer that before', + 'spam is not so long'}), + frozenset({'xyz very-very long string', + 'qwerty is also absurdly long'})}) + """, + + """ + frozenset({frozenset({'qwerty is also absurdly long', + 'xyz very-very long string'}), + frozenset({'abcd is even longer that before', + 'spam is not so long'})}) + """, + + """ + frozenset({frozenset({'qwerty is also absurdly long', + 'xyz very-very long string'}), + frozenset({'spam is not so long', + 'abcd is even longer that before'})}) + """, + ], + ) def test_depth(self): nested_tuple = (1, (2, (3, (4, (5, 6))))) From d44ee42cd7c9a40e1d7096b95476fe47156f571f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 27 Nov 2023 12:55:52 +0100 Subject: [PATCH 066/228] Move What's New In Python 3.12 entries to the right section (#112447) Jython and ctypes removals are unrelated to C API Removals. --- Doc/whatsnew/3.12.rst | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 8b7a043d068e5c..a4b3a6d12970b4 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -1773,6 +1773,14 @@ Others (*ssl_context* in :mod:`imaplib`) instead. (Contributed by Victor Stinner in :gh:`94172`.) +* Remove ``Jython`` compatibility hacks from several stdlib modules and tests. + (Contributed by Nikita Sobolev in :gh:`99482`.) + +* Remove ``_use_broken_old_ctypes_structure_semantics_`` flag + from :mod:`ctypes` module. + (Contributed by Nikita Sobolev in :gh:`99285`.) + + .. _whatsnew312-porting-to-python312: Porting to Python 3.12 @@ -2424,10 +2432,3 @@ Removed * Remove the ``PyUnicode_InternImmortal()`` function macro. (Contributed by Victor Stinner in :gh:`85858`.) - -* Remove ``Jython`` compatibility hacks from several stdlib modules and tests. - (Contributed by Nikita Sobolev in :gh:`99482`.) - -* Remove ``_use_broken_old_ctypes_structure_semantics_`` flag - from :mod:`ctypes` module. - (Contributed by Nikita Sobolev in :gh:`99285`.) From ffe1b2d07b88e185f373ad696fbea5a7f2a315c1 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:36:54 +0000 Subject: [PATCH 067/228] GH-101100: Fix reference warnings for ``socket`` methods (#110114) Co-authored-by: Serhiy Storchaka --- Doc/library/socket.rst | 24 ++++++++++++------------ Doc/whatsnew/2.0.rst | 6 +++--- Doc/whatsnew/2.7.rst | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 9ff1aa3984e828..e36fc17f89de24 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -23,7 +23,7 @@ all modern Unix systems, Windows, MacOS, and probably additional platforms. The Python interface is a straightforward transliteration of the Unix system call and library interface for sockets to Python's object-oriented style: the -:func:`.socket` function returns a :dfn:`socket object` whose methods implement +:func:`~socket.socket` function returns a :dfn:`socket object` whose methods implement the various socket system calls. Parameter types are somewhat higher-level than in the C interface: as with :meth:`read` and :meth:`write` operations on Python files, buffer allocation on receive operations is automatic, and buffer length @@ -348,7 +348,7 @@ Constants AF_INET6 These constants represent the address (and protocol) families, used for the - first argument to :func:`.socket`. If the :const:`AF_UNIX` constant is not + first argument to :func:`~socket.socket`. If the :const:`AF_UNIX` constant is not defined then this protocol is unsupported. More constants may be available depending on the system. @@ -365,7 +365,7 @@ Constants SOCK_SEQPACKET These constants represent the socket types, used for the second argument to - :func:`.socket`. More constants may be available depending on the system. + :func:`~socket.socket`. More constants may be available depending on the system. (Only :const:`SOCK_STREAM` and :const:`SOCK_DGRAM` appear to be generally useful.) @@ -404,7 +404,7 @@ Constants Many constants of these forms, documented in the Unix documentation on sockets and/or the IP protocol, are also defined in the socket module. They are - generally used in arguments to the :meth:`setsockopt` and :meth:`getsockopt` + generally used in arguments to the :meth:`~socket.setsockopt` and :meth:`~socket.getsockopt` methods of socket objects. In most cases, only those symbols that are defined in the Unix header files are defined; for a few symbols, default values are provided. @@ -770,7 +770,7 @@ The following functions all create :ref:`socket objects `. Build a pair of connected socket objects using the given address family, socket type, and protocol number. Address family, socket type, and protocol number are - as for the :func:`.socket` function above. The default family is :const:`AF_UNIX` + as for the :func:`~socket.socket` function above. The default family is :const:`AF_UNIX` if defined on the platform; otherwise, the default is :const:`AF_INET`. The newly created sockets are :ref:`non-inheritable `. @@ -866,7 +866,7 @@ The following functions all create :ref:`socket objects `. Duplicate the file descriptor *fd* (an integer as returned by a file object's :meth:`~io.IOBase.fileno` method) and build a socket object from the result. Address - family, socket type and protocol number are as for the :func:`.socket` function + family, socket type and protocol number are as for the :func:`~socket.socket` function above. The file descriptor should refer to a socket, but this is not checked --- subsequent operations on the object may fail if the file descriptor is invalid. This function is rarely needed, but can be used to get or set socket options on @@ -931,7 +931,7 @@ The :mod:`socket` module also offers various network-related services: ``(family, type, proto, canonname, sockaddr)`` In these tuples, *family*, *type*, *proto* are all integers and are - meant to be passed to the :func:`.socket` function. *canonname* will be + meant to be passed to the :func:`~socket.socket` function. *canonname* will be a string representing the canonical name of the *host* if :const:`AI_CANONNAME` is part of the *flags* argument; else *canonname* will be empty. *sockaddr* is a tuple describing a socket address, whose @@ -1047,7 +1047,7 @@ The :mod:`socket` module also offers various network-related services: .. function:: getprotobyname(protocolname) Translate an internet protocol name (for example, ``'icmp'``) to a constant - suitable for passing as the (optional) third argument to the :func:`.socket` + suitable for passing as the (optional) third argument to the :func:`~socket.socket` function. This is usually only needed for sockets opened in "raw" mode (:const:`SOCK_RAW`); for the normal socket modes, the correct protocol is chosen automatically if the protocol is omitted or zero. @@ -1331,7 +1331,7 @@ The :mod:`socket` module also offers various network-related services: Send the list of file descriptors *fds* over an :const:`AF_UNIX` socket *sock*. The *fds* parameter is a sequence of file descriptors. - Consult :meth:`sendmsg` for the documentation of these parameters. + Consult :meth:`~socket.sendmsg` for the documentation of these parameters. .. availability:: Unix, Windows, not Emscripten, not WASI. @@ -1345,7 +1345,7 @@ The :mod:`socket` module also offers various network-related services: Receive up to *maxfds* file descriptors from an :const:`AF_UNIX` socket *sock*. Return ``(msg, list(fds), flags, addr)``. - Consult :meth:`recvmsg` for the documentation of these parameters. + Consult :meth:`~socket.recvmsg` for the documentation of these parameters. .. availability:: Unix, Windows, not Emscripten, not WASI. @@ -2064,10 +2064,10 @@ Example Here are four minimal example programs using the TCP/IP protocol: a server that echoes all data that it receives back (servicing only one client), and a client -using it. Note that a server must perform the sequence :func:`.socket`, +using it. Note that a server must perform the sequence :func:`~socket.socket`, :meth:`~socket.bind`, :meth:`~socket.listen`, :meth:`~socket.accept` (possibly repeating the :meth:`~socket.accept` to service more than one client), while a -client only needs the sequence :func:`.socket`, :meth:`~socket.connect`. Also +client only needs the sequence :func:`~socket.socket`, :meth:`~socket.connect`. Also note that the server does not :meth:`~socket.sendall`/:meth:`~socket.recv` on the socket it is listening on but on the new socket returned by :meth:`~socket.accept`. diff --git a/Doc/whatsnew/2.0.rst b/Doc/whatsnew/2.0.rst index c2b0ae8c76302a..6d6e51006d5bd8 100644 --- a/Doc/whatsnew/2.0.rst +++ b/Doc/whatsnew/2.0.rst @@ -671,9 +671,9 @@ errors. If you absolutely must use 2.0 but can't fix your code, you can edit ``NO_STRICT_LIST_APPEND`` to preserve the old behaviour; this isn't recommended. Some of the functions in the :mod:`socket` module are still forgiving in this -way. For example, :func:`socket.connect( ('hostname', 25) )` is the correct -form, passing a tuple representing an IP address, but :func:`socket.connect( -'hostname', 25 )` also works. :func:`socket.connect_ex` and :func:`socket.bind` +way. For example, ``socket.connect( ('hostname', 25) )`` is the correct +form, passing a tuple representing an IP address, but ``socket.connect('hostname', 25)`` +also works. :meth:`socket.connect_ex ` and :meth:`socket.bind ` are similarly easy-going. 2.0alpha1 tightened these functions up, but because the documentation actually used the erroneous multiple argument form, many people wrote code which would break with the stricter checking. GvR backed out diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index 61934ab1a2df56..da66dd731831bc 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -2383,8 +2383,8 @@ Port-Specific Changes: Mac OS X Port-Specific Changes: FreeBSD ----------------------------------- -* FreeBSD 7.1's :const:`SO_SETFIB` constant, used with - :func:`~socket.getsockopt`/:func:`~socket.setsockopt` to select an +* FreeBSD 7.1's :const:`SO_SETFIB` constant, used with the :func:`~socket.socket` methods + :func:`~socket.socket.getsockopt`/:func:`~socket.socket.setsockopt` to select an alternate routing table, is now available in the :mod:`socket` module. (Added by Kyle VanderBeek; :issue:`8235`.) From 22e411e1d107f79a0904d41a489a82355a39b5de Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 27 Nov 2023 16:34:44 +0000 Subject: [PATCH 068/228] gh-111874: Call `__set_name__` on objects that define the method inside a `typing.NamedTuple` class dictionary as part of the creation of that class (#111876) Co-authored-by: Jelle Zijlstra --- Lib/test/test_typing.py | 77 +++++++++++++++++++ Lib/typing.py | 21 ++++- ...-11-09-11-07-34.gh-issue-111874.dzYc3j.rst | 4 + 3 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-09-11-07-34.gh-issue-111874.dzYc3j.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 31d7fda2f978da..669803177315e3 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -7535,6 +7535,83 @@ class GenericNamedTuple(NamedTuple, Generic[T]): self.assertEqual(CallNamedTuple.__orig_bases__, (NamedTuple,)) + def test_setname_called_on_values_in_class_dictionary(self): + class Vanilla: + def __set_name__(self, owner, name): + self.name = name + + class Foo(NamedTuple): + attr = Vanilla() + + foo = Foo() + self.assertEqual(len(foo), 0) + self.assertNotIn('attr', Foo._fields) + self.assertIsInstance(foo.attr, Vanilla) + self.assertEqual(foo.attr.name, "attr") + + class Bar(NamedTuple): + attr: Vanilla = Vanilla() + + bar = Bar() + self.assertEqual(len(bar), 1) + self.assertIn('attr', Bar._fields) + self.assertIsInstance(bar.attr, Vanilla) + self.assertEqual(bar.attr.name, "attr") + + def test_setname_raises_the_same_as_on_other_classes(self): + class CustomException(BaseException): pass + + class Annoying: + def __set_name__(self, owner, name): + raise CustomException + + annoying = Annoying() + + with self.assertRaises(CustomException) as cm: + class NormalClass: + attr = annoying + normal_exception = cm.exception + + with self.assertRaises(CustomException) as cm: + class NamedTupleClass(NamedTuple): + attr = annoying + namedtuple_exception = cm.exception + + self.assertIs(type(namedtuple_exception), CustomException) + self.assertIs(type(namedtuple_exception), type(normal_exception)) + + self.assertEqual(len(namedtuple_exception.__notes__), 1) + self.assertEqual( + len(namedtuple_exception.__notes__), len(normal_exception.__notes__) + ) + + expected_note = ( + "Error calling __set_name__ on 'Annoying' instance " + "'attr' in 'NamedTupleClass'" + ) + self.assertEqual(namedtuple_exception.__notes__[0], expected_note) + self.assertEqual( + namedtuple_exception.__notes__[0], + normal_exception.__notes__[0].replace("NormalClass", "NamedTupleClass") + ) + + def test_strange_errors_when_accessing_set_name_itself(self): + class CustomException(Exception): pass + + class Meta(type): + def __getattribute__(self, attr): + if attr == "__set_name__": + raise CustomException + return object.__getattribute__(self, attr) + + class VeryAnnoying(metaclass=Meta): pass + + very_annoying = VeryAnnoying() + + with self.assertRaises(CustomException): + class Foo(NamedTuple): + attr = very_annoying + class TypedDictTests(BaseTestCase): def test_basics_functional_syntax(self): diff --git a/Lib/typing.py b/Lib/typing.py index 872aca82c4e779..216f0c141b62af 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -2743,11 +2743,26 @@ def __new__(cls, typename, bases, ns): class_getitem = _generic_class_getitem nm_tpl.__class_getitem__ = classmethod(class_getitem) # update from user namespace without overriding special namedtuple attributes - for key in ns: + for key, val in ns.items(): if key in _prohibited: raise AttributeError("Cannot overwrite NamedTuple attribute " + key) - elif key not in _special and key not in nm_tpl._fields: - setattr(nm_tpl, key, ns[key]) + elif key not in _special: + if key not in nm_tpl._fields: + setattr(nm_tpl, key, val) + try: + set_name = type(val).__set_name__ + except AttributeError: + pass + else: + try: + set_name(val, nm_tpl, key) + except BaseException as e: + e.add_note( + f"Error calling __set_name__ on {type(val).__name__!r} " + f"instance {key!r} in {typename!r}" + ) + raise + if Generic in bases: nm_tpl.__init_subclass__() return nm_tpl diff --git a/Misc/NEWS.d/next/Library/2023-11-09-11-07-34.gh-issue-111874.dzYc3j.rst b/Misc/NEWS.d/next/Library/2023-11-09-11-07-34.gh-issue-111874.dzYc3j.rst new file mode 100644 index 00000000000000..50408202a7a5a1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-09-11-07-34.gh-issue-111874.dzYc3j.rst @@ -0,0 +1,4 @@ +When creating a :class:`typing.NamedTuple` class, ensure +:func:`~object.__set_name__` is called on all objects that define +``__set_name__`` and exist in the values of the ``NamedTuple`` class's class +dictionary. Patch by Alex Waygood. From 812360fddda86d7aff5823f529ab720f57ddc411 Mon Sep 17 00:00:00 2001 From: Zackery Spytz Date: Mon, 27 Nov 2023 09:15:39 -0800 Subject: [PATCH 069/228] gh-84443: SSLSocket.recv_into() now support buffer protocol with itemsize != 1 (GH-20310) It is also no longer use __len__(). Co-authored-by: Serhiy Storchaka --- Lib/ssl.py | 12 ++++++---- Lib/test/test_ssl.py | 22 +++++++++++++++++++ .../2020-05-21-23-32-46.bpo-40262.z4fQv1.rst | 2 ++ 3 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-05-21-23-32-46.bpo-40262.z4fQv1.rst diff --git a/Lib/ssl.py b/Lib/ssl.py index 62e55857141dfc..36fca9d4aa93d1 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -1270,10 +1270,14 @@ def recv(self, buflen=1024, flags=0): def recv_into(self, buffer, nbytes=None, flags=0): self._checkClosed() - if buffer and (nbytes is None): - nbytes = len(buffer) - elif nbytes is None: - nbytes = 1024 + if nbytes is None: + if buffer is not None: + with memoryview(buffer) as view: + nbytes = view.nbytes + if not nbytes: + nbytes = 1024 + else: + nbytes = 1024 if self._sslobj is not None: if flags != 0: raise ValueError( diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 9ade595ef8ae7e..aecba89cde1495 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -10,6 +10,7 @@ from test.support import threading_helper from test.support import warnings_helper from test.support import asyncore +import array import re import socket import select @@ -3517,6 +3518,27 @@ def test_recv_zero(self): self.assertEqual(s.recv(0), b"") self.assertEqual(s.recv_into(bytearray()), 0) + def test_recv_into_buffer_protocol_len(self): + server = ThreadedEchoServer(CERTFILE) + self.enterContext(server) + s = socket.create_connection((HOST, server.port)) + self.addCleanup(s.close) + s = test_wrap_socket(s, suppress_ragged_eofs=False) + self.addCleanup(s.close) + + s.send(b"data") + buf = array.array('I', [0, 0]) + self.assertEqual(s.recv_into(buf), 4) + self.assertEqual(bytes(buf)[:4], b"data") + + class B(bytearray): + def __len__(self): + 1/0 + s.send(b"data") + buf = B(6) + self.assertEqual(s.recv_into(buf), 4) + self.assertEqual(bytes(buf), b"data\0\0") + def test_nonblocking_send(self): server = ThreadedEchoServer(CERTFILE, certreqs=ssl.CERT_NONE, diff --git a/Misc/NEWS.d/next/Library/2020-05-21-23-32-46.bpo-40262.z4fQv1.rst b/Misc/NEWS.d/next/Library/2020-05-21-23-32-46.bpo-40262.z4fQv1.rst new file mode 100644 index 00000000000000..c017a1c8df09d8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-05-21-23-32-46.bpo-40262.z4fQv1.rst @@ -0,0 +1,2 @@ +The :meth:`ssl.SSLSocket.recv_into` method no longer requires the *buffer* +argument to implement ``__len__`` and supports buffers with arbitrary item size. From 4eea1e82369fbf7a795d1956e7a8212a1b58009f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 27 Nov 2023 19:32:55 +0200 Subject: [PATCH 070/228] gh-112438: Fix support of format units with the "e" prefix in nested tuples in PyArg_Parse (gh-112439) --- Lib/test/test_capi/test_getargs.py | 28 +++++++++++++++++++ ...-11-27-09-44-16.gh-issue-112438.GdNZiI.rst | 2 ++ Modules/_testcapi/getargs.c | 14 ++++++---- Python/getargs.c | 2 +- 4 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2023-11-27-09-44-16.gh-issue-112438.GdNZiI.rst diff --git a/Lib/test/test_capi/test_getargs.py b/Lib/test/test_capi/test_getargs.py index c964b1efd577ba..9b6aef27625ad0 100644 --- a/Lib/test/test_capi/test_getargs.py +++ b/Lib/test/test_capi/test_getargs.py @@ -1314,6 +1314,34 @@ def test_nonascii_keywords(self): f"'{name2}' is an invalid keyword argument"): parse((), {name2: 1, name3: 2}, '|OO', [name, name3]) + def test_nested_tuple(self): + parse = _testcapi.parse_tuple_and_keywords + + self.assertEqual(parse(((1, 2, 3),), {}, '(OOO)', ['a']), (1, 2, 3)) + self.assertEqual(parse((1, (2, 3), 4), {}, 'O(OO)O', ['a', 'b', 'c']), + (1, 2, 3, 4)) + parse(((1, 2, 3),), {}, '(iii)', ['a']) + + with self.assertRaisesRegex(TypeError, + "argument 1 must be sequence of length 2, not 3"): + parse(((1, 2, 3),), {}, '(ii)', ['a']) + with self.assertRaisesRegex(TypeError, + "argument 1 must be sequence of length 2, not 1"): + parse(((1,),), {}, '(ii)', ['a']) + with self.assertRaisesRegex(TypeError, + "argument 1 must be 2-item sequence, not int"): + parse((1,), {}, '(ii)', ['a']) + with self.assertRaisesRegex(TypeError, + "argument 1 must be 2-item sequence, not bytes"): + parse((b'ab',), {}, '(ii)', ['a']) + + for f in 'es', 'et', 'es#', 'et#': + with self.assertRaises(LookupError): # empty encoding "" + parse((('a',),), {}, '(' + f + ')', ['a']) + with self.assertRaisesRegex(TypeError, + "argument 1 must be sequence of length 1, not 0"): + parse(((),), {}, '(' + f + ')', ['a']) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/C API/2023-11-27-09-44-16.gh-issue-112438.GdNZiI.rst b/Misc/NEWS.d/next/C API/2023-11-27-09-44-16.gh-issue-112438.GdNZiI.rst new file mode 100644 index 00000000000000..113119efd6aebb --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-11-27-09-44-16.gh-issue-112438.GdNZiI.rst @@ -0,0 +1,2 @@ +Fix support of format units "es", "et", "es#", and "et#" in nested tuples in +:c:func:`PyArg_ParseTuple`-like functions. diff --git a/Modules/_testcapi/getargs.c b/Modules/_testcapi/getargs.c index e4cd15503fd11f..33e8af7d7bbb39 100644 --- a/Modules/_testcapi/getargs.c +++ b/Modules/_testcapi/getargs.c @@ -71,18 +71,22 @@ parse_tuple_and_keywords(PyObject *self, PyObject *args) if (result) { int objects_only = 1; + int count = 0; for (const char *f = sub_format; *f; f++) { - if (Py_ISALNUM(*f) && strchr("OSUY", *f) == NULL) { - objects_only = 0; - break; + if (Py_ISALNUM(*f)) { + if (strchr("OSUY", *f) == NULL) { + objects_only = 0; + break; + } + count++; } } if (objects_only) { - return_value = PyTuple_New(size); + return_value = PyTuple_New(count); if (return_value == NULL) { goto exit; } - for (Py_ssize_t i = 0; i < size; i++) { + for (Py_ssize_t i = 0; i < count; i++) { PyObject *arg = *(PyObject **)(buffers + i); if (arg == NULL) { arg = Py_None; diff --git a/Python/getargs.c b/Python/getargs.c index c0c2eb27184e3c..5ff8f7473609f5 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -477,7 +477,7 @@ converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags, } else if (c == ':' || c == ';' || c == '\0') break; - else if (level == 0 && Py_ISALPHA(c)) + else if (level == 0 && Py_ISALPHA(c) && c != 'e') n++; } From b14e5df120ca8ce968a67df2e00e7a764dd703a0 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 27 Nov 2023 19:35:52 +0200 Subject: [PATCH 071/228] gh-111789: Use PyDict_GetItemRef() in Modules/_csv.c (gh-112073) --- Modules/_csv.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Modules/_csv.c b/Modules/_csv.c index 714fbef08d22c9..ae6b6457ffad9a 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -160,15 +160,9 @@ static PyObject * get_dialect_from_registry(PyObject *name_obj, _csvstate *module_state) { PyObject *dialect_obj; - - dialect_obj = PyDict_GetItemWithError(module_state->dialects, name_obj); - if (dialect_obj == NULL) { - if (!PyErr_Occurred()) - PyErr_Format(module_state->error_obj, "unknown dialect"); + if (PyDict_GetItemRef(module_state->dialects, name_obj, &dialect_obj) == 0) { + PyErr_SetString(module_state->error_obj, "unknown dialect"); } - else - Py_INCREF(dialect_obj); - return dialect_obj; } From 0f009033202b339375944f613aa6d0597a2841de Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 27 Nov 2023 19:41:47 +0200 Subject: [PATCH 072/228] gh-111789: Use PyDict_GetItemRef() in Modules/_struct.c (gh-112076) --- Modules/_struct.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Modules/_struct.c b/Modules/_struct.c index 24a4cb3b6413f1..bd16fa89f18945 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -2257,14 +2257,13 @@ cache_struct_converter(PyObject *module, PyObject *fmt, PyStructObject **ptr) return 1; } - s_object = PyDict_GetItemWithError(state->cache, fmt); + if (PyDict_GetItemRef(state->cache, fmt, &s_object) < 0) { + return 0; + } if (s_object != NULL) { - *ptr = (PyStructObject *)Py_NewRef(s_object); + *ptr = (PyStructObject *)s_object; return Py_CLEANUP_SUPPORTED; } - else if (PyErr_Occurred()) { - return 0; - } s_object = PyObject_CallOneArg(state->PyStructType, fmt); if (s_object != NULL) { From ef9b2fc9b0378aee87328fadce73b3fefb6aca4a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 27 Nov 2023 19:46:43 +0200 Subject: [PATCH 073/228] gh-111789: Use PyDict_GetItemRef() in Modules/_threadmodule.c (gh-112077) --- Modules/_threadmodule.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index c608789b5fbd5c..afcf646e3bc19e 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1115,12 +1115,10 @@ local_getattro(localobject *self, PyObject *name) } /* Optimization: just look in dict ourselves */ - PyObject *value = PyDict_GetItemWithError(ldict, name); - if (value != NULL) { - return Py_NewRef(value); - } - if (PyErr_Occurred()) { - return NULL; + PyObject *value; + if (PyDict_GetItemRef(ldict, name, &value) != 0) { + // found or error + return value; } /* Fall back on generic to get __class__ and __dict__ */ From d8908932fc03e064ba8df03d17d8cc7ffa5f171f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 27 Nov 2023 19:51:31 +0200 Subject: [PATCH 074/228] gh-111789: Use PyDict_GetItemRef() in Modules/pyexpat.c (gh-112079) --- Modules/pyexpat.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index 21579a80dd7f70..9d95309dbb7aa6 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -240,19 +240,12 @@ string_intern(xmlparseobject *self, const char* str) return result; if (!self->intern) return result; - value = PyDict_GetItemWithError(self->intern, result); - if (!value) { - if (!PyErr_Occurred() && - PyDict_SetItem(self->intern, result, result) == 0) - { - return result; - } - else { - Py_DECREF(result); - return NULL; - } + if (PyDict_GetItemRef(self->intern, result, &value) == 0 && + PyDict_SetItem(self->intern, result, result) == 0) + { + return result; } - Py_INCREF(value); + assert((value != NULL) == !PyErr_Occurred()); Py_DECREF(result); return value; } From 395fd9c1808fa0babc96540744d2c915178a452b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 27 Nov 2023 19:52:54 +0200 Subject: [PATCH 075/228] gh-111789: Use PyDict_GetItemRef() in Python/bltinmodule.c (gh-112081) --- Python/bltinmodule.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index ff07c498263cd3..7a9625134761f9 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -5,7 +5,6 @@ #include "pycore_call.h" // _PyObject_CallNoArgs() #include "pycore_ceval.h" // _PyEval_Vector() #include "pycore_compile.h" // _PyAST_Compile() -#include "pycore_dict.h" // _PyDict_GetItemWithError() #include "pycore_long.h" // _PyLong_CompactValue #include "pycore_modsupport.h" // _PyArg_NoKwnames() #include "pycore_object.h" // _Py_AddToAllObjects() @@ -141,18 +140,16 @@ builtin___build_class__(PyObject *self, PyObject *const *args, Py_ssize_t nargs, goto error; } - meta = _PyDict_GetItemWithError(mkw, &_Py_ID(metaclass)); + if (PyDict_GetItemRef(mkw, &_Py_ID(metaclass), &meta) < 0) { + goto error; + } if (meta != NULL) { - Py_INCREF(meta); if (PyDict_DelItem(mkw, &_Py_ID(metaclass)) < 0) { goto error; } /* metaclass is explicitly given, check if it's indeed a class */ isclass = PyType_Check(meta); } - else if (PyErr_Occurred()) { - goto error; - } } if (meta == NULL) { /* if there are no bases, use type: */ From aa438bdd6deed225d30d87dc3a77602ffc924213 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 27 Nov 2023 19:53:43 +0200 Subject: [PATCH 076/228] gh-111789: Use PyDict_GetItemRef() in Python/codecs.c (gh-112082) --- Python/codecs.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Python/codecs.c b/Python/codecs.c index b79bf555f2f22a..545bf82e00dca1 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -146,15 +146,14 @@ PyObject *_PyCodec_Lookup(const char *encoding) PyUnicode_InternInPlace(&v); /* First, try to lookup the name in the registry dictionary */ - PyObject *result = PyDict_GetItemWithError(interp->codec_search_cache, v); + PyObject *result; + if (PyDict_GetItemRef(interp->codec_search_cache, v, &result) < 0) { + goto onError; + } if (result != NULL) { - Py_INCREF(result); Py_DECREF(v); return result; } - else if (PyErr_Occurred()) { - goto onError; - } /* Next, scan the search functions in order of registration */ const Py_ssize_t len = PyList_Size(interp->codec_search_path); From befbad3663a48a8de2e1263afe18ec9fa47dfc6d Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 27 Nov 2023 19:55:30 +0200 Subject: [PATCH 077/228] gh-111789: Use PyDict_GetItemRef() in Python/symtable.c (gh-112084) --- Python/symtable.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Python/symtable.c b/Python/symtable.c index da7fec0ee7cf0c..52d5932896b263 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -497,18 +497,14 @@ _PySymtable_Lookup(struct symtable *st, void *key) k = PyLong_FromVoidPtr(key); if (k == NULL) return NULL; - v = PyDict_GetItemWithError(st->st_blocks, k); - Py_DECREF(k); - - if (v) { - assert(PySTEntry_Check(v)); - } - else if (!PyErr_Occurred()) { + if (PyDict_GetItemRef(st->st_blocks, k, &v) == 0) { PyErr_SetString(PyExc_KeyError, "unknown symbol table entry"); } + Py_DECREF(k); - return (PySTEntryObject *)Py_XNewRef(v); + assert(v == NULL || PySTEntry_Check(v)); + return (PySTEntryObject *)v; } long From 936c503a442ee062c837e334f237796554c792ff Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 27 Nov 2023 19:58:43 +0200 Subject: [PATCH 078/228] gh-111789: Use PyDict_GetItemRef() in Python/_warnings.c (gh-112080) --- Python/_warnings.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Python/_warnings.c b/Python/_warnings.c index 4b7fb888247145..d4765032824e56 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -425,15 +425,15 @@ already_warned(PyInterpreterState *interp, PyObject *registry, PyObject *key, Py_DECREF(version_obj); } else { - already_warned = PyDict_GetItemWithError(registry, key); + if (PyDict_GetItemRef(registry, key, &already_warned) < 0) { + return -1; + } if (already_warned != NULL) { int rc = PyObject_IsTrue(already_warned); + Py_DECREF(already_warned); if (rc != 0) return rc; } - else if (PyErr_Occurred()) { - return -1; - } } /* This warning wasn't found in the registry, set it. */ From 99a73c3465a45fe57cac01a917fc50e0743b5964 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Mon, 27 Nov 2023 13:05:55 -0500 Subject: [PATCH 079/228] gh-76912: Raise OSError from any failure in getpass.getuser() (#29739) * bpo-32731: Raise OSError from any failure in getpass.getuser() Previously, if the username was not set in certain environment variables, ImportError escaped on Windows systems, and it was possible for KeyError to escape on other systems if getpwuid() failed. --- Doc/library/getpass.rst | 7 +++++-- Doc/whatsnew/3.13.rst | 4 ++++ Lib/getpass.py | 13 ++++++++++--- Lib/test/test_getpass.py | 4 ++-- .../2021-11-23-22-22-49.bpo-32731.kNOASr.rst | 3 +++ 5 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2021-11-23-22-22-49.bpo-32731.kNOASr.rst diff --git a/Doc/library/getpass.rst b/Doc/library/getpass.rst index 5c79daf0f47d8e..54c84d45a59856 100644 --- a/Doc/library/getpass.rst +++ b/Doc/library/getpass.rst @@ -46,7 +46,10 @@ The :mod:`getpass` module provides two functions: :envvar:`USER`, :envvar:`!LNAME` and :envvar:`USERNAME`, in order, and returns the value of the first one which is set to a non-empty string. If none are set, the login name from the password database is returned on - systems which support the :mod:`pwd` module, otherwise, an exception is - raised. + systems which support the :mod:`pwd` module, otherwise, an :exc:`OSError` + is raised. In general, this function should be preferred over :func:`os.getlogin()`. + + .. versionchanged:: 3.13 + Previously, various exceptions beyond just :exc:`OSError` were raised. diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 3fd0f5e165f018..ec09dfea4aad3c 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1032,6 +1032,10 @@ Changes in the Python API recomended in the documentation. (Contributed by Serhiy Storchaka in :gh:`106672`.) +* An :exc:`OSError` is now raised by :func:`getpass.getuser` for any failure to + retrieve a username, instead of :exc:`ImportError` on non-Unix platforms or + :exc:`KeyError` on Unix platforms where the password database is empty. + Build Changes ============= diff --git a/Lib/getpass.py b/Lib/getpass.py index 8b42c0a536b4c4..bd0097ced94c5e 100644 --- a/Lib/getpass.py +++ b/Lib/getpass.py @@ -156,7 +156,11 @@ def getuser(): First try various environment variables, then the password database. This works on Windows as long as USERNAME is set. + Any failure to find a username raises OSError. + .. versionchanged:: 3.13 + Previously, various exceptions beyond just :exc:`OSError` + were raised. """ for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'): @@ -164,9 +168,12 @@ def getuser(): if user: return user - # If this fails, the exception will "explain" why - import pwd - return pwd.getpwuid(os.getuid())[0] + try: + import pwd + return pwd.getpwuid(os.getuid())[0] + except (ImportError, KeyError) as e: + raise OSError('No username set in the environment') from e + # Bind the name getpass to the appropriate function try: diff --git a/Lib/test/test_getpass.py b/Lib/test/test_getpass.py index 98ecec94336e32..80dda2caaa3331 100644 --- a/Lib/test/test_getpass.py +++ b/Lib/test/test_getpass.py @@ -26,7 +26,7 @@ def test_username_priorities_of_env_values(self, environ): environ.get.return_value = None try: getpass.getuser() - except ImportError: # in case there's no pwd module + except OSError: # in case there's no pwd module pass except KeyError: # current user has no pwd entry @@ -47,7 +47,7 @@ def test_username_falls_back_to_pwd(self, environ): getpass.getuser()) getpw.assert_called_once_with(42) else: - self.assertRaises(ImportError, getpass.getuser) + self.assertRaises(OSError, getpass.getuser) class GetpassRawinputTest(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2021-11-23-22-22-49.bpo-32731.kNOASr.rst b/Misc/NEWS.d/next/Library/2021-11-23-22-22-49.bpo-32731.kNOASr.rst new file mode 100644 index 00000000000000..92f3b870c11131 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-11-23-22-22-49.bpo-32731.kNOASr.rst @@ -0,0 +1,3 @@ +:func:`getpass.getuser` now raises :exc:`OSError` for all failures rather +than :exc:`ImportError` on systems lacking the :mod:`pwd` module or +:exc:`KeyError` if the password database is empty. From 967f2a3052c2d22e31564b428a9aa8cc63dc2a9f Mon Sep 17 00:00:00 2001 From: kale-smoothie <34165060+kale-smoothie@users.noreply.github.com> Date: Mon, 27 Nov 2023 18:09:41 +0000 Subject: [PATCH 080/228] bpo-41422: Visit the Pickler's and Unpickler's memo in tp_traverse (GH-21664) Co-authored-by: Serhiy Storchaka --- .../2020-07-28-20-48-05.bpo-41422.iMwnMu.rst | 2 ++ Modules/_pickle.c | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2020-07-28-20-48-05.bpo-41422.iMwnMu.rst diff --git a/Misc/NEWS.d/next/Library/2020-07-28-20-48-05.bpo-41422.iMwnMu.rst b/Misc/NEWS.d/next/Library/2020-07-28-20-48-05.bpo-41422.iMwnMu.rst new file mode 100644 index 00000000000000..8bde68f8f2afc8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-07-28-20-48-05.bpo-41422.iMwnMu.rst @@ -0,0 +1,2 @@ +Fixed memory leaks of :class:`pickle.Pickler` and :class:`pickle.Unpickler` involving cyclic references via the +internal memo mapping. diff --git a/Modules/_pickle.c b/Modules/_pickle.c index a3cf34699ba509..227e5378e42285 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -4707,6 +4707,14 @@ Pickler_traverse(PicklerObject *self, visitproc visit, void *arg) Py_VISIT(self->fast_memo); Py_VISIT(self->reducer_override); Py_VISIT(self->buffer_callback); + PyMemoTable *memo = self->memo; + if (memo && memo->mt_table) { + Py_ssize_t i = memo->mt_allocated; + while (--i >= 0) { + Py_VISIT(memo->mt_table[i].me_key); + } + } + return 0; } @@ -7175,6 +7183,13 @@ Unpickler_traverse(UnpicklerObject *self, visitproc visit, void *arg) Py_VISIT(self->stack); Py_VISIT(self->pers_func); Py_VISIT(self->buffers); + PyObject **memo = self->memo; + if (memo) { + Py_ssize_t i = self->memo_size; + while (--i >= 0) { + Py_VISIT(memo[i]); + } + } return 0; } From 2c8b19174274c183eb652932871f60570123fe99 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Mon, 27 Nov 2023 18:36:11 +0000 Subject: [PATCH 081/228] gh-112388: Fix an error that was causing the parser to try to overwrite tokenizer errors (#112410) Signed-off-by: Pablo Galindo --- Lib/test/test_syntax.py | 1 + .../2023-11-25-22-58-49.gh-issue-112388.MU3cIM.rst | 2 ++ Parser/pegen_errors.c | 4 ++++ 3 files changed, 7 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-25-22-58-49.gh-issue-112388.MU3cIM.rst diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index f6fa6495508d2c..e80e95383b897d 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -2349,6 +2349,7 @@ def test_error_string_literal(self): def test_invisible_characters(self): self._check_error('print\x17("Hello")', "invalid non-printable character") + self._check_error(b"with(0,,):\n\x01", "invalid non-printable character") def test_match_call_does_not_raise_syntax_error(self): code = """ diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-25-22-58-49.gh-issue-112388.MU3cIM.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-25-22-58-49.gh-issue-112388.MU3cIM.rst new file mode 100644 index 00000000000000..1c82be2febda4f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-25-22-58-49.gh-issue-112388.MU3cIM.rst @@ -0,0 +1,2 @@ +Fix an error that was causing the parser to try to overwrite tokenizer +errors. Patch by pablo Galindo diff --git a/Parser/pegen_errors.c b/Parser/pegen_errors.c index e2bc3b91c80718..2528d4502b3c0c 100644 --- a/Parser/pegen_errors.c +++ b/Parser/pegen_errors.c @@ -219,6 +219,10 @@ _PyPegen_tokenize_full_source_to_check_for_errors(Parser *p) { void * _PyPegen_raise_error(Parser *p, PyObject *errtype, int use_mark, const char *errmsg, ...) { + // Bail out if we already have an error set. + if (p->error_indicator && PyErr_Occurred()) { + return NULL; + } if (p->fill == 0) { va_list va; va_start(va, errmsg); From 45d648597b1146431bf3d91041e60d7f040e70bf Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Mon, 27 Nov 2023 18:37:48 +0000 Subject: [PATCH 082/228] gh-112387: Fix error positions for decoded strings with backwards tokenize errors (#112409) Signed-off-by: Pablo Galindo --- Lib/test/test_syntax.py | 4 ++++ .../2023-11-25-22-39-44.gh-issue-112387.AbBq5W.rst | 2 ++ Parser/pegen_errors.c | 4 ++++ 3 files changed, 10 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-25-22-39-44.gh-issue-112387.AbBq5W.rst diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index e80e95383b897d..99433df73387d0 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -2334,6 +2334,10 @@ def test_error_parenthesis(self): """ self._check_error(code, "parenthesis '\\)' does not match opening parenthesis '\\['") + # Examples with dencodings + s = b'# coding=latin\n(aaaaaaaaaaaaaaaaa\naaaaaaaaaaa\xb5' + self._check_error(s, "'\(' was never closed") + def test_error_string_literal(self): self._check_error("'blech", r"unterminated string literal \(.*\)$") diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-25-22-39-44.gh-issue-112387.AbBq5W.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-25-22-39-44.gh-issue-112387.AbBq5W.rst new file mode 100644 index 00000000000000..adac11bf4c90a1 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-25-22-39-44.gh-issue-112387.AbBq5W.rst @@ -0,0 +1,2 @@ +Fix error positions for decoded strings with backwards tokenize errors. +Patch by Pablo Galindo diff --git a/Parser/pegen_errors.c b/Parser/pegen_errors.c index 2528d4502b3c0c..20232f3a26a2cc 100644 --- a/Parser/pegen_errors.c +++ b/Parser/pegen_errors.c @@ -282,6 +282,10 @@ get_error_line_from_tokenizer_buffers(Parser *p, Py_ssize_t lineno) Py_ssize_t relative_lineno = p->starting_lineno ? lineno - p->starting_lineno + 1 : lineno; const char* buf_end = p->tok->fp_interactive ? p->tok->interactive_src_end : p->tok->inp; + if (buf_end < cur_line) { + buf_end = cur_line + strlen(cur_line); + } + for (int i = 0; i < relative_lineno - 1; i++) { char *new_line = strchr(cur_line, '\n'); // The assert is here for debug builds but the conditional that From 4dcfd02bed0d7958703ef44baa79a4a98475be2e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 27 Nov 2023 20:57:33 +0200 Subject: [PATCH 083/228] gh-68166: Add support of "vsapi" in ttk.Style.element_create() (GH-111393) --- Doc/library/tkinter.ttk.rst | 60 +++++++++++++- Doc/whatsnew/3.13.rst | 5 ++ Lib/test/test_ttk/test_style.py | 82 +++++++++++++++++++ Lib/test/test_ttk_textonly.py | 32 ++++++-- Lib/tkinter/ttk.py | 55 +++++++------ ...3-11-08-18-53-07.gh-issue-68166.1iTh4Y.rst | 2 + 6 files changed, 204 insertions(+), 32 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-08-18-53-07.gh-issue-68166.1iTh4Y.rst diff --git a/Doc/library/tkinter.ttk.rst b/Doc/library/tkinter.ttk.rst index 5fab1454944989..6e01ec7b291255 100644 --- a/Doc/library/tkinter.ttk.rst +++ b/Doc/library/tkinter.ttk.rst @@ -1391,7 +1391,8 @@ option. If you don't know the class name of a widget, use the method .. method:: element_create(elementname, etype, *args, **kw) Create a new element in the current theme, of the given *etype* which is - expected to be either "image" or "from". + expected to be either "image", "from" or "vsapi". + The latter is only available in Tk 8.6 on Windows. If "image" is used, *args* should contain the default image name followed by statespec/value pairs (this is the imagespec), and *kw* may have the @@ -1439,6 +1440,63 @@ option. If you don't know the class name of a widget, use the method style = ttk.Style(root) style.element_create('plain.background', 'from', 'default') + If "vsapi" is used as the value of *etype*, :meth:`element_create` + will create a new element in the current theme whose visual appearance + is drawn using the Microsoft Visual Styles API which is responsible + for the themed styles on Windows XP and Vista. + *args* is expected to contain the Visual Styles class and part as + given in the Microsoft documentation followed by an optional sequence + of tuples of ttk states and the corresponding Visual Styles API state + value. + *kw* may have the following options: + + padding=padding + Specify the element's interior padding. + *padding* is a list of up to four integers specifying the left, + top, right and bottom padding quantities respectively. + If fewer than four elements are specified, bottom defaults to top, + right defaults to left, and top defaults to left. + In other words, a list of three numbers specify the left, vertical, + and right padding; a list of two numbers specify the horizontal + and the vertical padding; a single number specifies the same + padding all the way around the widget. + This option may not be mixed with any other options. + + margins=padding + Specifies the elements exterior padding. + *padding* is a list of up to four integers specifying the left, top, + right and bottom padding quantities respectively. + This option may not be mixed with any other options. + + width=width + Specifies the width for the element. + If this option is set then the Visual Styles API will not be queried + for the recommended size or the part. + If this option is set then *height* should also be set. + The *width* and *height* options cannot be mixed with the *padding* + or *margins* options. + + height=height + Specifies the height of the element. + See the comments for *width*. + + Example:: + + style = ttk.Style(root) + style.element_create('pin', 'vsapi', 'EXPLORERBAR', 3, [ + ('pressed', '!selected', 3), + ('active', '!selected', 2), + ('pressed', 'selected', 6), + ('active', 'selected', 5), + ('selected', 4), + ('', 1)]) + style.layout('Explorer.Pin', + [('Explorer.Pin.pin', {'sticky': 'news'})]) + pin = ttk.Checkbutton(style='Explorer.Pin') + pin.pack(expand=True, fill='both') + + .. versionchanged:: 3.13 + Added support of the "vsapi" element factory. .. method:: element_names() diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index ec09dfea4aad3c..dad49f43d9090f 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -301,6 +301,11 @@ tkinter :meth:`!tk_busy_current`, and :meth:`!tk_busy_status`. (Contributed by Miguel, klappnase and Serhiy Storchaka in :gh:`72684`.) +* Add support of the "vsapi" element type in + the :meth:`~tkinter.ttk.Style.element_create` method of + :class:`tkinter.ttk.Style`. + (Contributed by Serhiy Storchaka in :gh:`68166`.) + traceback --------- diff --git a/Lib/test/test_ttk/test_style.py b/Lib/test/test_ttk/test_style.py index 52c4b7a0beabd0..9a04a95dc40d65 100644 --- a/Lib/test/test_ttk/test_style.py +++ b/Lib/test/test_ttk/test_style.py @@ -258,6 +258,55 @@ def test_element_create_image_errors(self): with self.assertRaisesRegex(TclError, 'bad option'): style.element_create('block2', 'image', image, spam=1) + def test_element_create_vsapi_1(self): + style = self.style + if 'xpnative' not in style.theme_names(): + self.skipTest("requires 'xpnative' theme") + style.element_create('smallclose', 'vsapi', 'WINDOW', 19, [ + ('disabled', 4), + ('pressed', 3), + ('active', 2), + ('', 1)]) + style.layout('CloseButton', + [('CloseButton.smallclose', {'sticky': 'news'})]) + b = ttk.Button(self.root, style='CloseButton') + b.pack(expand=True, fill='both') + self.assertEqual(b.winfo_reqwidth(), 13) + self.assertEqual(b.winfo_reqheight(), 13) + + def test_element_create_vsapi_2(self): + style = self.style + if 'xpnative' not in style.theme_names(): + self.skipTest("requires 'xpnative' theme") + style.element_create('pin', 'vsapi', 'EXPLORERBAR', 3, [ + ('pressed', '!selected', 3), + ('active', '!selected', 2), + ('pressed', 'selected', 6), + ('active', 'selected', 5), + ('selected', 4), + ('', 1)]) + style.layout('Explorer.Pin', + [('Explorer.Pin.pin', {'sticky': 'news'})]) + pin = ttk.Checkbutton(self.root, style='Explorer.Pin') + pin.pack(expand=True, fill='both') + self.assertEqual(pin.winfo_reqwidth(), 16) + self.assertEqual(pin.winfo_reqheight(), 16) + + def test_element_create_vsapi_3(self): + style = self.style + if 'xpnative' not in style.theme_names(): + self.skipTest("requires 'xpnative' theme") + style.element_create('headerclose', 'vsapi', 'EXPLORERBAR', 2, [ + ('pressed', 3), + ('active', 2), + ('', 1)]) + style.layout('Explorer.CloseButton', + [('Explorer.CloseButton.headerclose', {'sticky': 'news'})]) + b = ttk.Button(self.root, style='Explorer.CloseButton') + b.pack(expand=True, fill='both') + self.assertEqual(b.winfo_reqwidth(), 16) + self.assertEqual(b.winfo_reqheight(), 16) + def test_theme_create(self): style = self.style curr_theme = style.theme_use() @@ -358,6 +407,39 @@ def test_theme_create_image(self): style.theme_use(curr_theme) + def test_theme_create_vsapi(self): + style = self.style + if 'xpnative' not in style.theme_names(): + self.skipTest("requires 'xpnative' theme") + curr_theme = style.theme_use() + new_theme = 'testtheme5' + style.theme_create(new_theme, settings={ + 'pin' : { + 'element create': ['vsapi', 'EXPLORERBAR', 3, [ + ('pressed', '!selected', 3), + ('active', '!selected', 2), + ('pressed', 'selected', 6), + ('active', 'selected', 5), + ('selected', 4), + ('', 1)]], + }, + 'Explorer.Pin' : { + 'layout': [('Explorer.Pin.pin', {'sticky': 'news'})], + }, + }) + + style.theme_use(new_theme) + self.assertIn('pin', style.element_names()) + self.assertEqual(style.layout('Explorer.Pin'), + [('Explorer.Pin.pin', {'sticky': 'nswe'})]) + + pin = ttk.Checkbutton(self.root, style='Explorer.Pin') + pin.pack(expand=True, fill='both') + self.assertEqual(pin.winfo_reqwidth(), 16) + self.assertEqual(pin.winfo_reqheight(), 16) + + style.theme_use(curr_theme) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_ttk_textonly.py b/Lib/test/test_ttk_textonly.py index 96dc179a69ecac..e6525c4d6c6982 100644 --- a/Lib/test/test_ttk_textonly.py +++ b/Lib/test/test_ttk_textonly.py @@ -179,7 +179,7 @@ def test_format_elemcreate(self): # don't format returned values as a tcl script # minimum acceptable for image type self.assertEqual(ttk._format_elemcreate('image', False, 'test'), - ("test ", ())) + ("test", ())) # specifying a state spec self.assertEqual(ttk._format_elemcreate('image', False, 'test', ('', 'a')), ("test {} a", ())) @@ -203,17 +203,19 @@ def test_format_elemcreate(self): # don't format returned values as a tcl script # minimum acceptable for vsapi self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b'), - ("a b ", ())) + ('a', 'b', ('', 1), ())) # now with a state spec with multiple states self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b', - ('a', 'b', 'c')), ("a b {a b} c", ())) + [('a', 'b', 'c')]), ('a', 'b', ('a b', 'c'), ())) # state spec and option self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b', - ('a', 'b'), opt='x'), ("a b a b", ("-opt", "x"))) + [('a', 'b')], opt='x'), ('a', 'b', ('a', 'b'), ("-opt", "x"))) # format returned values as a tcl script # state spec with a multivalue and an option self.assertEqual(ttk._format_elemcreate('vsapi', True, 'a', 'b', - ('a', 'b', [1, 2]), opt='x'), ("{a b {a b} {1 2}}", "-opt x")) + opt='x'), ("a b {{} 1}", "-opt x")) + self.assertEqual(ttk._format_elemcreate('vsapi', True, 'a', 'b', + [('a', 'b', [1, 2])], opt='x'), ("a b {{a b} {1 2}}", "-opt x")) # Testing type = from # from type expects at least a type name @@ -222,9 +224,9 @@ def test_format_elemcreate(self): self.assertEqual(ttk._format_elemcreate('from', False, 'a'), ('a', ())) self.assertEqual(ttk._format_elemcreate('from', False, 'a', 'b'), - ('a', ('b', ))) + ('a', ('b',))) self.assertEqual(ttk._format_elemcreate('from', True, 'a', 'b'), - ('{a}', 'b')) + ('a', 'b')) def test_format_layoutlist(self): @@ -326,6 +328,22 @@ def test_script_from_settings(self): "ttk::style element create thing image {name {state1 state2} val} " "-opt {3 2m}") + vsapi = {'pin': {'element create': + ['vsapi', 'EXPLORERBAR', 3, [ + ('pressed', '!selected', 3), + ('active', '!selected', 2), + ('pressed', 'selected', 6), + ('active', 'selected', 5), + ('selected', 4), + ('', 1)]]}} + self.assertEqual(ttk._script_from_settings(vsapi), + "ttk::style element create pin vsapi EXPLORERBAR 3 {" + "{pressed !selected} 3 " + "{active !selected} 2 " + "{pressed selected} 6 " + "{active selected} 5 " + "selected 4 " + "{} 1} ") def test_tclobj_to_py(self): self.assertEqual( diff --git a/Lib/tkinter/ttk.py b/Lib/tkinter/ttk.py index efeabb7a92c627..5ca938a670831a 100644 --- a/Lib/tkinter/ttk.py +++ b/Lib/tkinter/ttk.py @@ -95,40 +95,47 @@ def _format_mapdict(mapdict, script=False): def _format_elemcreate(etype, script=False, *args, **kw): """Formats args and kw according to the given element factory etype.""" - spec = None + specs = () opts = () - if etype in ("image", "vsapi"): - if etype == "image": # define an element based on an image - # first arg should be the default image name - iname = args[0] - # next args, if any, are statespec/value pairs which is almost - # a mapdict, but we just need the value - imagespec = _join(_mapdict_values(args[1:])) - spec = "%s %s" % (iname, imagespec) - + if etype == "image": # define an element based on an image + # first arg should be the default image name + iname = args[0] + # next args, if any, are statespec/value pairs which is almost + # a mapdict, but we just need the value + imagespec = (iname, *_mapdict_values(args[1:])) + if script: + specs = (imagespec,) else: - # define an element whose visual appearance is drawn using the - # Microsoft Visual Styles API which is responsible for the - # themed styles on Windows XP and Vista. - # Availability: Tk 8.6, Windows XP and Vista. - class_name, part_id = args[:2] - statemap = _join(_mapdict_values(args[2:])) - spec = "%s %s %s" % (class_name, part_id, statemap) + specs = (_join(imagespec),) + opts = _format_optdict(kw, script) + if etype == "vsapi": + # define an element whose visual appearance is drawn using the + # Microsoft Visual Styles API which is responsible for the + # themed styles on Windows XP and Vista. + # Availability: Tk 8.6, Windows XP and Vista. + if len(args) < 3: + class_name, part_id = args + statemap = (((), 1),) + else: + class_name, part_id, statemap = args + specs = (class_name, part_id, tuple(_mapdict_values(statemap))) opts = _format_optdict(kw, script) elif etype == "from": # clone an element # it expects a themename and optionally an element to clone from, # otherwise it will clone {} (empty element) - spec = args[0] # theme name + specs = (args[0],) # theme name if len(args) > 1: # elementfrom specified opts = (_format_optvalue(args[1], script),) if script: - spec = '{%s}' % spec + specs = _join(specs) opts = ' '.join(opts) + return specs, opts + else: + return *specs, opts - return spec, opts def _format_layoutlist(layout, indent=0, indent_size=2): """Formats a layout list so we can pass the result to ttk::style @@ -214,10 +221,10 @@ def _script_from_settings(settings): elemargs = eopts[1:argc] elemkw = eopts[argc] if argc < len(eopts) and eopts[argc] else {} - spec, opts = _format_elemcreate(etype, True, *elemargs, **elemkw) + specs, eopts = _format_elemcreate(etype, True, *elemargs, **elemkw) script.append("ttk::style element create %s %s %s %s" % ( - name, etype, spec, opts)) + name, etype, specs, eopts)) return '\n'.join(script) @@ -434,9 +441,9 @@ def layout(self, style, layoutspec=None): def element_create(self, elementname, etype, *args, **kw): """Create a new element in the current theme of given etype.""" - spec, opts = _format_elemcreate(etype, False, *args, **kw) + *specs, opts = _format_elemcreate(etype, False, *args, **kw) self.tk.call(self._name, "element", "create", elementname, etype, - spec, *opts) + *specs, *opts) def element_names(self): diff --git a/Misc/NEWS.d/next/Library/2023-11-08-18-53-07.gh-issue-68166.1iTh4Y.rst b/Misc/NEWS.d/next/Library/2023-11-08-18-53-07.gh-issue-68166.1iTh4Y.rst new file mode 100644 index 00000000000000..30379b8fa1afaf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-08-18-53-07.gh-issue-68166.1iTh4Y.rst @@ -0,0 +1,2 @@ +Add support of the "vsapi" element type in +:meth:`tkinter.ttk.Style.element_create`. From 8f71b349de1ff2b11223ff7a8241c62a5a932339 Mon Sep 17 00:00:00 2001 From: apaz Date: Mon, 27 Nov 2023 15:13:27 -0600 Subject: [PATCH 084/228] gh-112217: Add check to call result for `do_raise()` where cause is a type. (#112216) --- Lib/test/test_raise.py | 14 ++++++++++++++ .../2023-11-27-18-55-30.gh-issue-112217.SwFLMj.rst | 1 + Python/ceval.c | 7 +++++++ 3 files changed, 22 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-27-18-55-30.gh-issue-112217.SwFLMj.rst diff --git a/Lib/test/test_raise.py b/Lib/test/test_raise.py index 5936d7535edd5f..6d26a61bee4292 100644 --- a/Lib/test/test_raise.py +++ b/Lib/test/test_raise.py @@ -185,6 +185,20 @@ def test_class_cause(self): else: self.fail("No exception raised") + def test_class_cause_nonexception_result(self): + class ConstructsNone(BaseException): + @classmethod + def __new__(*args, **kwargs): + return None + try: + raise IndexError from ConstructsNone + except TypeError as e: + self.assertIn("should have returned an instance of BaseException", str(e)) + except IndexError: + self.fail("Wrong kind of exception raised") + else: + self.fail("No exception raised") + def test_instance_cause(self): cause = KeyError() try: diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-27-18-55-30.gh-issue-112217.SwFLMj.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-27-18-55-30.gh-issue-112217.SwFLMj.rst new file mode 100644 index 00000000000000..d4efbab6b2d128 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-27-18-55-30.gh-issue-112217.SwFLMj.rst @@ -0,0 +1 @@ +Add check for the type of ``__cause__`` returned from calling the type ``T`` in ``raise from T``. diff --git a/Python/ceval.c b/Python/ceval.c index 76ab5df42f63db..def75fd114cb86 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1920,6 +1920,13 @@ do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause) fixed_cause = _PyObject_CallNoArgs(cause); if (fixed_cause == NULL) goto raise_error; + if (!PyExceptionInstance_Check(fixed_cause)) { + _PyErr_Format(tstate, PyExc_TypeError, + "calling %R should have returned an instance of " + "BaseException, not %R", + cause, Py_TYPE(fixed_cause)); + goto raise_error; + } Py_DECREF(cause); } else if (PyExceptionInstance_Check(cause)) { From b90a5cf11cdb69e60aed7be732e80113bca7bbe4 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Mon, 27 Nov 2023 14:11:40 -0900 Subject: [PATCH 085/228] gh-99367: Do not mangle sys.path[0] in pdb if safe_path is set (#111762) Co-authored-by: Christian Walther --- Doc/whatsnew/3.13.rst | 5 +++ Lib/pdb.py | 6 ++-- Lib/test/test_pdb.py | 33 ++++++++++++++++--- ...3-11-05-20-09-27.gh-issue-99367.HLaWKo.rst | 1 + 4 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-05-20-09-27.gh-issue-99367.HLaWKo.rst diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index dad49f43d9090f..bf6a70f2009b8e 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -285,6 +285,11 @@ pdb identified and executed. (Contributed by Tian Gao in :gh:`108464`.) +* ``sys.path[0]`` will no longer be replaced by the directory of the script + being debugged when ``sys.flags.safe_path`` is set (via the :option:`-P` + command line option or :envvar:`PYTHONSAFEPATH` environment variable). + (Contributed by Tian Gao and Christian Walther in :gh:`111762`.) + sqlite3 ------- diff --git a/Lib/pdb.py b/Lib/pdb.py index ed78d749a47fa8..9d124189df11cf 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -142,8 +142,10 @@ def check(self): print('Error:', self.orig, 'is a directory') sys.exit(1) - # Replace pdb's dir with script's dir in front of module search path. - sys.path[0] = os.path.dirname(self) + # If safe_path(-P) is not set, sys.path[0] is the directory + # of pdb, and we should replace it with the directory of the script + if not sys.flags.safe_path: + sys.path[0] = os.path.dirname(self) @property def filename(self): diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 67a4053a2ac8bc..2a279ca869e9c7 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -2520,15 +2520,21 @@ def tearDown(self): @unittest.skipIf(sys.flags.safe_path, 'PYTHONSAFEPATH changes default sys.path') - def _run_pdb(self, pdb_args, commands, expected_returncode=0): + def _run_pdb(self, pdb_args, commands, + expected_returncode=0, + extra_env=None): self.addCleanup(os_helper.rmtree, '__pycache__') cmd = [sys.executable, '-m', 'pdb'] + pdb_args + if extra_env is not None: + env = os.environ | extra_env + else: + env = os.environ with subprocess.Popen( cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, - env = {**os.environ, 'PYTHONIOENCODING': 'utf-8'} + env = {**env, 'PYTHONIOENCODING': 'utf-8'} ) as proc: stdout, stderr = proc.communicate(str.encode(commands)) stdout = stdout and bytes.decode(stdout) @@ -2540,13 +2546,15 @@ def _run_pdb(self, pdb_args, commands, expected_returncode=0): ) return stdout, stderr - def run_pdb_script(self, script, commands, expected_returncode=0): + def run_pdb_script(self, script, commands, + expected_returncode=0, + extra_env=None): """Run 'script' lines with pdb and the pdb 'commands'.""" filename = 'main.py' with open(filename, 'w') as f: f.write(textwrap.dedent(script)) self.addCleanup(os_helper.unlink, filename) - return self._run_pdb([filename], commands, expected_returncode) + return self._run_pdb([filename], commands, expected_returncode, extra_env) def run_pdb_module(self, script, commands): """Runs the script code as part of a module""" @@ -3131,6 +3139,23 @@ def test_issue42384_symlink(self): self.assertEqual(stdout.split('\n')[2].rstrip('\r'), expected) + def test_safe_path(self): + """ With safe_path set, pdb should not mangle sys.path[0]""" + + script = textwrap.dedent(""" + import sys + import random + print('sys.path[0] is', sys.path[0]) + """) + commands = 'c\n' + + + with os_helper.temp_cwd() as cwd: + stdout, _ = self.run_pdb_script(script, commands, extra_env={'PYTHONSAFEPATH': '1'}) + + unexpected = f'sys.path[0] is {os.path.realpath(cwd)}' + self.assertNotIn(unexpected, stdout) + def test_issue42383(self): with os_helper.temp_cwd() as cwd: with open('foo.py', 'w') as f: diff --git a/Misc/NEWS.d/next/Library/2023-11-05-20-09-27.gh-issue-99367.HLaWKo.rst b/Misc/NEWS.d/next/Library/2023-11-05-20-09-27.gh-issue-99367.HLaWKo.rst new file mode 100644 index 00000000000000..0920da221e423f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-05-20-09-27.gh-issue-99367.HLaWKo.rst @@ -0,0 +1 @@ +Do not mangle ``sys.path[0]`` in :mod:`pdb` if safe_path is set From 562d7149c6944fb9e4c7be80664b2f2d5a12a3ea Mon Sep 17 00:00:00 2001 From: Eugene Toder Date: Mon, 27 Nov 2023 18:42:37 -0500 Subject: [PATCH 086/228] Correct documentation for AF_PACKET (#112339) Protocol in the address tuple should *not* be in the network-byte-order, because it is converted internally[1]. [1] https://github.com/python/cpython/blob/89ddea4886942b0c27a778a0ad3f0d5ac5f518f0/Modules/socketmodule.c#L2144 network byte order doesn't make sense for a python level int anyways. It's a fixed size C serialization concept. --- Doc/library/socket.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index e36fc17f89de24..e0a75304ef1606 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -185,7 +185,7 @@ created. Socket addresses are represented as follows: .. versionadded:: 3.7 - :const:`AF_PACKET` is a low-level interface directly to network devices. - The packets are represented by the tuple + The addresses are represented by the tuple ``(ifname, proto[, pkttype[, hatype[, addr]]])`` where: - *ifname* - String specifying the device name. @@ -193,7 +193,6 @@ created. Socket addresses are represented as follows: May be :data:`ETH_P_ALL` to capture all protocols, one of the :ref:`ETHERTYPE_* constants ` or any other Ethernet protocol number. - Value must be in network-byte-order. - *pkttype* - Optional integer specifying the packet type: - ``PACKET_HOST`` (the default) - Packet addressed to the local host. From cf2054059c08ef1c5546f24874191f341dc94eb9 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 28 Nov 2023 00:09:59 +0000 Subject: [PATCH 087/228] gh-112414: Add additional unit tests for calling `repr()` on a namespace package (#112475) Co-authored-by: Eric Snow --- .../test_importlib/import_/test___loader__.py | 4 --- Lib/test/test_module/__init__.py | 30 +++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_importlib/import_/test___loader__.py b/Lib/test/test_importlib/import_/test___loader__.py index c6996a42534676..858b37effc64bd 100644 --- a/Lib/test/test_importlib/import_/test___loader__.py +++ b/Lib/test/test_importlib/import_/test___loader__.py @@ -23,10 +23,6 @@ def test___loader__(self): with util.uncache('blah'), util.import_state(meta_path=[loader]): module = self.__import__('blah') self.assertEqual(loader, module.__loader__) - expected_repr_pattern = ( - r"\)>" - ) - self.assertRegex(repr(module), expected_repr_pattern) (Frozen_SpecTests, diff --git a/Lib/test/test_module/__init__.py b/Lib/test/test_module/__init__.py index db2133a9e8d17b..d49c44df4d839d 100644 --- a/Lib/test/test_module/__init__.py +++ b/Lib/test/test_module/__init__.py @@ -1,4 +1,5 @@ # Test the module type +import importlib.machinery import unittest import weakref from test.support import gc_collect @@ -264,6 +265,35 @@ def test_module_repr_source(self): self.assertEqual(r[-len(ends_with):], ends_with, '{!r} does not end with {!r}'.format(r, ends_with)) + def test_module_repr_with_namespace_package(self): + m = ModuleType('foo') + loader = importlib.machinery.NamespaceLoader('foo', ['bar'], 'baz') + spec = importlib.machinery.ModuleSpec('foo', loader) + m.__loader__ = loader + m.__spec__ = spec + self.assertEqual(repr(m), "") + + def test_module_repr_with_namespace_package_and_custom_loader(self): + m = ModuleType('foo') + loader = BareLoader() + spec = importlib.machinery.ModuleSpec('foo', loader) + m.__loader__ = loader + m.__spec__ = spec + expected_repr_pattern = r"\)>" + self.assertRegex(repr(m), expected_repr_pattern) + self.assertNotIn('from', repr(m)) + + def test_module_repr_with_fake_namespace_package(self): + m = ModuleType('foo') + loader = BareLoader() + loader._path = ['spam'] + spec = importlib.machinery.ModuleSpec('foo', loader) + m.__loader__ = loader + m.__spec__ = spec + expected_repr_pattern = r"\)>" + self.assertRegex(repr(m), expected_repr_pattern) + self.assertNotIn('from', repr(m)) + def test_module_finalization_at_shutdown(self): # Module globals and builtins should still be available during shutdown rc, out, err = assert_python_ok("-c", "from test.test_module import final_a") From 2e632fa07d13a58be62f59be4e656ad58b378f9b Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 28 Nov 2023 00:15:23 +0000 Subject: [PATCH 088/228] Docs: fix markup for `importlib.machinery.NamespaceLoader` (#112479) --- Doc/library/importlib.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index fc954724bb72fe..2402bc5cd3ee2c 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1145,7 +1145,7 @@ find and load modules. .. versionadded:: 3.4 -.. class:: NamespaceLoader(name, path, path_finder): +.. class:: NamespaceLoader(name, path, path_finder) A concrete implementation of :class:`importlib.abc.InspectLoader` for namespace packages. This is an alias for a private class and is only made From 154f099e611cea74daa755c77df3b8003861cc76 Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Tue, 28 Nov 2023 12:58:53 +1100 Subject: [PATCH 089/228] gh-112292 : Catch import error conditions with readline hooks (gh-112313) Prevents a segmentation fault in registered hooks for the readline library, but only when the readline module is loaded inside an isolated sub interpreter. The module is single-phase init so loading it fails, but not until the module init function has already run, where the readline hooks get registered. The readlinestate_global macro was error-prone to PyImport_FindModule returning NULL and crashing in about 18 places. I could reproduce 1 easily, but this PR replaces the macro with a function and adds error conditions to the other functions. --- ...-11-22-19-43-54.gh-issue-112292.5nDU87.rst | 2 + Modules/readline.c | 91 ++++++++++++++----- 2 files changed, 71 insertions(+), 22 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-22-19-43-54.gh-issue-112292.5nDU87.rst diff --git a/Misc/NEWS.d/next/Library/2023-11-22-19-43-54.gh-issue-112292.5nDU87.rst b/Misc/NEWS.d/next/Library/2023-11-22-19-43-54.gh-issue-112292.5nDU87.rst new file mode 100644 index 00000000000000..8345e33791cde0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-22-19-43-54.gh-issue-112292.5nDU87.rst @@ -0,0 +1,2 @@ +Fix a crash in :mod:`readline` when imported from a sub interpreter. Patch +by Anthony Shaw diff --git a/Modules/readline.c b/Modules/readline.c index fde552d124bc77..209ac8bbcfbe78 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -147,8 +147,19 @@ readline_free(void *m) static PyModuleDef readlinemodule; -#define readlinestate_global ((readlinestate *)PyModule_GetState(PyState_FindModule(&readlinemodule))) - +static inline readlinestate* +get_hook_module_state(void) +{ + PyObject *mod = PyState_FindModule(&readlinemodule); + if (mod == NULL){ + PyErr_Clear(); + return NULL; + } + Py_INCREF(mod); + readlinestate *state = get_readline_state(mod); + Py_DECREF(mod); + return state; +} /* Convert to/from multibyte C strings */ @@ -438,14 +449,15 @@ readline_set_completion_display_matches_hook_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=516e5cb8db75a328 input=4f0bfd5ab0179a26]*/ { + readlinestate *state = get_readline_state(module); PyObject *result = set_hook("completion_display_matches_hook", - &readlinestate_global->completion_display_matches_hook, + &state->completion_display_matches_hook, function); #ifdef HAVE_RL_COMPLETION_DISPLAY_MATCHES_HOOK /* We cannot set this hook globally, since it replaces the default completion display. */ rl_completion_display_matches_hook = - readlinestate_global->completion_display_matches_hook ? + state->completion_display_matches_hook ? #if defined(HAVE_RL_COMPDISP_FUNC_T) (rl_compdisp_func_t *)on_completion_display_matches_hook : 0; #else @@ -472,7 +484,8 @@ static PyObject * readline_set_startup_hook_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=02cd0e0c4fa082ad input=7783b4334b26d16d]*/ { - return set_hook("startup_hook", &readlinestate_global->startup_hook, + readlinestate *state = get_readline_state(module); + return set_hook("startup_hook", &state->startup_hook, function); } @@ -497,7 +510,8 @@ static PyObject * readline_set_pre_input_hook_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=fe1a96505096f464 input=4f3eaeaf7ce1fdbe]*/ { - return set_hook("pre_input_hook", &readlinestate_global->pre_input_hook, + readlinestate *state = get_readline_state(module); + return set_hook("pre_input_hook", &state->pre_input_hook, function); } #endif @@ -530,7 +544,8 @@ static PyObject * readline_get_begidx_impl(PyObject *module) /*[clinic end generated code: output=362616ee8ed1b2b1 input=e083b81c8eb4bac3]*/ { - return Py_NewRef(readlinestate_global->begidx); + readlinestate *state = get_readline_state(module); + return Py_NewRef(state->begidx); } /* Get the ending index for the scope of the tab-completion */ @@ -545,7 +560,8 @@ static PyObject * readline_get_endidx_impl(PyObject *module) /*[clinic end generated code: output=7f763350b12d7517 input=d4c7e34a625fd770]*/ { - return Py_NewRef(readlinestate_global->endidx); + readlinestate *state = get_readline_state(module); + return Py_NewRef(state->endidx); } /* Set the tab-completion word-delimiters that readline uses */ @@ -772,7 +788,8 @@ static PyObject * readline_set_completer_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=171a2a60f81d3204 input=51e81e13118eb877]*/ { - return set_hook("completer", &readlinestate_global->completer, function); + readlinestate *state = get_readline_state(module); + return set_hook("completer", &state->completer, function); } /*[clinic input] @@ -785,10 +802,11 @@ static PyObject * readline_get_completer_impl(PyObject *module) /*[clinic end generated code: output=6e6bbd8226d14475 input=6457522e56d70d13]*/ { - if (readlinestate_global->completer == NULL) { + readlinestate *state = get_readline_state(module); + if (state->completer == NULL) { Py_RETURN_NONE; } - return Py_NewRef(readlinestate_global->completer); + return Py_NewRef(state->completer); } /* Private function to get current length of history. XXX It may be @@ -1026,7 +1044,12 @@ on_startup_hook(void) { int r; PyGILState_STATE gilstate = PyGILState_Ensure(); - r = on_hook(readlinestate_global->startup_hook); + readlinestate *state = get_hook_module_state(); + if (state == NULL) { + PyGILState_Release(gilstate); + return -1; + } + r = on_hook(state->startup_hook); PyGILState_Release(gilstate); return r; } @@ -1043,7 +1066,12 @@ on_pre_input_hook(void) { int r; PyGILState_STATE gilstate = PyGILState_Ensure(); - r = on_hook(readlinestate_global->pre_input_hook); + readlinestate *state = get_hook_module_state(); + if (state == NULL) { + PyGILState_Release(gilstate); + return -1; + } + r = on_hook(state->pre_input_hook); PyGILState_Release(gilstate); return r; } @@ -1060,6 +1088,11 @@ on_completion_display_matches_hook(char **matches, int i; PyObject *sub, *m=NULL, *s=NULL, *r=NULL; PyGILState_STATE gilstate = PyGILState_Ensure(); + readlinestate *state = get_hook_module_state(); + if (state == NULL) { + PyGILState_Release(gilstate); + return; + } m = PyList_New(num_matches); if (m == NULL) goto error; @@ -1070,7 +1103,7 @@ on_completion_display_matches_hook(char **matches, PyList_SET_ITEM(m, i, s); } sub = decode(matches[0]); - r = PyObject_CallFunction(readlinestate_global->completion_display_matches_hook, + r = PyObject_CallFunction(state->completion_display_matches_hook, "NNi", sub, m, max_length); m=NULL; @@ -1118,12 +1151,17 @@ static char * on_completion(const char *text, int state) { char *result = NULL; - if (readlinestate_global->completer != NULL) { + PyGILState_STATE gilstate = PyGILState_Ensure(); + readlinestate *module_state = get_hook_module_state(); + if (module_state == NULL) { + PyGILState_Release(gilstate); + return NULL; + } + if (module_state->completer != NULL) { PyObject *r = NULL, *t; - PyGILState_STATE gilstate = PyGILState_Ensure(); rl_attempted_completion_over = 1; t = decode(text); - r = PyObject_CallFunction(readlinestate_global->completer, "Ni", t, state); + r = PyObject_CallFunction(module_state->completer, "Ni", t, state); if (r == NULL) goto error; if (r == Py_None) { @@ -1145,6 +1183,7 @@ on_completion(const char *text, int state) PyGILState_Release(gilstate); return result; } + PyGILState_Release(gilstate); return result; } @@ -1160,6 +1199,7 @@ flex_complete(const char *text, int start, int end) size_t start_size, end_size; wchar_t *s; PyGILState_STATE gilstate = PyGILState_Ensure(); + readlinestate *state = get_hook_module_state(); #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER rl_completion_append_character ='\0'; #endif @@ -1187,10 +1227,12 @@ flex_complete(const char *text, int start, int end) end = start + (int)end_size; done: - Py_XDECREF(readlinestate_global->begidx); - Py_XDECREF(readlinestate_global->endidx); - readlinestate_global->begidx = PyLong_FromLong((long) start); - readlinestate_global->endidx = PyLong_FromLong((long) end); + if (state) { + Py_XDECREF(state->begidx); + Py_XDECREF(state->endidx); + state->begidx = PyLong_FromLong((long) start); + state->endidx = PyLong_FromLong((long) end); + } result = completion_matches((char *)text, *on_completion); PyGILState_Release(gilstate); return result; @@ -1511,12 +1553,17 @@ PyInit_readline(void) } mod_state = (readlinestate *) PyModule_GetState(m); + if (mod_state == NULL){ + goto error; + } PyOS_ReadlineFunctionPointer = call_readline; if (setup_readline(mod_state) < 0) { PyErr_NoMemory(); goto error; } - + if (PyErr_Occurred()){ + goto error; + } return m; error: From ac4b44266d61651aea5928ce7d3fae4de226f83d Mon Sep 17 00:00:00 2001 From: Radislav Chugunov <52372310+chgnrdv@users.noreply.github.com> Date: Tue, 28 Nov 2023 06:27:39 +0300 Subject: [PATCH 090/228] gh-112071: Make `_random.Random` methods thread-safe in `--disable-gil` builds (gh-112128) Co-authored-by: Donghee Na --- Modules/_randommodule.c | 18 +++++++++------ Modules/clinic/_randommodule.c.h | 38 +++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/Modules/_randommodule.c b/Modules/_randommodule.c index 514bec16a347f4..4403e1d132c057 100644 --- a/Modules/_randommodule.c +++ b/Modules/_randommodule.c @@ -175,6 +175,7 @@ genrand_uint32(RandomObject *self) */ /*[clinic input] +@critical_section _random.Random.random self: self(type="RandomObject *") @@ -184,7 +185,7 @@ random() -> x in the interval [0, 1). static PyObject * _random_Random_random_impl(RandomObject *self) -/*[clinic end generated code: output=117ff99ee53d755c input=afb2a59cbbb00349]*/ +/*[clinic end generated code: output=117ff99ee53d755c input=26492e52d26e8b7b]*/ { uint32_t a=genrand_uint32(self)>>5, b=genrand_uint32(self)>>6; return PyFloat_FromDouble((a*67108864.0+b)*(1.0/9007199254740992.0)); @@ -368,6 +369,7 @@ random_seed(RandomObject *self, PyObject *arg) } /*[clinic input] +@critical_section _random.Random.seed self: self(type="RandomObject *") @@ -382,7 +384,7 @@ of the current time and the process identifier. static PyObject * _random_Random_seed_impl(RandomObject *self, PyObject *n) -/*[clinic end generated code: output=0fad1e16ba883681 input=78d6ef0d52532a54]*/ +/*[clinic end generated code: output=0fad1e16ba883681 input=46d01d2ba938c7b1]*/ { if (random_seed(self, n) < 0) { return NULL; @@ -391,6 +393,7 @@ _random_Random_seed_impl(RandomObject *self, PyObject *n) } /*[clinic input] +@critical_section _random.Random.getstate self: self(type="RandomObject *") @@ -400,7 +403,7 @@ getstate() -> tuple containing the current state. static PyObject * _random_Random_getstate_impl(RandomObject *self) -/*[clinic end generated code: output=bf6cef0c092c7180 input=b937a487928c0e89]*/ +/*[clinic end generated code: output=bf6cef0c092c7180 input=b6621f31eb639694]*/ { PyObject *state; PyObject *element; @@ -428,6 +431,7 @@ _random_Random_getstate_impl(RandomObject *self) /*[clinic input] +@critical_section _random.Random.setstate self: self(type="RandomObject *") @@ -438,8 +442,8 @@ setstate(state) -> None. Restores generator state. [clinic start generated code]*/ static PyObject * -_random_Random_setstate(RandomObject *self, PyObject *state) -/*[clinic end generated code: output=fd1c3cd0037b6681 input=b3b4efbb1bc66af8]*/ +_random_Random_setstate_impl(RandomObject *self, PyObject *state) +/*[clinic end generated code: output=babfc2c2eac6b027 input=358e898ec07469b7]*/ { int i; unsigned long element; @@ -479,7 +483,7 @@ _random_Random_setstate(RandomObject *self, PyObject *state) } /*[clinic input] - +@critical_section _random.Random.getrandbits self: self(type="RandomObject *") @@ -491,7 +495,7 @@ getrandbits(k) -> x. Generates an int with k random bits. static PyObject * _random_Random_getrandbits_impl(RandomObject *self, int k) -/*[clinic end generated code: output=b402f82a2158887f input=8c0e6396dd176fc0]*/ +/*[clinic end generated code: output=b402f82a2158887f input=87603cd60f79f730]*/ { int i, words; uint32_t r; diff --git a/Modules/clinic/_randommodule.c.h b/Modules/clinic/_randommodule.c.h index 757e49e23cacfb..6193acac67e7ac 100644 --- a/Modules/clinic/_randommodule.c.h +++ b/Modules/clinic/_randommodule.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(_random_Random_random__doc__, @@ -19,7 +20,13 @@ _random_Random_random_impl(RandomObject *self); static PyObject * _random_Random_random(RandomObject *self, PyObject *Py_UNUSED(ignored)) { - return _random_Random_random_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _random_Random_random_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_random_Random_seed__doc__, @@ -51,7 +58,9 @@ _random_Random_seed(RandomObject *self, PyObject *const *args, Py_ssize_t nargs) } n = args[0]; skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _random_Random_seed_impl(self, n); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -72,7 +81,13 @@ _random_Random_getstate_impl(RandomObject *self); static PyObject * _random_Random_getstate(RandomObject *self, PyObject *Py_UNUSED(ignored)) { - return _random_Random_getstate_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _random_Random_getstate_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_random_Random_setstate__doc__, @@ -84,6 +99,21 @@ PyDoc_STRVAR(_random_Random_setstate__doc__, #define _RANDOM_RANDOM_SETSTATE_METHODDEF \ {"setstate", (PyCFunction)_random_Random_setstate, METH_O, _random_Random_setstate__doc__}, +static PyObject * +_random_Random_setstate_impl(RandomObject *self, PyObject *state); + +static PyObject * +_random_Random_setstate(RandomObject *self, PyObject *state) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _random_Random_setstate_impl(self, state); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(_random_Random_getrandbits__doc__, "getrandbits($self, k, /)\n" "--\n" @@ -106,9 +136,11 @@ _random_Random_getrandbits(RandomObject *self, PyObject *arg) if (k == -1 && PyErr_Occurred()) { goto exit; } + Py_BEGIN_CRITICAL_SECTION(self); return_value = _random_Random_getrandbits_impl(self, k); + Py_END_CRITICAL_SECTION(); exit: return return_value; } -/*[clinic end generated code: output=5c800a28c2d7b9d1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=bf49ece1d341b1b6 input=a9049054013a1b77]*/ From 2df26d83486b8f9ac6b7df2a9a4669508aa61983 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Mon, 27 Nov 2023 21:23:23 -0900 Subject: [PATCH 091/228] gh-112105: Make completer delims work on libedit (gh-112106) --- Lib/test/test_readline.py | 20 +++++++++++++++++++ ...-11-15-04-53-37.gh-issue-112105.I3RcVN.rst | 1 + Modules/readline.c | 16 +++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-11-15-04-53-37.gh-issue-112105.I3RcVN.rst diff --git a/Lib/test/test_readline.py b/Lib/test/test_readline.py index 835280f2281cde..6c2726d3209ecf 100644 --- a/Lib/test/test_readline.py +++ b/Lib/test/test_readline.py @@ -5,6 +5,7 @@ import os import sys import tempfile +import textwrap import unittest from test.support import verbose from test.support.import_helper import import_module @@ -163,6 +164,25 @@ def test_auto_history_disabled(self): # end, so don't expect it in the output. self.assertIn(b"History length: 0", output) + def test_set_complete_delims(self): + script = textwrap.dedent(""" + import readline + def complete(text, state): + if state == 0 and text == "$": + return "$complete" + return None + if "libedit" in getattr(readline, "__doc__", ""): + readline.parse_and_bind(r'bind "\\t" rl_complete') + else: + readline.parse_and_bind(r'"\\t": complete') + readline.set_completer_delims(" \\t\\n") + readline.set_completer(complete) + print(input()) + """) + + output = run_pty(script, input=b"$\t\n") + self.assertIn(b"$complete", output) + def test_nonascii(self): loc = locale.setlocale(locale.LC_CTYPE, None) if loc in ('C', 'POSIX'): diff --git a/Misc/NEWS.d/next/Library/2023-11-15-04-53-37.gh-issue-112105.I3RcVN.rst b/Misc/NEWS.d/next/Library/2023-11-15-04-53-37.gh-issue-112105.I3RcVN.rst new file mode 100644 index 00000000000000..4243dcb190434f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-15-04-53-37.gh-issue-112105.I3RcVN.rst @@ -0,0 +1 @@ +Make :func:`readline.set_completer_delims` work with libedit diff --git a/Modules/readline.c b/Modules/readline.c index 209ac8bbcfbe78..eb9a3d4693ee90 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -592,6 +592,13 @@ readline_set_completer_delims(PyObject *module, PyObject *string) if (break_chars) { free(completer_word_break_characters); completer_word_break_characters = break_chars; +#ifdef WITH_EDITLINE + rl_basic_word_break_characters = break_chars; +#else + if (using_libedit_emulation) { + rl_basic_word_break_characters = break_chars; + } +#endif rl_completer_word_break_characters = break_chars; Py_RETURN_NONE; } @@ -1309,6 +1316,15 @@ setup_readline(readlinestate *mod_state) completer_word_break_characters = strdup(" \t\n`~!@#$%^&*()-=+[{]}\\|;:'\",<>/?"); /* All nonalphanums except '.' */ +#ifdef WITH_EDITLINE + // libedit uses rl_basic_word_break_characters instead of + // rl_completer_word_break_characters as complete delimiter + rl_basic_word_break_characters = completer_word_break_characters; +#else + if (using_libedit_emulation) { + rl_basic_word_break_characters = completer_word_break_characters; + } +#endif rl_completer_word_break_characters = completer_word_break_characters; mod_state->begidx = PyLong_FromLong(0L); From 2c68011780bd68463f5183601ea9c10af368dff6 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 28 Nov 2023 08:03:25 +0000 Subject: [PATCH 092/228] gh-112332: Deprecate TracebackException.exc_type, add exc_type_str. (#112333) --- Doc/library/traceback.rst | 8 +++ Doc/whatsnew/3.13.rst | 11 ++++ Lib/test/test_traceback.py | 51 ++++++++++++++++--- Lib/traceback.py | 50 +++++++++++++----- ...-11-23-10-41-21.gh-issue-112332.rhTBaa.rst | 2 + 5 files changed, 102 insertions(+), 20 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-23-10-41-21.gh-issue-112332.rhTBaa.rst diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst index 408da7fc5f0645..80dda5ec520d7a 100644 --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -287,6 +287,14 @@ capture data for later printing in a lightweight fashion. The class of the original traceback. + .. deprecated:: 3.13 + + .. attribute:: exc_type_str + + String display of the class of the original exception. + + .. versionadded:: 3.13 + .. attribute:: filename For syntax errors - the file name where the error occurred. diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index bf6a70f2009b8e..198ea3a4b57bde 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -318,6 +318,12 @@ traceback to format the nested exceptions of a :exc:`BaseExceptionGroup` instance, recursively. (Contributed by Irit Katriel in :gh:`105292`.) +* Add the field *exc_type_str* to :class:`~traceback.TracebackException`, which + holds a string display of the *exc_type*. Deprecate the field *exc_type* + which holds the type object itself. Add parameter *save_exc_type* (default + ``True``) to indicate whether ``exc_type`` should be saved. + (Contributed by Irit Katriel in :gh:`112332`.) + typing ------ @@ -377,6 +383,11 @@ Deprecated security and functionality bugs. This includes removal of the ``--cgi`` flag to the ``python -m http.server`` command line in 3.15. +* :mod:`traceback`: + + * The field *exc_type* of :class:`traceback.TracebackException` is + deprecated. Use *exc_type_str* instead. + * :mod:`typing`: * Creating a :class:`typing.NamedTuple` class using keyword arguments to denote diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index b43dca6f640b9a..c58d979bdd0115 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -2715,9 +2715,9 @@ def __repr__(self) -> str: class TestTracebackException(unittest.TestCase): - def test_smoke(self): + def do_test_smoke(self, exc, expected_type_str): try: - 1/0 + raise exc except Exception as e: exc_obj = e exc = traceback.TracebackException.from_exception(e) @@ -2727,9 +2727,23 @@ def test_smoke(self): self.assertEqual(None, exc.__context__) self.assertEqual(False, exc.__suppress_context__) self.assertEqual(expected_stack, exc.stack) - self.assertEqual(type(exc_obj), exc.exc_type) + with self.assertWarns(DeprecationWarning): + self.assertEqual(type(exc_obj), exc.exc_type) + self.assertEqual(expected_type_str, exc.exc_type_str) self.assertEqual(str(exc_obj), str(exc)) + def test_smoke_builtin(self): + self.do_test_smoke(ValueError(42), 'ValueError') + + def test_smoke_user_exception(self): + class MyException(Exception): + pass + + self.do_test_smoke( + MyException('bad things happened'), + ('test.test_traceback.TestTracebackException.' + 'test_smoke_user_exception..MyException')) + def test_from_exception(self): # Check all the parameters are accepted. def foo(): @@ -2750,7 +2764,9 @@ def foo(): self.assertEqual(None, exc.__context__) self.assertEqual(False, exc.__suppress_context__) self.assertEqual(expected_stack, exc.stack) - self.assertEqual(type(exc_obj), exc.exc_type) + with self.assertWarns(DeprecationWarning): + self.assertEqual(type(exc_obj), exc.exc_type) + self.assertEqual(type(exc_obj).__name__, exc.exc_type_str) self.assertEqual(str(exc_obj), str(exc)) def test_cause(self): @@ -2772,7 +2788,9 @@ def test_cause(self): self.assertEqual(exc_context, exc.__context__) self.assertEqual(True, exc.__suppress_context__) self.assertEqual(expected_stack, exc.stack) - self.assertEqual(type(exc_obj), exc.exc_type) + with self.assertWarns(DeprecationWarning): + self.assertEqual(type(exc_obj), exc.exc_type) + self.assertEqual(type(exc_obj).__name__, exc.exc_type_str) self.assertEqual(str(exc_obj), str(exc)) def test_context(self): @@ -2792,7 +2810,9 @@ def test_context(self): self.assertEqual(exc_context, exc.__context__) self.assertEqual(False, exc.__suppress_context__) self.assertEqual(expected_stack, exc.stack) - self.assertEqual(type(exc_obj), exc.exc_type) + with self.assertWarns(DeprecationWarning): + self.assertEqual(type(exc_obj), exc.exc_type) + self.assertEqual(type(exc_obj).__name__, exc.exc_type_str) self.assertEqual(str(exc_obj), str(exc)) def test_long_context_chain(self): @@ -2837,7 +2857,9 @@ def test_compact_with_cause(self): self.assertEqual(None, exc.__context__) self.assertEqual(True, exc.__suppress_context__) self.assertEqual(expected_stack, exc.stack) - self.assertEqual(type(exc_obj), exc.exc_type) + with self.assertWarns(DeprecationWarning): + self.assertEqual(type(exc_obj), exc.exc_type) + self.assertEqual(type(exc_obj).__name__, exc.exc_type_str) self.assertEqual(str(exc_obj), str(exc)) def test_compact_no_cause(self): @@ -2857,9 +2879,22 @@ def test_compact_no_cause(self): self.assertEqual(exc_context, exc.__context__) self.assertEqual(False, exc.__suppress_context__) self.assertEqual(expected_stack, exc.stack) - self.assertEqual(type(exc_obj), exc.exc_type) + with self.assertWarns(DeprecationWarning): + self.assertEqual(type(exc_obj), exc.exc_type) + self.assertEqual(type(exc_obj).__name__, exc.exc_type_str) self.assertEqual(str(exc_obj), str(exc)) + def test_no_save_exc_type(self): + try: + 1/0 + except Exception as e: + exc = e + + te = traceback.TracebackException.from_exception( + exc, save_exc_type=False) + with self.assertWarns(DeprecationWarning): + self.assertIsNone(te.exc_type) + def test_no_refs_to_exception_and_traceback_objects(self): try: 1/0 diff --git a/Lib/traceback.py b/Lib/traceback.py index b25a7291f6be51..5d83f85ac3edb0 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -5,6 +5,7 @@ import linecache import sys import textwrap +import warnings from contextlib import suppress __all__ = ['extract_stack', 'extract_tb', 'format_exception', @@ -719,7 +720,8 @@ class TracebackException: - :attr:`__suppress_context__` The *__suppress_context__* value from the original exception. - :attr:`stack` A `StackSummary` representing the traceback. - - :attr:`exc_type` The class of the original traceback. + - :attr:`exc_type` (deprecated) The class of the original traceback. + - :attr:`exc_type_str` String display of exc_type - :attr:`filename` For syntax errors - the filename where the error occurred. - :attr:`lineno` For syntax errors - the linenumber where the error @@ -737,7 +739,7 @@ class TracebackException: def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, lookup_lines=True, capture_locals=False, compact=False, - max_group_width=15, max_group_depth=10, _seen=None): + max_group_width=15, max_group_depth=10, save_exc_type=True, _seen=None): # NB: we need to accept exc_traceback, exc_value, exc_traceback to # permit backwards compat with the existing API, otherwise we # need stub thunk objects just to glue it together. @@ -754,12 +756,23 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, _walk_tb_with_full_positions(exc_traceback), limit=limit, lookup_lines=lookup_lines, capture_locals=capture_locals) - self.exc_type = exc_type + + self._exc_type = exc_type if save_exc_type else None + # Capture now to permit freeing resources: only complication is in the # unofficial API _format_final_exc_line self._str = _safe_string(exc_value, 'exception') self.__notes__ = getattr(exc_value, '__notes__', None) + self._is_syntax_error = False + self._have_exc_type = exc_type is not None + if exc_type is not None: + self.exc_type_qualname = exc_type.__qualname__ + self.exc_type_module = exc_type.__module__ + else: + self.exc_type_qualname = None + self.exc_type_module = None + if exc_type and issubclass(exc_type, SyntaxError): # Handle SyntaxError's specially self.filename = exc_value.filename @@ -771,6 +784,7 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, self.offset = exc_value.offset self.end_offset = exc_value.end_offset self.msg = exc_value.msg + self._is_syntax_error = True elif exc_type and issubclass(exc_type, ImportError) and \ getattr(exc_value, "name_from", None) is not None: wrong_name = getattr(exc_value, "name_from", None) @@ -869,6 +883,24 @@ def from_exception(cls, exc, *args, **kwargs): """Create a TracebackException from an exception.""" return cls(type(exc), exc, exc.__traceback__, *args, **kwargs) + @property + def exc_type(self): + warnings.warn('Deprecated in 3.13. Use exc_type_str instead.', + DeprecationWarning, stacklevel=2) + return self._exc_type + + @property + def exc_type_str(self): + if not self._have_exc_type: + return None + stype = self.exc_type_qualname + smod = self.exc_type_module + if smod not in ("__main__", "builtins"): + if not isinstance(smod, str): + smod = "" + stype = smod + '.' + stype + return stype + def _load_lines(self): """Private API. force all lines in the stack to be loaded.""" for frame in self.stack: @@ -901,18 +933,12 @@ def format_exception_only(self, *, show_group=False, _depth=0): """ indent = 3 * _depth * ' ' - if self.exc_type is None: + if not self._have_exc_type: yield indent + _format_final_exc_line(None, self._str) return - stype = self.exc_type.__qualname__ - smod = self.exc_type.__module__ - if smod not in ("__main__", "builtins"): - if not isinstance(smod, str): - smod = "" - stype = smod + '.' + stype - - if not issubclass(self.exc_type, SyntaxError): + stype = self.exc_type_str + if not self._is_syntax_error: if _depth > 0: # Nested exceptions needs correct handling of multiline messages. formatted = _format_final_exc_line( diff --git a/Misc/NEWS.d/next/Library/2023-11-23-10-41-21.gh-issue-112332.rhTBaa.rst b/Misc/NEWS.d/next/Library/2023-11-23-10-41-21.gh-issue-112332.rhTBaa.rst new file mode 100644 index 00000000000000..bd686ad052e5b2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-23-10-41-21.gh-issue-112332.rhTBaa.rst @@ -0,0 +1,2 @@ +Deprecate the ``exc_type`` field of :class:`traceback.TracebackException`. +Add ``exc_type_str`` to replace it. From f14d741daa1b9e5b9c9fc1edba93d0fa92b5ba8d Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 28 Nov 2023 11:18:33 +0300 Subject: [PATCH 093/228] gh-109802: Increase test coverage for complexobject.c (GH-112452) --- Lib/test/test_capi/test_complex.py | 87 ++++++++++++++++++++++++++++++ Lib/test/test_complex.py | 47 ++++++++++++++++ Modules/_testcapi/complex.c | 59 ++++++++++++++++++++ 3 files changed, 193 insertions(+) diff --git a/Lib/test/test_capi/test_complex.py b/Lib/test/test_capi/test_complex.py index 9f51efb091dea5..d6fc1f077c40aa 100644 --- a/Lib/test/test_capi/test_complex.py +++ b/Lib/test/test_capi/test_complex.py @@ -1,3 +1,5 @@ +from math import isnan +import errno import unittest import warnings @@ -10,6 +12,10 @@ _testcapi = import_helper.import_module('_testcapi') NULL = None +INF = float("inf") +NAN = float("nan") +DBL_MAX = _testcapi.DBL_MAX + class BadComplex3: def __complex__(self): @@ -141,6 +147,87 @@ def test_asccomplex(self): # CRASHES asccomplex(NULL) + def test_py_c_sum(self): + # Test _Py_c_sum() + _py_c_sum = _testcapi._py_c_sum + + self.assertEqual(_py_c_sum(1, 1j), (1+1j, 0)) + + def test_py_c_diff(self): + # Test _Py_c_diff() + _py_c_diff = _testcapi._py_c_diff + + self.assertEqual(_py_c_diff(1, 1j), (1-1j, 0)) + + def test_py_c_neg(self): + # Test _Py_c_neg() + _py_c_neg = _testcapi._py_c_neg + + self.assertEqual(_py_c_neg(1+1j), -1-1j) + + def test_py_c_prod(self): + # Test _Py_c_prod() + _py_c_prod = _testcapi._py_c_prod + + self.assertEqual(_py_c_prod(2, 1j), (2j, 0)) + + def test_py_c_quot(self): + # Test _Py_c_quot() + _py_c_quot = _testcapi._py_c_quot + + self.assertEqual(_py_c_quot(1, 1j), (-1j, 0)) + self.assertEqual(_py_c_quot(1, -1j), (1j, 0)) + self.assertEqual(_py_c_quot(1j, 2), (0.5j, 0)) + self.assertEqual(_py_c_quot(1j, -2), (-0.5j, 0)) + self.assertEqual(_py_c_quot(1, 2j), (-0.5j, 0)) + + z, e = _py_c_quot(NAN, 1j) + self.assertTrue(isnan(z.real)) + self.assertTrue(isnan(z.imag)) + self.assertEqual(e, 0) + + z, e = _py_c_quot(1j, NAN) + self.assertTrue(isnan(z.real)) + self.assertTrue(isnan(z.imag)) + self.assertEqual(e, 0) + + self.assertEqual(_py_c_quot(1, 0j)[1], errno.EDOM) + + def test_py_c_pow(self): + # Test _Py_c_pow() + _py_c_pow = _testcapi._py_c_pow + + self.assertEqual(_py_c_pow(1j, 0j), (1+0j, 0)) + self.assertEqual(_py_c_pow(1, 1j), (1+0j, 0)) + self.assertEqual(_py_c_pow(0j, 1), (0j, 0)) + self.assertAlmostEqual(_py_c_pow(1j, 2)[0], -1.0+0j) + + r, e = _py_c_pow(1+1j, -1) + self.assertAlmostEqual(r, 0.5-0.5j) + self.assertEqual(e, 0) + + self.assertEqual(_py_c_pow(0j, -1)[1], errno.EDOM) + self.assertEqual(_py_c_pow(0j, 1j)[1], errno.EDOM) + self.assertEqual(_py_c_pow(*[DBL_MAX+1j]*2)[0], complex(*[INF]*2)) + + + def test_py_c_abs(self): + # Test _Py_c_abs() + _py_c_abs = _testcapi._py_c_abs + + self.assertEqual(_py_c_abs(-1), (1.0, 0)) + self.assertEqual(_py_c_abs(1j), (1.0, 0)) + + self.assertEqual(_py_c_abs(complex('+inf+1j')), (INF, 0)) + self.assertEqual(_py_c_abs(complex('-inf+1j')), (INF, 0)) + self.assertEqual(_py_c_abs(complex('1.25+infj')), (INF, 0)) + self.assertEqual(_py_c_abs(complex('1.25-infj')), (INF, 0)) + + self.assertTrue(isnan(_py_c_abs(complex('1.25+nanj'))[0])) + self.assertTrue(isnan(_py_c_abs(complex('nan-1j'))[0])) + + self.assertEqual(_py_c_abs(complex(*[DBL_MAX]*2))[1], errno.ERANGE) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 9180cca62b28b8..b057121f285dc7 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -109,6 +109,8 @@ def test_truediv(self): complex(random(), random())) self.assertAlmostEqual(complex.__truediv__(2+0j, 1+1j), 1-1j) + self.assertRaises(TypeError, operator.truediv, 1j, None) + self.assertRaises(TypeError, operator.truediv, None, 1j) for denom_real, denom_imag in [(0, NAN), (NAN, 0), (NAN, NAN)]: z = complex(0, 0) / complex(denom_real, denom_imag) @@ -140,6 +142,7 @@ def test_floordiv_zero_division(self): def test_richcompare(self): self.assertIs(complex.__eq__(1+1j, 1<<10000), False) self.assertIs(complex.__lt__(1+1j, None), NotImplemented) + self.assertIs(complex.__eq__(1+1j, None), NotImplemented) self.assertIs(complex.__eq__(1+1j, 1+1j), True) self.assertIs(complex.__eq__(1+1j, 2+2j), False) self.assertIs(complex.__ne__(1+1j, 1+1j), False) @@ -162,6 +165,7 @@ def test_richcompare(self): self.assertIs(operator.eq(1+1j, 2+2j), False) self.assertIs(operator.ne(1+1j, 1+1j), False) self.assertIs(operator.ne(1+1j, 2+2j), True) + self.assertIs(operator.eq(1+1j, 2.0), False) def test_richcompare_boundaries(self): def check(n, deltas, is_equal, imag = 0.0): @@ -180,6 +184,27 @@ def check(n, deltas, is_equal, imag = 0.0): check(2 ** pow, range(1, 101), lambda delta: False, float(i)) check(2 ** 53, range(-100, 0), lambda delta: True) + def test_add(self): + self.assertEqual(1j + int(+1), complex(+1, 1)) + self.assertEqual(1j + int(-1), complex(-1, 1)) + self.assertRaises(OverflowError, operator.add, 1j, 10**1000) + self.assertRaises(TypeError, operator.add, 1j, None) + self.assertRaises(TypeError, operator.add, None, 1j) + + def test_sub(self): + self.assertEqual(1j - int(+1), complex(-1, 1)) + self.assertEqual(1j - int(-1), complex(1, 1)) + self.assertRaises(OverflowError, operator.sub, 1j, 10**1000) + self.assertRaises(TypeError, operator.sub, 1j, None) + self.assertRaises(TypeError, operator.sub, None, 1j) + + def test_mul(self): + self.assertEqual(1j * int(20), complex(0, 20)) + self.assertEqual(1j * int(-1), complex(0, -1)) + self.assertRaises(OverflowError, operator.mul, 1j, 10**1000) + self.assertRaises(TypeError, operator.mul, 1j, None) + self.assertRaises(TypeError, operator.mul, None, 1j) + def test_mod(self): # % is no longer supported on complex numbers with self.assertRaises(TypeError): @@ -212,11 +237,18 @@ def test_divmod_zero_division(self): def test_pow(self): self.assertAlmostEqual(pow(1+1j, 0+0j), 1.0) self.assertAlmostEqual(pow(0+0j, 2+0j), 0.0) + self.assertEqual(pow(0+0j, 2000+0j), 0.0) + self.assertEqual(pow(0, 0+0j), 1.0) + self.assertEqual(pow(-1, 0+0j), 1.0) self.assertRaises(ZeroDivisionError, pow, 0+0j, 1j) + self.assertRaises(ZeroDivisionError, pow, 0+0j, -1000) self.assertAlmostEqual(pow(1j, -1), 1/1j) self.assertAlmostEqual(pow(1j, 200), 1) self.assertRaises(ValueError, pow, 1+1j, 1+1j, 1+1j) self.assertRaises(OverflowError, pow, 1e200+1j, 1e200+1j) + self.assertRaises(TypeError, pow, 1j, None) + self.assertRaises(TypeError, pow, None, 1j) + self.assertAlmostEqual(pow(1j, 0.5), 0.7071067811865476+0.7071067811865475j) a = 3.33+4.43j self.assertEqual(a ** 0j, 1) @@ -301,6 +333,7 @@ def test_boolcontext(self): for i in range(100): self.assertTrue(complex(random() + 1e-6, random() + 1e-6)) self.assertTrue(not complex(0.0, 0.0)) + self.assertTrue(1j) def test_conjugate(self): self.assertClose(complex(5.3, 9.8).conjugate(), 5.3-9.8j) @@ -314,6 +347,8 @@ def __complex__(self): return self.value self.assertRaises(TypeError, complex, {}) self.assertRaises(TypeError, complex, NS(1.5)) self.assertRaises(TypeError, complex, NS(1)) + self.assertRaises(TypeError, complex, object()) + self.assertRaises(TypeError, complex, NS(4.25+0.5j), object()) self.assertAlmostEqual(complex("1+10j"), 1+10j) self.assertAlmostEqual(complex(10), 10+0j) @@ -359,6 +394,8 @@ def __complex__(self): return self.value self.assertAlmostEqual(complex('1e-500'), 0.0 + 0.0j) self.assertAlmostEqual(complex('-1e-500j'), 0.0 - 0.0j) self.assertAlmostEqual(complex('-1e-500+1e-500j'), -0.0 + 0.0j) + self.assertEqual(complex('1-1j'), 1.0 - 1j) + self.assertEqual(complex('1J'), 1j) class complex2(complex): pass self.assertAlmostEqual(complex(complex2(1+1j)), 1+1j) @@ -553,6 +590,8 @@ def test_hash(self): x /= 3.0 # now check against floating point self.assertEqual(hash(x), hash(complex(x, 0.))) + self.assertNotEqual(hash(2000005 - 1j), -1) + def test_abs(self): nums = [complex(x/3., y/7.) for x in range(-9,9) for y in range(-9,9)] for num in nums: @@ -602,6 +641,14 @@ def test(v, expected, test_fn=self.assertEqual): test(complex(-0., 0.), "(-0+0j)") test(complex(-0., -0.), "(-0-0j)") + def test_pos(self): + class ComplexSubclass(complex): + pass + + self.assertEqual(+(1+6j), 1+6j) + self.assertEqual(+ComplexSubclass(1, 6), 1+6j) + self.assertIs(type(+ComplexSubclass(1, 6)), complex) + def test_neg(self): self.assertEqual(-(1+6j), -1-6j) diff --git a/Modules/_testcapi/complex.c b/Modules/_testcapi/complex.c index 400f4054c613ee..4a70217eb90d62 100644 --- a/Modules/_testcapi/complex.c +++ b/Modules/_testcapi/complex.c @@ -85,6 +85,58 @@ complex_asccomplex(PyObject *Py_UNUSED(module), PyObject *obj) return PyComplex_FromCComplex(complex); } +static PyObject* +_py_c_neg(PyObject *Py_UNUSED(module), PyObject *num) +{ + Py_complex complex; + + complex = PyComplex_AsCComplex(num); + if (complex.real == -1. && PyErr_Occurred()) { + return NULL; + } + + return PyComplex_FromCComplex(_Py_c_neg(complex)); +} + +#define _PY_C_FUNC2(suffix) \ + static PyObject * \ + _py_c_##suffix(PyObject *Py_UNUSED(module), PyObject *args) \ + { \ + Py_complex num, exp, res; \ + \ + if (!PyArg_ParseTuple(args, "DD", &num, &exp)) { \ + return NULL; \ + } \ + \ + errno = 0; \ + res = _Py_c_##suffix(num, exp); \ + return Py_BuildValue("Di", &res, errno); \ + }; + +_PY_C_FUNC2(sum) +_PY_C_FUNC2(diff) +_PY_C_FUNC2(prod) +_PY_C_FUNC2(quot) +_PY_C_FUNC2(pow) + +static PyObject* +_py_c_abs(PyObject *Py_UNUSED(module), PyObject* obj) +{ + Py_complex complex; + double res; + + NULLABLE(obj); + complex = PyComplex_AsCComplex(obj); + + if (complex.real == -1. && PyErr_Occurred()) { + return NULL; + } + + errno = 0; + res = _Py_c_abs(complex); + return Py_BuildValue("di", res, errno); +} + static PyMethodDef test_methods[] = { {"complex_check", complex_check, METH_O}, @@ -94,6 +146,13 @@ static PyMethodDef test_methods[] = { {"complex_realasdouble", complex_realasdouble, METH_O}, {"complex_imagasdouble", complex_imagasdouble, METH_O}, {"complex_asccomplex", complex_asccomplex, METH_O}, + {"_py_c_sum", _py_c_sum, METH_VARARGS}, + {"_py_c_diff", _py_c_diff, METH_VARARGS}, + {"_py_c_neg", _py_c_neg, METH_O}, + {"_py_c_prod", _py_c_prod, METH_VARARGS}, + {"_py_c_quot", _py_c_quot, METH_VARARGS}, + {"_py_c_pow", _py_c_pow, METH_VARARGS}, + {"_py_c_abs", _py_c_abs, METH_O}, {NULL}, }; From a194938f33a71e727e53490815bae874eece1460 Mon Sep 17 00:00:00 2001 From: James Morris <6653392+J-M0@users.noreply.github.com> Date: Tue, 28 Nov 2023 04:24:59 -0500 Subject: [PATCH 094/228] gh-112431: Unconditionally call `hash -r` (GH-112432) The `activate` script calls `hash -r` in two places to make sure the shell picks up the environment changes the script makes. Before that, it checks to see if the shell running the script is bash or zsh. `hash -r` is specified by POSIX and is not exclusive to bash and zsh. This guard prevents the script from calling `hash -r` in other `#!/bin/sh`-compatible shells like dash. --- Lib/venv/scripts/common/activate | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/Lib/venv/scripts/common/activate b/Lib/venv/scripts/common/activate index 8398981ce53b9c..6fdf423a1c516a 100644 --- a/Lib/venv/scripts/common/activate +++ b/Lib/venv/scripts/common/activate @@ -14,12 +14,9 @@ deactivate () { unset _OLD_VIRTUAL_PYTHONHOME fi - # This should detect bash and zsh, which have a hash command that must - # be called to get it to forget past commands. Without forgetting + # Call hash to forget past commands. Without forgetting # past commands the $PATH changes we made may not be respected - if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then - hash -r 2> /dev/null - fi + hash -r 2> /dev/null if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then PS1="${_OLD_VIRTUAL_PS1:-}" @@ -69,9 +66,6 @@ if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then export PS1 fi -# This should detect bash and zsh, which have a hash command that must -# be called to get it to forget past commands. Without forgetting +# Call hash to forget past commands. Without forgetting # past commands the $PATH changes we made may not be respected -if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then - hash -r 2> /dev/null -fi +hash -r 2> /dev/null From 3fdf7ae3d1a08894e53c263945fba67fe62ac05b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 28 Nov 2023 16:46:00 +0200 Subject: [PATCH 095/228] gh-110930: Correct book title by Alan D. Moore (#112490) --- Doc/library/tkinter.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst index a8c67b02a23e4d..e084d8554c7c09 100644 --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -58,8 +58,8 @@ details that are unchanged. * `Modern Tkinter for Busy Python Developers `_ By Mark Roseman. (ISBN 978-1999149567) - * `Python and Tkinter Programming `_ - By Alan Moore. (ISBN 978-1788835886) + * `Python GUI programming with Tkinter `_ + By Alan D. Moore. (ISBN 978-1788835886) * `Programming Python `_ By Mark Lutz; has excellent coverage of Tkinter. (ISBN 978-0596158101) From 48dfd74a9db9d4aa9c6f23b4a67b461e5d977173 Mon Sep 17 00:00:00 2001 From: Itamar Oren Date: Tue, 28 Nov 2023 07:45:03 -0800 Subject: [PATCH 096/228] GH-112245: Promote free threaded CI (#112246) --- .github/workflows/build.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4210194b978749..cfb36c8c32e18d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -190,7 +190,7 @@ jobs: build_windows_free_threaded: name: 'Windows (free-threaded)' needs: check_source - if: needs.check_source.outputs.run_tests == 'true' && contains(github.event.pull_request.labels.*.name, 'topic-free-threaded') + if: needs.check_source.outputs.run_tests == 'true' uses: ./.github/workflows/reusable-windows.yml with: free-threaded: true @@ -206,7 +206,7 @@ jobs: build_macos_free_threaded: name: 'macOS (free-threaded)' needs: check_source - if: needs.check_source.outputs.run_tests == 'true' && contains(github.event.pull_request.labels.*.name, 'topic-free-threaded') + if: needs.check_source.outputs.run_tests == 'true' uses: ./.github/workflows/reusable-macos.yml with: config_hash: ${{ needs.check_source.outputs.config_hash }} @@ -228,7 +228,7 @@ jobs: build_ubuntu_free_threaded: name: 'Ubuntu (free-threaded)' needs: check_source - if: needs.check_source.outputs.run_tests == 'true' && contains(github.event.pull_request.labels.*.name, 'topic-free-threaded') + if: needs.check_source.outputs.run_tests == 'true' uses: ./.github/workflows/reusable-ubuntu.yml with: config_hash: ${{ needs.check_source.outputs.config_hash }} @@ -521,10 +521,7 @@ jobs: uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe with: allowed-failures: >- - build_macos_free_threaded, - build_ubuntu_free_threaded, build_ubuntu_ssltests, - build_windows_free_threaded, cifuzz, test_hypothesis, allowed-skips: >- From e413daf5f6b983bdb4e1965d76b5313cb93b266e Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Wed, 29 Nov 2023 13:15:39 +1300 Subject: [PATCH 097/228] gh-112454: Disable TLS-PSK if OpenSSL was built without PSK support (#112491) If OpenSSL was built without PSK support, the python TLS-PSK methods will raise "NotImplementedError" if called. Add a constant "ssl.HAS_PSK" to check if TLS-PSK is supported --- Doc/library/ssl.rst | 12 ++++++++++++ Lib/ssl.py | 2 +- Lib/test/test_ssl.py | 2 ++ Modules/_ssl.c | 28 ++++++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 206294528e0016..0db233e2dde33c 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -908,6 +908,12 @@ Constants .. versionadded:: 3.7 +.. data:: HAS_PSK + + Whether the OpenSSL library has built-in support for TLS-PSK. + + .. versionadded:: 3.13 + .. data:: CHANNEL_BINDING_TYPES List of supported TLS channel binding types. Strings in this list @@ -2050,6 +2056,9 @@ to speed up repeated connections from the same clients. return 'ClientId_1', psk_table.get(hint, b'') context.set_psk_client_callback(callback) + This method will raise :exc:`NotImplementedError` if :data:`HAS_PSK` is + ``False``. + .. versionadded:: 3.13 .. method:: SSLContext.set_psk_server_callback(callback, identity_hint=None) @@ -2092,6 +2101,9 @@ to speed up repeated connections from the same clients. return psk_table.get(identity, b'') context.set_psk_server_callback(callback, 'ServerId_1') + This method will raise :exc:`NotImplementedError` if :data:`HAS_PSK` is + ``False``. + .. versionadded:: 3.13 .. index:: single: certificates diff --git a/Lib/ssl.py b/Lib/ssl.py index 36fca9d4aa93d1..d01484964b6895 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -116,7 +116,7 @@ from _ssl import ( HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN, HAS_SSLv2, HAS_SSLv3, HAS_TLSv1, - HAS_TLSv1_1, HAS_TLSv1_2, HAS_TLSv1_3 + HAS_TLSv1_1, HAS_TLSv1_2, HAS_TLSv1_3, HAS_PSK ) from _ssl import _DEFAULT_CIPHERS, _OPENSSL_API_VERSION diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index aecba89cde1495..3fdfa2960503b8 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -4259,6 +4259,7 @@ def test_session_handling(self): 'Session refers to a different SSLContext.') @requires_tls_version('TLSv1_2') + @unittest.skipUnless(ssl.HAS_PSK, 'TLS-PSK disabled on this OpenSSL build') def test_psk(self): psk = bytes.fromhex('deadbeef') @@ -4326,6 +4327,7 @@ def server_callback(identity): s.connect((HOST, server.port)) @requires_tls_version('TLSv1_3') + @unittest.skipUnless(ssl.HAS_PSK, 'TLS-PSK disabled on this OpenSSL build') def test_psk_tls1_3(self): psk = bytes.fromhex('deadbeef') identity_hint = 'identity-hint' diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 707e7ad9543acb..90b600f4b776a3 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -301,8 +301,10 @@ typedef struct { BIO *keylog_bio; /* Cached module state, also used in SSLSocket and SSLSession code. */ _sslmodulestate *state; +#ifndef OPENSSL_NO_PSK PyObject *psk_client_callback; PyObject *psk_server_callback; +#endif } PySSLContext; typedef struct { @@ -3125,8 +3127,10 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version) self->alpn_protocols = NULL; self->set_sni_cb = NULL; self->state = get_ssl_state(module); +#ifndef OPENSSL_NO_PSK self->psk_client_callback = NULL; self->psk_server_callback = NULL; +#endif /* Don't check host name by default */ if (proto_version == PY_SSL_VERSION_TLS_CLIENT) { @@ -3239,8 +3243,10 @@ context_clear(PySSLContext *self) Py_CLEAR(self->set_sni_cb); Py_CLEAR(self->msg_cb); Py_CLEAR(self->keylog_filename); +#ifndef OPENSSL_NO_PSK Py_CLEAR(self->psk_client_callback); Py_CLEAR(self->psk_server_callback); +#endif if (self->keylog_bio != NULL) { PySSL_BEGIN_ALLOW_THREADS BIO_free_all(self->keylog_bio); @@ -4668,6 +4674,7 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form) return NULL; } +#ifndef OPENSSL_NO_PSK static unsigned int psk_client_callback(SSL *s, const char *hint, char *identity, @@ -4735,6 +4742,7 @@ static unsigned int psk_client_callback(SSL *s, PyGILState_Release(gstate); return 0; } +#endif /*[clinic input] _ssl._SSLContext.set_psk_client_callback @@ -4747,6 +4755,7 @@ _ssl__SSLContext_set_psk_client_callback_impl(PySSLContext *self, PyObject *callback) /*[clinic end generated code: output=0aba86f6ed75119e input=7627bae0e5ee7635]*/ { +#ifndef OPENSSL_NO_PSK if (self->protocol == PY_SSL_VERSION_TLS_SERVER) { _setSSLError(get_state_ctx(self), "Cannot add PSK client callback to a " @@ -4774,8 +4783,14 @@ _ssl__SSLContext_set_psk_client_callback_impl(PySSLContext *self, SSL_CTX_set_psk_client_callback(self->ctx, ssl_callback); Py_RETURN_NONE; +#else + PyErr_SetString(PyExc_NotImplementedError, + "TLS-PSK is not supported by your OpenSSL version."); + return NULL; +#endif } +#ifndef OPENSSL_NO_PSK static unsigned int psk_server_callback(SSL *s, const char *identity, unsigned char *psk, @@ -4835,6 +4850,7 @@ static unsigned int psk_server_callback(SSL *s, PyGILState_Release(gstate); return 0; } +#endif /*[clinic input] _ssl._SSLContext.set_psk_server_callback @@ -4849,6 +4865,7 @@ _ssl__SSLContext_set_psk_server_callback_impl(PySSLContext *self, const char *identity_hint) /*[clinic end generated code: output=1f4d6a4e09a92b03 input=65d4b6022aa85ea3]*/ { +#ifndef OPENSSL_NO_PSK if (self->protocol == PY_SSL_VERSION_TLS_CLIENT) { _setSSLError(get_state_ctx(self), "Cannot add PSK server callback to a " @@ -4882,6 +4899,11 @@ _ssl__SSLContext_set_psk_server_callback_impl(PySSLContext *self, SSL_CTX_set_psk_server_callback(self->ctx, ssl_callback); Py_RETURN_NONE; +#else + PyErr_SetString(PyExc_NotImplementedError, + "TLS-PSK is not supported by your OpenSSL version."); + return NULL; +#endif } @@ -6243,6 +6265,12 @@ sslmodule_init_constants(PyObject *m) addbool(m, "HAS_TLSv1_3", 0); #endif +#ifdef OPENSSL_NO_PSK + addbool(m, "HAS_PSK", 0); +#else + addbool(m, "HAS_PSK", 1); +#endif + #undef addbool #undef ADD_INT_CONST From e723700190ba497d1601cb423ee48d5d222a9d26 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 29 Nov 2023 10:10:11 +0900 Subject: [PATCH 098/228] Rename ...Uop... to ...UOp... (uppercase O) for consistency (#112327) * Rename _PyUopExecute to _PyUOpExecute (uppercase O) for consistency * Also rename _PyUopName and _PyUOp_Replacements, and some output strings --- Include/internal/pycore_uops.h | 2 +- Python/bytecodes.c | 2 +- Python/ceval.c | 12 ++++++------ Python/generated_cases.c.h | 2 +- Python/optimizer.c | 28 ++++++++++++++-------------- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Include/internal/pycore_uops.h b/Include/internal/pycore_uops.h index e2b94894681f44..ea8f90bf8c1d8f 100644 --- a/Include/internal/pycore_uops.h +++ b/Include/internal/pycore_uops.h @@ -24,7 +24,7 @@ typedef struct { _PyUOpInstruction trace[1]; } _PyUOpExecutorObject; -_PyInterpreterFrame *_PyUopExecute( +_PyInterpreterFrame *_PyUOpExecute( _PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject **stack_pointer); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index a1ca66e457e47d..2e5f6c8d0d10e2 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2357,7 +2357,7 @@ dummy_func( JUMPBY(1-original_oparg); frame->instr_ptr = next_instr; Py_INCREF(executor); - if (executor->execute == _PyUopExecute) { + if (executor->execute == _PyUOpExecute) { current_executor = (_PyUOpExecutorObject *)executor; GOTO_TIER_TWO(); } diff --git a/Python/ceval.c b/Python/ceval.c index def75fd114cb86..1806ceb7fa9681 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -647,7 +647,7 @@ static const _Py_CODEUNIT _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS[] = { extern const struct _PyCode_DEF(8) _Py_InitCleanup; -extern const char *_PyUopName(int index); +extern const char *_PyUOpName(int index); /* Disable unused label warnings. They are handy for debugging, even if computed gotos aren't used. */ @@ -1002,7 +1002,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int DPRINTF(3, "%4d: uop %s, oparg %d, operand %" PRIu64 ", target %d, stack_level %d\n", (int)(next_uop - current_executor->trace), - _PyUopName(uopcode), + _PyUOpName(uopcode), next_uop->oparg, next_uop->operand, next_uop->target, @@ -1051,8 +1051,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int pop_1_error_tier_two: STACK_SHRINK(1); error_tier_two: - DPRINTF(2, "Error: [Uop %d (%s), oparg %d, operand %" PRIu64 ", target %d @ %d]\n", - uopcode, _PyUopName(uopcode), next_uop[-1].oparg, next_uop[-1].operand, next_uop[-1].target, + DPRINTF(2, "Error: [UOp %d (%s), oparg %d, operand %" PRIu64 ", target %d @ %d]\n", + uopcode, _PyUOpName(uopcode), next_uop[-1].oparg, next_uop[-1].operand, next_uop[-1].target, (int)(next_uop - current_executor->trace - 1)); OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); frame->return_offset = 0; // Don't leave this random @@ -1064,8 +1064,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int deoptimize: // On DEOPT_IF we just repeat the last instruction. // This presumes nothing was popped from the stack (nor pushed). - DPRINTF(2, "DEOPT: [Uop %d (%s), oparg %d, operand %" PRIu64 ", target %d @ %d]\n", - uopcode, _PyUopName(uopcode), next_uop[-1].oparg, next_uop[-1].operand, next_uop[-1].target, + DPRINTF(2, "DEOPT: [UOp %d (%s), oparg %d, operand %" PRIu64 ", target %d @ %d]\n", + uopcode, _PyUOpName(uopcode), next_uop[-1].oparg, next_uop[-1].operand, next_uop[-1].target, (int)(next_uop - current_executor->trace - 1)); OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); UOP_STAT_INC(uopcode, miss); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index dedd793111b7ff..0ac99e759deb12 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2331,7 +2331,7 @@ JUMPBY(1-original_oparg); frame->instr_ptr = next_instr; Py_INCREF(executor); - if (executor->execute == _PyUopExecute) { + if (executor->execute == _PyUOpExecute) { current_executor = (_PyUOpExecutorObject *)executor; GOTO_TIER_TWO(); } diff --git a/Python/optimizer.c b/Python/optimizer.c index ec59fea26fbc70..dd24fbebbfd2a9 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -325,7 +325,7 @@ uop_dealloc(_PyUOpExecutorObject *self) { } const char * -_PyUopName(int index) +_PyUOpName(int index) { if (index <= MAX_REAL_OPCODE) { return _PyOpcode_OpName[index]; @@ -347,7 +347,7 @@ uop_item(_PyUOpExecutorObject *self, Py_ssize_t index) PyErr_SetNone(PyExc_IndexError); return NULL; } - const char *name = _PyUopName(self->trace[index].opcode); + const char *name = _PyUOpName(self->trace[index].opcode); if (name == NULL) { name = ""; } @@ -388,7 +388,7 @@ PyTypeObject _PyUOpExecutor_Type = { /* TO DO -- Generate these tables */ static const uint16_t -_PyUop_Replacements[OPCODE_METADATA_SIZE] = { +_PyUOp_Replacements[OPCODE_METADATA_SIZE] = { [_ITER_JUMP_RANGE] = _GUARD_NOT_EXHAUSTED_RANGE, [_ITER_JUMP_LIST] = _GUARD_NOT_EXHAUSTED_LIST, [_ITER_JUMP_TUPLE] = _GUARD_NOT_EXHAUSTED_TUPLE, @@ -451,7 +451,7 @@ translate_bytecode_to_trace( #define ADD_TO_TRACE(OPCODE, OPARG, OPERAND, TARGET) \ DPRINTF(2, \ " ADD_TO_TRACE(%s, %d, %" PRIu64 ")\n", \ - _PyUopName(OPCODE), \ + _PyUOpName(OPCODE), \ (OPARG), \ (uint64_t)(OPERAND)); \ assert(trace_length < max_length); \ @@ -474,7 +474,7 @@ translate_bytecode_to_trace( } // Reserve space for N uops, plus 3 for _SET_IP, _CHECK_VALIDITY and _EXIT_TRACE -#define RESERVE(needed) RESERVE_RAW((needed) + 3, _PyUopName(opcode)) +#define RESERVE(needed) RESERVE_RAW((needed) + 3, _PyUOpName(opcode)) // Trace stack operations (used by _PUSH_FRAME, _POP_FRAME) #define TRACE_STACK_PUSH() \ @@ -546,8 +546,8 @@ translate_bytecode_to_trace( uint32_t uopcode = BRANCH_TO_GUARD[opcode - POP_JUMP_IF_FALSE][jump_likely]; _Py_CODEUNIT *next_instr = instr + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]]; DPRINTF(4, "%s(%d): counter=%x, bitcount=%d, likely=%d, uopcode=%s\n", - _PyUopName(opcode), oparg, - counter, bitcount, jump_likely, _PyUopName(uopcode)); + _PyUOpName(opcode), oparg, + counter, bitcount, jump_likely, _PyUOpName(uopcode)); ADD_TO_TRACE(uopcode, max_length, 0, target); if (jump_likely) { _Py_CODEUNIT *target_instr = next_instr + oparg; @@ -615,8 +615,8 @@ translate_bytecode_to_trace( oparg += extras; } } - if (_PyUop_Replacements[uop]) { - uop = _PyUop_Replacements[uop]; + if (_PyUOp_Replacements[uop]) { + uop = _PyUOp_Replacements[uop]; if (uop == _FOR_ITER_TIER_TWO) { target += 1 + INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1; assert(_PyCode_CODE(code)[target-1].op.code == END_FOR || @@ -712,7 +712,7 @@ translate_bytecode_to_trace( } break; } - DPRINTF(2, "Unsupported opcode %s\n", _PyUopName(opcode)); + DPRINTF(2, "Unsupported opcode %s\n", _PyUOpName(opcode)); OPT_UNSUPPORTED_OPCODE(opcode); goto done; // Break out of loop } // End default @@ -832,7 +832,7 @@ make_executor_from_uops(_PyUOpInstruction *buffer, _PyBloomFilter *dependencies) dest--; } assert(dest == -1); - executor->base.execute = _PyUopExecute; + executor->base.execute = _PyUOpExecute; _Py_ExecutorInit((_PyExecutorObject *)executor, dependencies); #ifdef Py_DEBUG char *python_lltrace = Py_GETENV("PYTHON_LLTRACE"); @@ -845,7 +845,7 @@ make_executor_from_uops(_PyUOpInstruction *buffer, _PyBloomFilter *dependencies) for (int i = 0; i < length; i++) { printf("%4d %s(%d, %d, %" PRIu64 ")\n", i, - _PyUopName(executor->trace[i].opcode), + _PyUOpName(executor->trace[i].opcode), executor->trace[i].oparg, executor->trace[i].target, executor->trace[i].operand); @@ -888,11 +888,11 @@ uop_optimize( return 1; } -/* Dummy execute() function for Uop Executor. +/* Dummy execute() function for UOp Executor. * The actual implementation is inlined in ceval.c, * in _PyEval_EvalFrameDefault(). */ _PyInterpreterFrame * -_PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject **stack_pointer) +_PyUOpExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject **stack_pointer) { Py_FatalError("Tier 2 is now inlined into Tier 1"); } From f9e6ce03953e9ee988d55324dc715b0ef2303cfb Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Tue, 28 Nov 2023 20:40:12 -0800 Subject: [PATCH 099/228] [Enum] update class creation for RuntimeError changes (GH-111815) --- Doc/howto/enum.rst | 1 - Lib/enum.py | 14 +++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Doc/howto/enum.rst b/Doc/howto/enum.rst index a136c76303c8ef..ffdafb749c73a9 100644 --- a/Doc/howto/enum.rst +++ b/Doc/howto/enum.rst @@ -1439,7 +1439,6 @@ alias:: Traceback (most recent call last): ... ValueError: aliases not allowed in DuplicateFreeEnum: 'GRENE' --> 'GREEN' - Error calling __set_name__ on '_proto_member' instance 'GRENE' in 'Color' .. note:: diff --git a/Lib/enum.py b/Lib/enum.py index 4e76e96d2c916e..648401e80be685 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -568,12 +568,16 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k try: exc = None enum_class = super().__new__(metacls, cls, bases, classdict, **kwds) - except RuntimeError as e: - # any exceptions raised by member.__new__ will get converted to a - # RuntimeError, so get that original exception back and raise it instead - exc = e.__cause__ or e + except Exception as e: + # since 3.12 the line "Error calling __set_name__ on '_proto_member' instance ..." + # is tacked on to the error instead of raising a RuntimeError + # recreate the exception to discard + exc = type(e)(str(e)) + exc.__cause__ = e.__cause__ + exc.__context__ = e.__context__ + tb = e.__traceback__ if exc is not None: - raise exc + raise exc.with_traceback(tb) # # update classdict with any changes made by __init_subclass__ classdict.update(enum_class.__dict__) From e0449b9a7fffc0c0eed806bf4cbb8f1f65397bbb Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 29 Nov 2023 17:37:05 +0200 Subject: [PATCH 100/228] Add more C API tests (GH-112522) Add tests for PyObject_Str(), PyObject_Repr(), PyObject_ASCII() and PyObject_Bytes(). --- Lib/test/test_capi/test_abstract.py | 86 +++++++++++++++++++++++++++++ Modules/_testcapi/abstract.c | 33 +++++++++++ 2 files changed, 119 insertions(+) diff --git a/Lib/test/test_capi/test_abstract.py b/Lib/test/test_capi/test_abstract.py index 26152c3049848c..97ed939928c360 100644 --- a/Lib/test/test_capi/test_abstract.py +++ b/Lib/test/test_capi/test_abstract.py @@ -8,6 +8,30 @@ NULL = None +class StrSubclass(str): + pass + +class BytesSubclass(bytes): + pass + +class WithStr: + def __init__(self, value): + self.value = value + def __str__(self): + return self.value + +class WithRepr: + def __init__(self, value): + self.value = value + def __repr__(self): + return self.value + +class WithBytes: + def __init__(self, value): + self.value = value + def __bytes__(self): + return self.value + class TestObject: @property def evil(self): @@ -44,6 +68,68 @@ def gen(): class CAPITest(unittest.TestCase): + def assertTypedEqual(self, actual, expected): + self.assertIs(type(actual), type(expected)) + self.assertEqual(actual, expected) + + def test_object_str(self): + # Test PyObject_Str() + object_str = _testcapi.object_str + self.assertTypedEqual(object_str(''), '') + self.assertTypedEqual(object_str('abc'), 'abc') + self.assertTypedEqual(object_str('\U0001f40d'), '\U0001f40d') + self.assertTypedEqual(object_str(StrSubclass('abc')), 'abc') + self.assertTypedEqual(object_str(WithStr('abc')), 'abc') + self.assertTypedEqual(object_str(WithStr(StrSubclass('abc'))), StrSubclass('abc')) + self.assertTypedEqual(object_str(WithRepr('')), '') + self.assertTypedEqual(object_str(WithRepr(StrSubclass(''))), StrSubclass('')) + self.assertTypedEqual(object_str(NULL), '') + + def test_object_repr(self): + # Test PyObject_Repr() + object_repr = _testcapi.object_repr + self.assertTypedEqual(object_repr(''), "''") + self.assertTypedEqual(object_repr('abc'), "'abc'") + self.assertTypedEqual(object_repr('\U0001f40d'), "'\U0001f40d'") + self.assertTypedEqual(object_repr(StrSubclass('abc')), "'abc'") + self.assertTypedEqual(object_repr(WithRepr('')), '') + self.assertTypedEqual(object_repr(WithRepr(StrSubclass(''))), StrSubclass('')) + self.assertTypedEqual(object_repr(WithRepr('<\U0001f40d>')), '<\U0001f40d>') + self.assertTypedEqual(object_repr(WithRepr(StrSubclass('<\U0001f40d>'))), StrSubclass('<\U0001f40d>')) + self.assertTypedEqual(object_repr(NULL), '') + + def test_object_ascii(self): + # Test PyObject_ASCII() + object_ascii = _testcapi.object_ascii + self.assertTypedEqual(object_ascii(''), "''") + self.assertTypedEqual(object_ascii('abc'), "'abc'") + self.assertTypedEqual(object_ascii('\U0001f40d'), r"'\U0001f40d'") + self.assertTypedEqual(object_ascii(StrSubclass('abc')), "'abc'") + self.assertTypedEqual(object_ascii(WithRepr('')), '') + self.assertTypedEqual(object_ascii(WithRepr(StrSubclass(''))), StrSubclass('')) + self.assertTypedEqual(object_ascii(WithRepr('<\U0001f40d>')), r'<\U0001f40d>') + self.assertTypedEqual(object_ascii(WithRepr(StrSubclass('<\U0001f40d>'))), r'<\U0001f40d>') + self.assertTypedEqual(object_ascii(NULL), '') + + def test_object_bytes(self): + # Test PyObject_Bytes() + object_bytes = _testcapi.object_bytes + self.assertTypedEqual(object_bytes(b''), b'') + self.assertTypedEqual(object_bytes(b'abc'), b'abc') + self.assertTypedEqual(object_bytes(BytesSubclass(b'abc')), b'abc') + self.assertTypedEqual(object_bytes(WithBytes(b'abc')), b'abc') + self.assertTypedEqual(object_bytes(WithBytes(BytesSubclass(b'abc'))), BytesSubclass(b'abc')) + self.assertTypedEqual(object_bytes(bytearray(b'abc')), b'abc') + self.assertTypedEqual(object_bytes(memoryview(b'abc')), b'abc') + self.assertTypedEqual(object_bytes([97, 98, 99]), b'abc') + self.assertTypedEqual(object_bytes((97, 98, 99)), b'abc') + self.assertTypedEqual(object_bytes(iter([97, 98, 99])), b'abc') + self.assertRaises(TypeError, object_bytes, WithBytes(bytearray(b'abc'))) + self.assertRaises(TypeError, object_bytes, WithBytes([97, 98, 99])) + self.assertRaises(TypeError, object_bytes, 3) + self.assertRaises(TypeError, object_bytes, 'abc') + self.assertRaises(TypeError, object_bytes, object()) + self.assertTypedEqual(object_bytes(NULL), b'') def test_object_getattr(self): xgetattr = _testcapi.object_getattr diff --git a/Modules/_testcapi/abstract.c b/Modules/_testcapi/abstract.c index 4a9144e66f0fcd..a8ba009eb6a54b 100644 --- a/Modules/_testcapi/abstract.c +++ b/Modules/_testcapi/abstract.c @@ -2,6 +2,34 @@ #include "util.h" +static PyObject * +object_repr(PyObject *self, PyObject *arg) +{ + NULLABLE(arg); + return PyObject_Repr(arg); +} + +static PyObject * +object_ascii(PyObject *self, PyObject *arg) +{ + NULLABLE(arg); + return PyObject_ASCII(arg); +} + +static PyObject * +object_str(PyObject *self, PyObject *arg) +{ + NULLABLE(arg); + return PyObject_Str(arg); +} + +static PyObject * +object_bytes(PyObject *self, PyObject *arg) +{ + NULLABLE(arg); + return PyObject_Bytes(arg); +} + static PyObject * object_getattr(PyObject *self, PyObject *args) { @@ -616,6 +644,11 @@ sequence_tuple(PyObject *self, PyObject *obj) static PyMethodDef test_methods[] = { + {"object_repr", object_repr, METH_O}, + {"object_ascii", object_ascii, METH_O}, + {"object_str", object_str, METH_O}, + {"object_bytes", object_bytes, METH_O}, + {"object_getattr", object_getattr, METH_VARARGS}, {"object_getattrstring", object_getattrstring, METH_VARARGS}, {"object_getoptionalattr", object_getoptionalattr, METH_VARARGS}, From 403886942376210662610627b01fea6acd77d331 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 29 Nov 2023 09:36:48 -0800 Subject: [PATCH 101/228] gh-112509: Fix keys being present in both required_keys and optional_keys in TypedDict (#112512) Co-authored-by: Alex Waygood --- Lib/test/test_typing.py | 40 +++++++++++++++++++ Lib/typing.py | 25 +++++++++--- ...-11-28-20-01-33.gh-issue-112509.QtoKed.rst | 3 ++ 3 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-28-20-01-33.gh-issue-112509.QtoKed.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 669803177315e3..4fbb410f26ab8d 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -7769,6 +7769,46 @@ class Cat(Animal): 'voice': str, }) + def test_keys_inheritance_with_same_name(self): + class NotTotal(TypedDict, total=False): + a: int + + class Total(NotTotal): + a: int + + self.assertEqual(NotTotal.__required_keys__, frozenset()) + self.assertEqual(NotTotal.__optional_keys__, frozenset(['a'])) + self.assertEqual(Total.__required_keys__, frozenset(['a'])) + self.assertEqual(Total.__optional_keys__, frozenset()) + + class Base(TypedDict): + a: NotRequired[int] + b: Required[int] + + class Child(Base): + a: Required[int] + b: NotRequired[int] + + self.assertEqual(Base.__required_keys__, frozenset(['b'])) + self.assertEqual(Base.__optional_keys__, frozenset(['a'])) + self.assertEqual(Child.__required_keys__, frozenset(['a'])) + self.assertEqual(Child.__optional_keys__, frozenset(['b'])) + + def test_multiple_inheritance_with_same_key(self): + class Base1(TypedDict): + a: NotRequired[int] + + class Base2(TypedDict): + a: Required[str] + + class Child(Base1, Base2): + pass + + # Last base wins + self.assertEqual(Child.__annotations__, {'a': Required[str]}) + self.assertEqual(Child.__required_keys__, frozenset(['a'])) + self.assertEqual(Child.__optional_keys__, frozenset()) + def test_required_notrequired_keys(self): self.assertEqual(NontotalMovie.__required_keys__, frozenset({"title"})) diff --git a/Lib/typing.py b/Lib/typing.py index 216f0c141b62af..b3af701f8d5437 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -2884,8 +2884,14 @@ def __new__(cls, name, bases, ns, total=True): for base in bases: annotations.update(base.__dict__.get('__annotations__', {})) - required_keys.update(base.__dict__.get('__required_keys__', ())) - optional_keys.update(base.__dict__.get('__optional_keys__', ())) + + base_required = base.__dict__.get('__required_keys__', set()) + required_keys |= base_required + optional_keys -= base_required + + base_optional = base.__dict__.get('__optional_keys__', set()) + required_keys -= base_optional + optional_keys |= base_optional annotations.update(own_annotations) for annotation_key, annotation_type in own_annotations.items(): @@ -2897,14 +2903,23 @@ def __new__(cls, name, bases, ns, total=True): annotation_origin = get_origin(annotation_type) if annotation_origin is Required: - required_keys.add(annotation_key) + is_required = True elif annotation_origin is NotRequired: - optional_keys.add(annotation_key) - elif total: + is_required = False + else: + is_required = total + + if is_required: required_keys.add(annotation_key) + optional_keys.discard(annotation_key) else: optional_keys.add(annotation_key) + required_keys.discard(annotation_key) + assert required_keys.isdisjoint(optional_keys), ( + f"Required keys overlap with optional keys in {name}:" + f" {required_keys=}, {optional_keys=}" + ) tp_dict.__annotations__ = annotations tp_dict.__required_keys__ = frozenset(required_keys) tp_dict.__optional_keys__ = frozenset(optional_keys) diff --git a/Misc/NEWS.d/next/Library/2023-11-28-20-01-33.gh-issue-112509.QtoKed.rst b/Misc/NEWS.d/next/Library/2023-11-28-20-01-33.gh-issue-112509.QtoKed.rst new file mode 100644 index 00000000000000..a16d67e7776bcb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-28-20-01-33.gh-issue-112509.QtoKed.rst @@ -0,0 +1,3 @@ +Fix edge cases that could cause a key to be present in both the +``__required_keys__`` and ``__optional_keys__`` attributes of a +:class:`typing.TypedDict`. Patch by Jelle Zijlstra. From d4a6229afe10d7e5a9a59bf9472f36d7698988db Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 29 Nov 2023 09:38:29 -0800 Subject: [PATCH 102/228] gh-104003: Implement PEP 702 (#104004) Co-authored-by: Hugo van Kemenade Co-authored-by: Alex Waygood --- Doc/library/warnings.rst | 50 ++++ Doc/whatsnew/3.13.rst | 9 + Lib/test/test_warnings/__init__.py | 282 +++++++++++++++++- Lib/warnings.py | 131 +++++++- ...-04-29-20-49-13.gh-issue-104003.-8Ruk2.rst | 3 + 5 files changed, 473 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-04-29-20-49-13.gh-issue-104003.-8Ruk2.rst diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst index 884de08eab1b16..a9c469707e8227 100644 --- a/Doc/library/warnings.rst +++ b/Doc/library/warnings.rst @@ -522,6 +522,56 @@ Available Functions and calls to :func:`simplefilter`. +.. decorator:: deprecated(msg, *, category=DeprecationWarning, stacklevel=1) + + Decorator to indicate that a class, function or overload is deprecated. + + When this decorator is applied to an object, + deprecation warnings may be emitted at runtime when the object is used. + :term:`static type checkers ` + will also generate a diagnostic on usage of the deprecated object. + + Usage:: + + from warnings import deprecated + from typing import overload + + @deprecated("Use B instead") + class A: + pass + + @deprecated("Use g instead") + def f(): + pass + + @overload + @deprecated("int support is deprecated") + def g(x: int) -> int: ... + @overload + def g(x: str) -> int: ... + + The warning specified by *category* will be emitted at runtime + on use of deprecated objects. For functions, that happens on calls; + for classes, on instantiation and on creation of subclasses. + If the *category* is ``None``, no warning is emitted at runtime. + The *stacklevel* determines where the + warning is emitted. If it is ``1`` (the default), the warning + is emitted at the direct caller of the deprecated object; if it + is higher, it is emitted further up the stack. + Static type checker behavior is not affected by the *category* + and *stacklevel* arguments. + + 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 + for the attribute to exist on the overload as returned by + :func:`typing.get_overloads`. + + .. versionadded:: 3.13 + See :pep:`702`. + + Available Context Managers -------------------------- diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 198ea3a4b57bde..372e4a45468e68 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -348,6 +348,15 @@ venv (using ``--without-scm-ignore-files``). (Contributed by Brett Cannon in :gh:`108125`.) +warnings +-------- + +* The new :func:`warnings.deprecated` decorator provides a way to communicate + deprecations to :term:`static type checkers ` and + to warn on usage of deprecated classes and functions. A runtime deprecation + warning may also be emitted when a decorated function or class is used at runtime. + See :pep:`702`. (Contributed by Jelle Zijlstra in :gh:`104003`.) + Optimizations ============= diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index 2c523230e7e97f..cd989fe36bf26b 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -5,6 +5,8 @@ import re import sys import textwrap +import types +from typing import overload, get_overloads import unittest from test import support from test.support import import_helper @@ -16,6 +18,7 @@ from test.test_warnings.data import stacklevel as warning_tests import warnings as original_warnings +from warnings import deprecated py_warnings = import_helper.import_fresh_module('warnings', @@ -90,7 +93,7 @@ def test_module_all_attribute(self): self.assertTrue(hasattr(self.module, '__all__')) target_api = ["warn", "warn_explicit", "showwarning", "formatwarning", "filterwarnings", "simplefilter", - "resetwarnings", "catch_warnings"] + "resetwarnings", "catch_warnings", "deprecated"] self.assertSetEqual(set(self.module.__all__), set(target_api)) @@ -1377,6 +1380,283 @@ def test_late_resource_warning(self): self.assertTrue(err.startswith(expected), ascii(err)) +class DeprecatedTests(unittest.TestCase): + def test_dunder_deprecated(self): + @deprecated("A will go away soon") + class A: + pass + + self.assertEqual(A.__deprecated__, "A will go away soon") + self.assertIsInstance(A, type) + + @deprecated("b will go away soon") + def b(): + pass + + self.assertEqual(b.__deprecated__, "b will go away soon") + self.assertIsInstance(b, types.FunctionType) + + @overload + @deprecated("no more ints") + def h(x: int) -> int: ... + @overload + def h(x: str) -> str: ... + def h(x): + return x + + overloads = get_overloads(h) + self.assertEqual(len(overloads), 2) + self.assertEqual(overloads[0].__deprecated__, "no more ints") + + def test_class(self): + @deprecated("A will go away soon") + class A: + pass + + with self.assertWarnsRegex(DeprecationWarning, "A will go away soon"): + A() + with self.assertWarnsRegex(DeprecationWarning, "A will go away soon"): + with self.assertRaises(TypeError): + A(42) + + def test_class_with_init(self): + @deprecated("HasInit will go away soon") + class HasInit: + def __init__(self, x): + self.x = x + + with self.assertWarnsRegex(DeprecationWarning, "HasInit will go away soon"): + instance = HasInit(42) + self.assertEqual(instance.x, 42) + + def test_class_with_new(self): + has_new_called = False + + @deprecated("HasNew will go away soon") + class HasNew: + def __new__(cls, x): + nonlocal has_new_called + has_new_called = True + return super().__new__(cls) + + def __init__(self, x) -> None: + self.x = x + + with self.assertWarnsRegex(DeprecationWarning, "HasNew will go away soon"): + instance = HasNew(42) + self.assertEqual(instance.x, 42) + self.assertTrue(has_new_called) + + def test_class_with_inherited_new(self): + new_base_called = False + + class NewBase: + def __new__(cls, x): + nonlocal new_base_called + new_base_called = True + return super().__new__(cls) + + def __init__(self, x) -> None: + self.x = x + + @deprecated("HasInheritedNew will go away soon") + class HasInheritedNew(NewBase): + pass + + with self.assertWarnsRegex(DeprecationWarning, "HasInheritedNew will go away soon"): + instance = HasInheritedNew(42) + self.assertEqual(instance.x, 42) + self.assertTrue(new_base_called) + + def test_class_with_new_but_no_init(self): + new_called = False + + @deprecated("HasNewNoInit will go away soon") + class HasNewNoInit: + def __new__(cls, x): + nonlocal new_called + new_called = True + obj = super().__new__(cls) + obj.x = x + return obj + + with self.assertWarnsRegex(DeprecationWarning, "HasNewNoInit will go away soon"): + instance = HasNewNoInit(42) + self.assertEqual(instance.x, 42) + self.assertTrue(new_called) + + def test_mixin_class(self): + @deprecated("Mixin will go away soon") + class Mixin: + pass + + class Base: + def __init__(self, a) -> None: + self.a = a + + with self.assertWarnsRegex(DeprecationWarning, "Mixin will go away soon"): + class Child(Base, Mixin): + pass + + instance = Child(42) + self.assertEqual(instance.a, 42) + + def test_existing_init_subclass(self): + @deprecated("C will go away soon") + class C: + def __init_subclass__(cls) -> None: + cls.inited = True + + with self.assertWarnsRegex(DeprecationWarning, "C will go away soon"): + C() + + with self.assertWarnsRegex(DeprecationWarning, "C will go away soon"): + class D(C): + pass + + self.assertTrue(D.inited) + self.assertIsInstance(D(), D) # no deprecation + + def test_existing_init_subclass_in_base(self): + class Base: + def __init_subclass__(cls, x) -> None: + cls.inited = x + + @deprecated("C will go away soon") + class C(Base, x=42): + pass + + self.assertEqual(C.inited, 42) + + with self.assertWarnsRegex(DeprecationWarning, "C will go away soon"): + C() + + with self.assertWarnsRegex(DeprecationWarning, "C will go away soon"): + class D(C, x=3): + pass + + self.assertEqual(D.inited, 3) + + def test_init_subclass_has_correct_cls(self): + init_subclass_saw = None + + @deprecated("Base will go away soon") + class Base: + def __init_subclass__(cls) -> None: + nonlocal init_subclass_saw + init_subclass_saw = cls + + self.assertIsNone(init_subclass_saw) + + with self.assertWarnsRegex(DeprecationWarning, "Base will go away soon"): + class C(Base): + pass + + self.assertIs(init_subclass_saw, C) + + def test_init_subclass_with_explicit_classmethod(self): + init_subclass_saw = None + + @deprecated("Base will go away soon") + class Base: + @classmethod + def __init_subclass__(cls) -> None: + nonlocal init_subclass_saw + init_subclass_saw = cls + + self.assertIsNone(init_subclass_saw) + + with self.assertWarnsRegex(DeprecationWarning, "Base will go away soon"): + class C(Base): + pass + + self.assertIs(init_subclass_saw, C) + + def test_function(self): + @deprecated("b will go away soon") + def b(): + pass + + with self.assertWarnsRegex(DeprecationWarning, "b will go away soon"): + b() + + def test_method(self): + class Capybara: + @deprecated("x will go away soon") + def x(self): + pass + + instance = Capybara() + with self.assertWarnsRegex(DeprecationWarning, "x will go away soon"): + instance.x() + + def test_property(self): + class Capybara: + @property + @deprecated("x will go away soon") + def x(self): + pass + + @property + def no_more_setting(self): + return 42 + + @no_more_setting.setter + @deprecated("no more setting") + def no_more_setting(self, value): + pass + + instance = Capybara() + with self.assertWarnsRegex(DeprecationWarning, "x will go away soon"): + instance.x + + with py_warnings.catch_warnings(): + py_warnings.simplefilter("error") + self.assertEqual(instance.no_more_setting, 42) + + with self.assertWarnsRegex(DeprecationWarning, "no more setting"): + instance.no_more_setting = 42 + + def test_category(self): + @deprecated("c will go away soon", category=RuntimeWarning) + def c(): + pass + + with self.assertWarnsRegex(RuntimeWarning, "c will go away soon"): + c() + + def test_turn_off_warnings(self): + @deprecated("d will go away soon", category=None) + def d(): + pass + + with py_warnings.catch_warnings(): + py_warnings.simplefilter("error") + d() + + def test_only_strings_allowed(self): + with self.assertRaisesRegex( + TypeError, + "Expected an object of type str for 'message', not 'type'" + ): + @deprecated + class Foo: ... + + with self.assertRaisesRegex( + TypeError, + "Expected an object of type str for 'message', not 'function'" + ): + @deprecated + def foo(): ... + + def test_no_retained_references_to_wrapper_instance(self): + @deprecated('depr') + def d(): pass + + self.assertFalse(any( + isinstance(cell.cell_contents, deprecated) for cell in d.__closure__ + )) + def setUpModule(): py_warnings.onceregistry.clear() c_warnings.onceregistry.clear() diff --git a/Lib/warnings.py b/Lib/warnings.py index 32e58072b9cc33..924f872172d4d1 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -5,7 +5,7 @@ __all__ = ["warn", "warn_explicit", "showwarning", "formatwarning", "filterwarnings", "simplefilter", - "resetwarnings", "catch_warnings"] + "resetwarnings", "catch_warnings", "deprecated"] def showwarning(message, category, filename, lineno, file=None, line=None): """Hook to write a warning to a file; replace if you like.""" @@ -508,6 +508,135 @@ def __exit__(self, *exc_info): self._module._showwarnmsg_impl = self._showwarnmsg_impl +class deprecated: + """Indicate that a class, function or overload is deprecated. + + When this decorator is applied to an object, the type checker + will generate a diagnostic on usage of the deprecated object. + + Usage: + + @deprecated("Use B instead") + class A: + pass + + @deprecated("Use g instead") + def f(): + pass + + @overload + @deprecated("int support is deprecated") + def g(x: int) -> int: ... + @overload + def g(x: str) -> int: ... + + The warning specified by *category* will be emitted at runtime + on use of deprecated objects. For functions, that happens on calls; + for classes, on instantiation and on creation of subclasses. + If the *category* is ``None``, no warning is emitted at runtime. + The *stacklevel* determines where the + warning is emitted. If it is ``1`` (the default), the warning + is emitted at the direct caller of the deprecated object; if it + is higher, it is emitted further up the stack. + Static type checker behavior is not affected by the *category* + and *stacklevel* arguments. + + 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 ``@overload`` decorator for the attribute to + exist on the overload as returned by ``get_overloads()``. + + See PEP 702 for details. + + """ + def __init__( + self, + message: str, + /, + *, + category: type[Warning] | None = DeprecationWarning, + stacklevel: int = 1, + ) -> None: + if not isinstance(message, str): + raise TypeError( + f"Expected an object of type str for 'message', not {type(message).__name__!r}" + ) + self.message = message + self.category = category + self.stacklevel = stacklevel + + def __call__(self, arg, /): + # Make sure the inner functions created below don't + # retain a reference to self. + msg = self.message + category = self.category + stacklevel = self.stacklevel + if category is None: + arg.__deprecated__ = msg + return arg + elif isinstance(arg, type): + import functools + from types import MethodType + + original_new = arg.__new__ + + @functools.wraps(original_new) + def __new__(cls, *args, **kwargs): + if cls is arg: + warn(msg, category=category, stacklevel=stacklevel + 1) + if original_new is not object.__new__: + return original_new(cls, *args, **kwargs) + # Mirrors a similar check in object.__new__. + elif cls.__init__ is object.__init__ and (args or kwargs): + raise TypeError(f"{cls.__name__}() takes no arguments") + else: + return original_new(cls) + + arg.__new__ = staticmethod(__new__) + + original_init_subclass = arg.__init_subclass__ + # We need slightly different behavior if __init_subclass__ + # is a bound method (likely if it was implemented in Python) + if isinstance(original_init_subclass, MethodType): + original_init_subclass = original_init_subclass.__func__ + + @functools.wraps(original_init_subclass) + def __init_subclass__(*args, **kwargs): + warn(msg, category=category, stacklevel=stacklevel + 1) + return original_init_subclass(*args, **kwargs) + + arg.__init_subclass__ = classmethod(__init_subclass__) + # Or otherwise, which likely means it's a builtin such as + # object's implementation of __init_subclass__. + else: + @functools.wraps(original_init_subclass) + def __init_subclass__(*args, **kwargs): + warn(msg, category=category, stacklevel=stacklevel + 1) + return original_init_subclass(*args, **kwargs) + + arg.__init_subclass__ = __init_subclass__ + + arg.__deprecated__ = __new__.__deprecated__ = msg + __init_subclass__.__deprecated__ = msg + return arg + elif callable(arg): + import functools + + @functools.wraps(arg) + def wrapper(*args, **kwargs): + warn(msg, category=category, stacklevel=stacklevel + 1) + return arg(*args, **kwargs) + + arg.__deprecated__ = wrapper.__deprecated__ = msg + return wrapper + else: + raise TypeError( + "@deprecated decorator with non-None category must be applied to " + f"a class or callable, not {arg!r}" + ) + + _DEPRECATED_MSG = "{name!r} is deprecated and slated for removal in Python {remove}" def _deprecated(name, message=_DEPRECATED_MSG, *, remove, _version=sys.version_info): diff --git a/Misc/NEWS.d/next/Library/2023-04-29-20-49-13.gh-issue-104003.-8Ruk2.rst b/Misc/NEWS.d/next/Library/2023-04-29-20-49-13.gh-issue-104003.-8Ruk2.rst new file mode 100644 index 00000000000000..82d61ca8b8bc97 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-04-29-20-49-13.gh-issue-104003.-8Ruk2.rst @@ -0,0 +1,3 @@ +Add :func:`warnings.deprecated`, a decorator to mark deprecated functions to +static type checkers and to warn on usage of deprecated classes and functions. +See :pep:`702`. Patch by Jelle Zijlstra. From 37589d76bbe97b0bf13ffafb8dd6aab361a0209a Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Wed, 29 Nov 2023 16:18:25 -0800 Subject: [PATCH 103/228] GH-103065, GH-106704, GH-105253: Provide a `Tools/wasm/wasi.py` script to simplify doing a WASI build (GH-112473) --- .devcontainer/Dockerfile | 2 +- .gitignore | 1 + Makefile.pre.in | 1 + ...-11-27-13-55-47.gh-issue-103065.o72OiA.rst | 1 + Tools/wasm/README.md | 112 +++--- Tools/wasm/mypy.ini | 5 +- Tools/wasm/wasi.py | 328 ++++++++++++++++++ 7 files changed, 373 insertions(+), 77 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2023-11-27-13-55-47.gh-issue-103065.o72OiA.rst create mode 100644 Tools/wasm/wasi.py diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 590d7834b2b8be..9f808af38e69df 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -6,7 +6,7 @@ ENV WASI_SDK_VERSION=20 ENV WASI_SDK_PATH=/opt/wasi-sdk ENV WASMTIME_HOME=/opt/wasmtime -ENV WASMTIME_VERSION=9.0.1 +ENV WASMTIME_VERSION=14.0.4 ENV WASMTIME_CPU_ARCH=x86_64 RUN dnf -y --nodocs --setopt=install_weak_deps=False install /usr/bin/{blurb,clang,curl,git,ln,tar,xz} 'dnf-command(builddep)' && \ diff --git a/.gitignore b/.gitignore index 8c8273fc7a3aa3..c424a894c2a6e0 100644 --- a/.gitignore +++ b/.gitignore @@ -125,6 +125,7 @@ Tools/unicode/data/ /config.status.lineno # hendrikmuhs/ccache-action@v1 /.ccache +/cross-build/ /platform /profile-clean-stamp /profile-run-stamp diff --git a/Makefile.pre.in b/Makefile.pre.in index 3d766425abba34..e7f8abce43d648 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2732,6 +2732,7 @@ clobber: clean -rm -rf build platform -rm -rf $(PYTHONFRAMEWORKDIR) -rm -f python-config.py python-config + -rm -rf cross-build # Make things extra clean, before making a distribution: # remove all generated files, even Makefile[.pre] diff --git a/Misc/NEWS.d/next/Build/2023-11-27-13-55-47.gh-issue-103065.o72OiA.rst b/Misc/NEWS.d/next/Build/2023-11-27-13-55-47.gh-issue-103065.o72OiA.rst new file mode 100644 index 00000000000000..e2240b7c656a2f --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-11-27-13-55-47.gh-issue-103065.o72OiA.rst @@ -0,0 +1 @@ +Introduce ``Tools/wasm/wasi.py`` to simplify doing a WASI build. diff --git a/Tools/wasm/README.md b/Tools/wasm/README.md index 8ef63c6dcd9ddc..acbece214ed9bd 100644 --- a/Tools/wasm/README.md +++ b/Tools/wasm/README.md @@ -298,100 +298,66 @@ AddType application/wasm wasm ## WASI (wasm32-wasi) -WASI builds require the [WASI SDK](https://github.com/WebAssembly/wasi-sdk) 16.0+. -See `.devcontainer/Dockerfile` for an example of how to download and -install the WASI SDK. +**NOTE**: The instructions below assume a Unix-based OS due to cross-compilation for CPython being set up for `./configure`. -### Build +### Prerequisites + +Developing for WASI requires two things to be installed: + +1. The [WASI SDK](https://github.com/WebAssembly/wasi-sdk) 16.0+ + (see `.devcontainer/Dockerfile` for an example of how to download and install the WASI SDK) +2. A WASI host/runtime ([wasmtime](https://wasmtime.dev) 14+ is recommended and what the instructions below assume) -The script ``wasi-env`` sets necessary compiler and linker flags as well as -``pkg-config`` overrides. The script assumes that WASI-SDK is installed in -``/opt/wasi-sdk`` or ``$WASI_SDK_PATH``. -There are two scripts you can use to do a WASI build from a source checkout. You can either use: +### Building +Building for WASI requires doing a cross-build where you have a "build" Python to help produce a WASI build of CPython (technically it's a "host x host" cross-build because the build Python is also the target Python while the host build is the WASI build; yes, it's confusing terminology). In the end you should have a build Python in `cross-build/build` and a WASI build in `cross-build/wasm32-wasi`. + +The easiest way to do a build is to use the `wasi.py` script. You can either have it perform the entire build process from start to finish in one step, or you can do it in discrete steps that mirror running `configure` and `make` for each of the two builds of Python you end up producing (which are beneficial when you only need to do a specific step after getting a complete build, e.g. editing some code and you just need to run `make` for the WASI build). + +The discrete steps are: ```shell -./Tools/wasm/wasm_build.py wasi build +python Tools/wasm/wasi.py configure-build-python +python Tools/wasm/wasi.py make-build-python +python Tools/wasm/wasi.py configure-host +python Tools/wasm/wasi.py make-host ``` -or: +To do it in a single command, run: ```shell -./Tools/wasm/build_wasi.sh +python Tools/wasm/wasi.py build ``` -The commands are equivalent to the following steps: - -- Make sure `Modules/Setup.local` exists -- Make sure the necessary build tools are installed: - - [WASI SDK](https://github.com/WebAssembly/wasi-sdk) (which ships with `clang`) - - `make` - - `pkg-config` (on Linux) -- Create the build Python - - `mkdir -p builddir/build` - - `pushd builddir/build` - - Get the build platform - - Python: `sysconfig.get_config_var("BUILD_GNU_TYPE")` - - Shell: `../../config.guess` - - `../../configure -C` - - `make all` - - ```PYTHON_VERSION=`./python -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")'` ``` - - `popd` -- Create the host/WASI Python - - `mkdir builddir/wasi` - - `pushd builddir/wasi` - - `../../Tools/wasm/wasi-env ../../configure -C --host=wasm32-unknown-wasi --build=$(../../config.guess) --with-build-python=../build/python` - - `CONFIG_SITE=../../Tools/wasm/config.site-wasm32-wasi` - - `HOSTRUNNER="wasmtime run --mapdir /::$(dirname $(dirname $(pwd))) --env PYTHONPATH=/builddir/wasi/build/lib.wasi-wasm32-$PYTHON_VERSION $(pwd)/python.wasm --"` - - Maps the source checkout to `/` in the WASI runtime - - Stdlib gets loaded from `/Lib` - - Gets `_sysconfigdata__wasi_wasm32-wasi.py` on to `sys.path` via `PYTHONPATH` - - Set by `wasi-env` - - `WASI_SDK_PATH` - - `WASI_SYSROOT` - - `CC` - - `CPP` - - `CXX` - - `LDSHARED` - - `AR` - - `RANLIB` - - `CFLAGS` - - `LDFLAGS` - - `PKG_CONFIG_PATH` - - `PKG_CONFIG_LIBDIR` - - `PKG_CONFIG_SYSROOT_DIR` - - `PATH` - - `make all` - +That will: -### Running - -If you followed the instructions above, you can run the interpreter via e.g., `wasmtime` from within the `Tools/wasi` directory (make sure to set/change `$PYTHON_VERSION` and do note the paths are relative to running in`builddir/wasi` for simplicity only): +1. Run `configure` for the build Python (same as `wasi.py configure-build-python`) +2. Run `make` for the build Python (`wasi.py make-build-python`) +3. Run `configure` for the WASI build (`wasi.py configure-host`) +4. Run `make` for the WASI build (`wasi.py make-host`) +See the `--help` for the various options available for each of the subcommands which controls things like the location of the WASI SDK, the command to use with the WASI host/runtime, etc. Also note that you can use `--` as a separtor for any of the `configure`-related commands -- including `build` -- to pass arguments to `configure` itself. For example, if you want a pydebug build that also caches the results from `configure`, you can do: ```shell -wasmtime run --mapdir /::../.. --env PYTHONPATH=/builddir/wasi/build/lib.wasi-wasm32-$PYTHON_VERSION python.wasm -- +python Tools/wasm/wasi.py build -- -C --with-pydebug ``` -There are also helpers provided by `Tools/wasm/wasm_build.py` as listed below. Also, if you used `Tools/wasm/build_wasi.sh`, a `run_wasi.sh` file will be created in `builddir/wasi` which will run the above command for you (it also uses absolute paths, so it can be executed from anywhere). - -#### REPL - +The `wasi.py` script is able to infer details from the build Python, and so you only technically need to specify `--with-pydebug` once for `configure-build-python` and `configure-host` will detect its use if you use the discrete steps: ```shell -./Tools/wasm/wasm_build.py wasi repl +python Tools/wasm/wasi.py configure-build-python -- -C --with-pydebug +python Tools/wasm/wasi.py make-build-python +python Tools/wasm/wasi.py configure-host -- -C +python Tools/wasm/wasi.py make-host ``` -#### Tests +### Running + +If you used `wasi.py` to do your build then there will be a `cross-build/wasm32-wasi/python.sh` file which you can use to run the `python.wasm` file (see the output from the `configure-host` subcommand): ```shell -./Tools/wasm/wasm_build.py wasi test +cross-build/wasm32-wasi/python.sh --version ``` -### Debugging +While you _can_ run `python.wasm` directly, Python will fail to start up without certain things being set (e.g. `PYTHONPATH` for `sysconfig` data). As such, the `python.sh` file records these details for you. -* ``wasmtime run -g`` generates debugging symbols for gdb and lldb. The - feature is currently broken, see - https://github.com/bytecodealliance/wasmtime/issues/4669 . -* The environment variable ``RUST_LOG=wasi_common`` enables debug and - trace logging. ## Detect WebAssembly builds @@ -402,15 +368,17 @@ import os, sys if sys.platform == "emscripten": # Python on Emscripten + ... if sys.platform == "wasi": # Python on WASI + ... if os.name == "posix": # WASM platforms identify as POSIX-like. # Windows does not provide os.uname(). machine = os.uname().machine if machine.startswith("wasm"): - # WebAssembly (wasm32, wasm64 in the future) + # WebAssembly (wasm32, wasm64 potentially in the future) ``` ```python diff --git a/Tools/wasm/mypy.ini b/Tools/wasm/mypy.ini index c62598f89eba69..4de0a30c260f5f 100644 --- a/Tools/wasm/mypy.ini +++ b/Tools/wasm/mypy.ini @@ -1,5 +1,5 @@ [mypy] -files = Tools/wasm +files = Tools/wasm/wasm_*.py pretty = True show_traceback = True @@ -9,6 +9,3 @@ python_version = 3.8 # Be strict... strict = True enable_error_code = truthy-bool,ignore-without-code - -# except for incomplete defs, which are useful for module authors: -disallow_incomplete_defs = False diff --git a/Tools/wasm/wasi.py b/Tools/wasm/wasi.py new file mode 100644 index 00000000000000..34c0e9375e24c8 --- /dev/null +++ b/Tools/wasm/wasi.py @@ -0,0 +1,328 @@ +#!/usr/bin/env python3 + +import argparse +import contextlib +import functools +import os +try: + from os import process_cpu_count as cpu_count +except ImportError: + from os import cpu_count +import pathlib +import shutil +import subprocess +import sys +import sysconfig +import tempfile + + +CHECKOUT = pathlib.Path(__file__).parent.parent.parent +CROSS_BUILD_DIR = CHECKOUT / "cross-build" +BUILD_DIR = CROSS_BUILD_DIR / "build" +HOST_TRIPLE = "wasm32-wasi" +HOST_DIR = CROSS_BUILD_DIR / HOST_TRIPLE + + +def updated_env(updates={}): + """Create a new dict representing the environment to use. + + The changes made to the execution environment are printed out. + """ + env_defaults = {} + # 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() + env_defaults["SOURCE_DATE_EPOCH"] = epoch + except subprocess.CalledProcessError: + pass # Might be building from a tarball. + # This layering lets SOURCE_DATE_EPOCH from os.environ takes precedence. + environment = env_defaults | os.environ | updates + + env_diff = {} + for key, value in environment.items(): + if os.environ.get(key) != value: + env_diff[key] = value + + print("🌎 Environment changes:") + for key in sorted(env_diff.keys()): + print(f" {key}={env_diff[key]}") + + return environment + + +def subdir(working_dir, *, clean_ok=False): + """Decorator to change to a working directory.""" + def decorator(func): + @functools.wraps(func) + def wrapper(context): + try: + tput_output = subprocess.check_output(["tput", "cols"], + encoding="utf-8") + terminal_width = int(tput_output.strip()) + except subprocess.CalledProcessError: + terminal_width = 80 + print("⎯" * terminal_width) + print("📁", working_dir) + if clean_ok and context.clean and working_dir.exists(): + print(f"🚮 Deleting directory (--clean)...") + shutil.rmtree(working_dir) + + working_dir.mkdir(parents=True, exist_ok=True) + + with contextlib.chdir(working_dir): + return func(context, working_dir) + + return wrapper + + return decorator + + +def call(command, *, quiet, **kwargs): + """Execute a command. + + If 'quiet' is true, then redirect stdout and stderr to a temporary file. + """ + print("❯", " ".join(map(str, command))) + if not quiet: + stdout = None + stderr = None + else: + stdout = tempfile.NamedTemporaryFile("w", encoding="utf-8", + delete=False, + prefix="cpython-wasi-", + suffix=".log") + stderr = subprocess.STDOUT + print(f"📝 Logging output to {stdout.name} (--quiet)...") + + subprocess.check_call(command, **kwargs, stdout=stdout, stderr=stderr) + + +def build_platform(): + """The name of the build/host platform.""" + # Can also be found via `config.guess`.` + return sysconfig.get_config_var("BUILD_GNU_TYPE") + + +def build_python_path(): + """The path to the build Python binary.""" + binary = BUILD_DIR / "python" + 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}") + + return binary + + +@subdir(BUILD_DIR, clean_ok=True) +def configure_build_python(context, working_dir): + """Configure the build/host Python.""" + local_setup = CHECKOUT / "Modules" / "Setup.local" + if local_setup.exists(): + print(f"👍 {local_setup} exists ...") + else: + print(f"📝 Touching {local_setup} ...") + local_setup.touch() + + configure = [os.path.relpath(CHECKOUT / 'configure', working_dir)] + if context.args: + configure.extend(context.args) + + call(configure, quiet=context.quiet) + + +@subdir(BUILD_DIR) +def make_build_python(context, working_dir): + """Make/build the build Python.""" + call(["make", "--jobs", str(cpu_count()), "all"], + quiet=context.quiet) + + binary = build_python_path() + 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}") + + +def find_wasi_sdk(): + """Find the path to wasi-sdk.""" + if wasi_sdk_path := os.environ.get("WASI_SDK_PATH"): + return pathlib.Path(wasi_sdk_path) + elif (default_path := pathlib.Path("/opt/wasi-sdk")).exists(): + return default_path + + +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++", + "LDSHARED": "wasm-ld", "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) + + if wasi_sdk_path != pathlib.Path("/opt/wasi-sdk"): + for compiler in ["CC", "CPP", "CXX"]: + env[compiler] += f" --sysroot={sysroot}" + + env["PKG_CONFIG_PATH"] = "" + env["PKG_CONFIG_LIBDIR"] = os.pathsep.join( + 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"]]) + + return env + + +@subdir(HOST_DIR, clean_ok=True) +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") + + config_site = os.fsdecode(CHECKOUT / "Tools" / "wasm" / "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}" + lib_dir = os.fsdecode(lib_dirs[0]) + pydebug = lib_dir.endswith("-pydebug") + python_version = lib_dir.removesuffix("-pydebug").rpartition("-")[-1] + sysconfig_data = f"{wasi_build_dir}/build/lib.wasi-wasm32-{python_version}" + if pydebug: + sysconfig_data += "-pydebug" + + # Use PYTHONPATH to include sysconfig data which must be anchored to the + # WASI guest's `/` directory. + host_runner = context.host_runner.format(GUEST_DIR="/", + HOST_DIR=CHECKOUT, + ENV_VAR_NAME="PYTHONPATH", + ENV_VAR_VALUE=f"/{sysconfig_data}", + PYTHON_WASM=working_dir / "python.wasm") + 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={HOST_TRIPLE}", + f"--build={build_platform()}", + f"--with-build-python={build_python}"] + if pydebug: + configure.append("--with-pydebug") + if context.args: + configure.extend(context.args) + call(configure, + env=updated_env(env_additions | wasi_sdk_env(context)), + quiet=context.quiet) + + exec_script = working_dir / "python.sh" + with exec_script.open("w", encoding="utf-8") as file: + file.write(f'#!/bin/sh\nexec {host_runner} "$@"\n') + exec_script.chmod(0o755) + print(f"🏃‍♀️ Created {exec_script} ... ") + sys.stdout.flush() + + +@subdir(HOST_DIR) +def make_wasi_python(context, working_dir): + """Run `make` for the WASI/host build.""" + call(["make", "--jobs", str(cpu_count()), "all"], + env=updated_env(), + quiet=context.quiet) + + exec_script = working_dir / "python.sh" + subprocess.check_call([exec_script, "--version"]) + + +def build_all(context): + """Build everything.""" + steps = [configure_build_python, make_build_python, configure_wasi_python, + make_wasi_python] + for step in steps: + step(context) + + +def main(): + default_host_runner = (f"{shutil.which('wasmtime')} run " + # Make sure the stack size will work for a pydebug + # build. + # The 8388608 value comes from `ulimit -s` under Linux + # which equates to 8291 KiB. + "--wasm max-wasm-stack=8388608 " + # Enable thread support. + "--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} " + # Path to the WASM binary. + "{PYTHON_WASM}") + + 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") + 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") + for subcommand in build, configure_build, configure_host: + 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`") + for subcommand in build, configure_host: + subcommand.add_argument("--wasi-sdk", type=pathlib.Path, + dest="wasi_sdk_path", + default=find_wasi_sdk(), + help="Path to wasi-sdk; defaults to " + "$WASI_SDK_PATH or /opt/wasi-sdk") + subcommand.add_argument("--host-runner", action="store", + default=default_host_runner, dest="host_runner", + help="Command template for running the WebAssembly " + "code (default meant for wasmtime 14 or newer: " + f"`{default_host_runner}`)") + + context = parser.parse_args() + + 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} + dispatch[context.subcommand](context) + + +if __name__ == "__main__": + main() From 81261fa67ff82b03c255733b0d1abbbb8a228187 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 30 Nov 2023 02:08:44 -0500 Subject: [PATCH 104/228] IDLE: fix config_key htest (#112545) Change 'Dialog' to 'Window' in two places to match the name of the config_key class being tested. --- Lib/idlelib/config_key.py | 2 +- Lib/idlelib/idle_test/htest.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/idlelib/config_key.py b/Lib/idlelib/config_key.py index bb07231cd590b6..e5f67e8d4069ee 100644 --- a/Lib/idlelib/config_key.py +++ b/Lib/idlelib/config_key.py @@ -351,4 +351,4 @@ def cancel(self, event=None): main('idlelib.idle_test.test_config_key', verbosity=2, exit=False) from idlelib.idle_test.htest import run - run(GetKeysDialog) + run(GetKeysWindow) diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index d297f8aa0094ee..595e51d3699f6e 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -152,7 +152,7 @@ def _wrapper(parent): # htest # "Best to close editor first." } -GetKeysDialog_spec = { +GetKeysWindow_spec = { 'file': 'config_key', 'kwds': {'title': 'Test keybindings', 'action': 'find-again', From 0785c685599aaa052f85d6163872bdecb9c66486 Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Thu, 30 Nov 2023 13:12:49 +0300 Subject: [PATCH 105/228] gh-111972: Make Unicode name C APIcapsule initialization thread-safe (#112249) --- Include/internal/pycore_ucnhash.h | 2 ++ Objects/unicodeobject.c | 32 ++++++++++++++++++++----------- Python/codecs.c | 12 +++--------- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/Include/internal/pycore_ucnhash.h b/Include/internal/pycore_ucnhash.h index 187dd68e7347ff..1561dfbb3150d3 100644 --- a/Include/internal/pycore_ucnhash.h +++ b/Include/internal/pycore_ucnhash.h @@ -28,6 +28,8 @@ typedef struct { } _PyUnicode_Name_CAPI; +extern _PyUnicode_Name_CAPI* _PyUnicode_GetNameCAPI(void); + #ifdef __cplusplus } #endif diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index cffc06297a9aee..10022e23c04abf 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -5869,6 +5869,23 @@ PyUnicode_AsUTF16String(PyObject *unicode) return _PyUnicode_EncodeUTF16(unicode, NULL, 0); } +_PyUnicode_Name_CAPI * +_PyUnicode_GetNameCAPI(void) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyUnicode_Name_CAPI *ucnhash_capi; + + ucnhash_capi = _Py_atomic_load_ptr(&interp->unicode.ucnhash_capi); + if (ucnhash_capi == NULL) { + ucnhash_capi = (_PyUnicode_Name_CAPI *)PyCapsule_Import( + PyUnicodeData_CAPSULE_NAME, 1); + + // It's fine if we overwite the value here. It's always the same value. + _Py_atomic_store_ptr(&interp->unicode.ucnhash_capi, ucnhash_capi); + } + return ucnhash_capi; +} + /* --- Unicode Escape Codec ----------------------------------------------- */ PyObject * @@ -5884,7 +5901,6 @@ _PyUnicode_DecodeUnicodeEscapeInternal(const char *s, PyObject *errorHandler = NULL; PyObject *exc = NULL; _PyUnicode_Name_CAPI *ucnhash_capi; - PyInterpreterState *interp = _PyInterpreterState_GET(); // so we can remember if we've seen an invalid escape char or not *first_invalid_escape = NULL; @@ -6032,19 +6048,13 @@ _PyUnicode_DecodeUnicodeEscapeInternal(const char *s, /* \N{name} */ case 'N': - ucnhash_capi = interp->unicode.ucnhash_capi; + ucnhash_capi = _PyUnicode_GetNameCAPI(); if (ucnhash_capi == NULL) { - /* load the unicode data module */ - ucnhash_capi = (_PyUnicode_Name_CAPI *)PyCapsule_Import( - PyUnicodeData_CAPSULE_NAME, 1); - if (ucnhash_capi == NULL) { - PyErr_SetString( + PyErr_SetString( PyExc_UnicodeError, "\\N escapes not supported (can't load unicodedata module)" - ); - goto onError; - } - interp->unicode.ucnhash_capi = ucnhash_capi; + ); + goto onError; } message = "malformed \\N character escape"; diff --git a/Python/codecs.c b/Python/codecs.c index 545bf82e00dca1..d8fe7b22063a80 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -931,8 +931,6 @@ PyObject *PyCodec_BackslashReplaceErrors(PyObject *exc) return Py_BuildValue("(Nn)", res, end); } -static _PyUnicode_Name_CAPI *ucnhash_capi = NULL; - PyObject *PyCodec_NameReplaceErrors(PyObject *exc) { if (PyObject_TypeCheck(exc, (PyTypeObject *)PyExc_UnicodeEncodeError)) { @@ -953,13 +951,9 @@ PyObject *PyCodec_NameReplaceErrors(PyObject *exc) return NULL; if (!(object = PyUnicodeEncodeError_GetObject(exc))) return NULL; - if (!ucnhash_capi) { - /* load the unicode data module */ - ucnhash_capi = (_PyUnicode_Name_CAPI *)PyCapsule_Import( - PyUnicodeData_CAPSULE_NAME, 1); - if (!ucnhash_capi) { - return NULL; - } + _PyUnicode_Name_CAPI *ucnhash_capi = _PyUnicode_GetNameCAPI(); + if (ucnhash_capi == NULL) { + return NULL; } for (i = start, ressize = 0; i < end; ++i) { /* object is guaranteed to be "ready" */ From 7eeea13403882af63a71226433c9a13b80c22564 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Thu, 30 Nov 2023 10:40:53 +0000 Subject: [PATCH 106/228] gh-112205: Support @getter annotation from AC (gh-112396) --- Lib/test/clinic.test.c | 21 ++++++++ Lib/test/test_clinic.py | 26 ++++++---- Modules/_io/bufferedio.c | 81 +++++++++++++------------------ Modules/_io/clinic/bufferedio.c.h | 56 ++++++++++++++++++++- Tools/clinic/clinic.py | 53 ++++++++++++++++---- 5 files changed, 169 insertions(+), 68 deletions(-) diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index 81f88c4d1535ce..ee4a4228fd28be 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -4951,6 +4951,27 @@ static PyObject * Test_meth_coexist_impl(TestObj *self) /*[clinic end generated code: output=808a293d0cd27439 input=2a1d75b5e6fec6dd]*/ +/*[clinic input] +@getter +Test.property +[clinic start generated code]*/ + +#define TEST_PROPERTY_GETTERDEF \ + {"property", (getter)Test_property_get, NULL, NULL}, + +static PyObject * +Test_property_get_impl(TestObj *self); + +static PyObject * +Test_property_get(TestObj *self, void *Py_UNUSED(context)) +{ + return Test_property_get_impl(self); +} + +static PyObject * +Test_property_get_impl(TestObj *self) +/*[clinic end generated code: output=892b6fb351ff85fd input=2d92b3449fbc7d2b]*/ + /*[clinic input] output push diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index da957fcebaa296..f53e9481083106 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -638,7 +638,7 @@ class C "void *" "" C.__init__ = C.meth [clinic start generated code]*/ """ - err = "'__init__' must be a normal method, not a class or static method" + err = "'__init__' must be a normal method; got 'FunctionKind.CLASS_METHOD'!" self.expect_failure(block, err, lineno=8) def test_validate_cloned_new(self): @@ -2180,14 +2180,22 @@ class Foo "" "" self.expect_failure(block, err, lineno=2) def test_init_must_be_a_normal_method(self): - err = "'__init__' must be a normal method, not a class or static method!" - block = """ - module foo - class Foo "" "" - @classmethod - Foo.__init__ - """ - self.expect_failure(block, err, lineno=3) + err_template = "'__init__' must be a normal method; got 'FunctionKind.{}'!" + annotations = { + "@classmethod": "CLASS_METHOD", + "@staticmethod": "STATIC_METHOD", + "@getter": "GETTER", + } + for annotation, invalid_kind in annotations.items(): + with self.subTest(annotation=annotation, invalid_kind=invalid_kind): + block = f""" + module foo + class Foo "" "" + {annotation} + Foo.__init__ + """ + expected_error = err_template.format(invalid_kind) + self.expect_failure(block, expected_error, lineno=3) def test_duplicate_coexist(self): err = "Called @coexist twice" diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 4f3786676d131f..679626863c385c 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -10,7 +10,6 @@ #include "Python.h" #include "pycore_bytesobject.h" // _PyBytes_Join() #include "pycore_call.h" // _PyObject_CallNoArgs() -#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION() #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "pycore_pyerrors.h" // _Py_FatalErrorFormat() #include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing() @@ -518,25 +517,20 @@ buffered_closed(buffered *self) return closed; } +/*[clinic input] +@critical_section +@getter +_io._Buffered.closed +[clinic start generated code]*/ + static PyObject * -buffered_closed_get_impl(buffered *self, void *context) +_io__Buffered_closed_get_impl(buffered *self) +/*[clinic end generated code: output=f08ce57290703a1a input=18eddefdfe4a3d2f]*/ { CHECK_INITIALIZED(self) return PyObject_GetAttr(self->raw, &_Py_ID(closed)); } -static PyObject * -buffered_closed_get(buffered *self, void *context) -{ - PyObject *return_value = NULL; - - Py_BEGIN_CRITICAL_SECTION(self); - return_value = buffered_closed_get_impl(self, context); - Py_END_CRITICAL_SECTION(); - - return return_value; -} - /*[clinic input] @critical_section _io._Buffered.close @@ -662,44 +656,35 @@ _io__Buffered_writable_impl(buffered *self) return PyObject_CallMethodNoArgs(self->raw, &_Py_ID(writable)); } + +/*[clinic input] +@critical_section +@getter +_io._Buffered.name +[clinic start generated code]*/ + static PyObject * -buffered_name_get_impl(buffered *self, void *context) +_io__Buffered_name_get_impl(buffered *self) +/*[clinic end generated code: output=d2adf384051d3d10 input=6b84a0e6126f545e]*/ { CHECK_INITIALIZED(self) return PyObject_GetAttr(self->raw, &_Py_ID(name)); } -static PyObject * -buffered_name_get(buffered *self, void *context) -{ - PyObject *return_value = NULL; - - Py_BEGIN_CRITICAL_SECTION(self); - return_value = buffered_name_get_impl(self, context); - Py_END_CRITICAL_SECTION(); - - return return_value; -} +/*[clinic input] +@critical_section +@getter +_io._Buffered.mode +[clinic start generated code]*/ static PyObject * -buffered_mode_get_impl(buffered *self, void *context) +_io__Buffered_mode_get_impl(buffered *self) +/*[clinic end generated code: output=0feb205748892fa4 input=0762d5e28542fd8c]*/ { CHECK_INITIALIZED(self) return PyObject_GetAttr(self->raw, &_Py_ID(mode)); } -static PyObject * -buffered_mode_get(buffered *self, void *context) -{ - PyObject *return_value = NULL; - - Py_BEGIN_CRITICAL_SECTION(self); - return_value = buffered_mode_get_impl(self, context); - Py_END_CRITICAL_SECTION(); - - return return_value; -} - /* Lower-level APIs */ /*[clinic input] @@ -2541,9 +2526,9 @@ static PyMemberDef bufferedreader_members[] = { }; static PyGetSetDef bufferedreader_getset[] = { - {"closed", (getter)buffered_closed_get, NULL, NULL}, - {"name", (getter)buffered_name_get, NULL, NULL}, - {"mode", (getter)buffered_mode_get, NULL, NULL}, + _IO__BUFFERED_CLOSED_GETTERDEF + _IO__BUFFERED_NAME_GETTERDEF + _IO__BUFFERED_MODE_GETTERDEF {NULL} }; @@ -2601,9 +2586,9 @@ static PyMemberDef bufferedwriter_members[] = { }; static PyGetSetDef bufferedwriter_getset[] = { - {"closed", (getter)buffered_closed_get, NULL, NULL}, - {"name", (getter)buffered_name_get, NULL, NULL}, - {"mode", (getter)buffered_mode_get, NULL, NULL}, + _IO__BUFFERED_CLOSED_GETTERDEF + _IO__BUFFERED_NAME_GETTERDEF + _IO__BUFFERED_MODE_GETTERDEF {NULL} }; @@ -2719,9 +2704,9 @@ static PyMemberDef bufferedrandom_members[] = { }; static PyGetSetDef bufferedrandom_getset[] = { - {"closed", (getter)buffered_closed_get, NULL, NULL}, - {"name", (getter)buffered_name_get, NULL, NULL}, - {"mode", (getter)buffered_mode_get, NULL, NULL}, + _IO__BUFFERED_CLOSED_GETTERDEF + _IO__BUFFERED_NAME_GETTERDEF + _IO__BUFFERED_MODE_GETTERDEF {NULL} }; diff --git a/Modules/_io/clinic/bufferedio.c.h b/Modules/_io/clinic/bufferedio.c.h index 20833a10139681..69d28ad00c2ad5 100644 --- a/Modules/_io/clinic/bufferedio.c.h +++ b/Modules/_io/clinic/bufferedio.c.h @@ -327,6 +327,24 @@ _io__Buffered_simple_flush(buffered *self, PyObject *Py_UNUSED(ignored)) return return_value; } +#define _IO__BUFFERED_CLOSED_GETTERDEF \ + {"closed", (getter)_io__Buffered_closed_get, NULL, NULL}, + +static PyObject * +_io__Buffered_closed_get_impl(buffered *self); + +static PyObject * +_io__Buffered_closed_get(buffered *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_closed_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(_io__Buffered_close__doc__, "close($self, /)\n" "--\n" @@ -442,6 +460,42 @@ _io__Buffered_writable(buffered *self, PyObject *Py_UNUSED(ignored)) return return_value; } +#define _IO__BUFFERED_NAME_GETTERDEF \ + {"name", (getter)_io__Buffered_name_get, NULL, NULL}, + +static PyObject * +_io__Buffered_name_get_impl(buffered *self); + +static PyObject * +_io__Buffered_name_get(buffered *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_name_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#define _IO__BUFFERED_MODE_GETTERDEF \ + {"mode", (getter)_io__Buffered_mode_get, NULL, NULL}, + +static PyObject * +_io__Buffered_mode_get_impl(buffered *self); + +static PyObject * +_io__Buffered_mode_get(buffered *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_mode_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(_io__Buffered_fileno__doc__, "fileno($self, /)\n" "--\n" @@ -1164,4 +1218,4 @@ _io_BufferedRandom___init__(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=e8ad39a45531d7f2 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f21ed03255032b43 input=a9049054013a1b77]*/ diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index c0830864175adf..54962c9e1c92f9 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -846,6 +846,10 @@ class CLanguage(Language): static PyObject * {c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored)) """) + PARSER_PROTOTYPE_GETTER: Final[str] = normalize_snippet(""" + static PyObject * + {c_basename}({self_type}{self_name}, void *Py_UNUSED(context)) + """) METH_O_PROTOTYPE: Final[str] = normalize_snippet(""" static PyObject * {c_basename}({impl_parameters}) @@ -865,6 +869,10 @@ class CLanguage(Language): #define {methoddef_name} \ {{"{name}", {methoddef_cast}{c_basename}{methoddef_cast_end}, {methoddef_flags}, {c_basename}__doc__}}, """) + GETTERDEF_PROTOTYPE_DEFINE: Final[str] = normalize_snippet(r""" + #define {getter_name} \ + {{"{name}", (getter){c_basename}, NULL, NULL}}, + """) METHODDEF_PROTOTYPE_IFNDEF: Final[str] = normalize_snippet(""" #ifndef {methoddef_name} #define {methoddef_name} @@ -1161,6 +1169,9 @@ def output_templates( methoddef_define = self.METHODDEF_PROTOTYPE_DEFINE if new_or_init and not f.docstring: docstring_prototype = docstring_definition = '' + elif f.kind is GETTER: + methoddef_define = self.GETTERDEF_PROTOTYPE_DEFINE + docstring_prototype = docstring_definition = '' else: docstring_prototype = self.DOCSTRING_PROTOTYPE_VAR docstring_definition = self.DOCSTRING_PROTOTYPE_STRVAR @@ -1217,7 +1228,11 @@ def parser_body( parsearg: str | None if not parameters: parser_code: list[str] | None - if not requires_defining_class: + if f.kind is GETTER: + flags = "" # This should end up unused + parser_prototype = self.PARSER_PROTOTYPE_GETTER + parser_code = [] + elif not requires_defining_class: # no parameters, METH_NOARGS flags = "METH_NOARGS" parser_prototype = self.PARSER_PROTOTYPE_NOARGS @@ -1670,6 +1685,8 @@ def parser_body( methoddef_cast_end = "" if flags in ('METH_NOARGS', 'METH_O', 'METH_VARARGS'): methoddef_cast = "(PyCFunction)" + elif f.kind is GETTER: + methoddef_cast = "" # This should end up unused elif limited_capi: methoddef_cast = "(PyCFunction)(void(*)(void))" else: @@ -1927,8 +1944,12 @@ def render_function( full_name = f.full_name template_dict = {'full_name': full_name} template_dict['name'] = f.displayname - template_dict['c_basename'] = f.c_basename - template_dict['methoddef_name'] = f.c_basename.upper() + "_METHODDEF" + if f.kind is GETTER: + template_dict['getter_name'] = f.c_basename.upper() + "_GETTERDEF" + template_dict['c_basename'] = f.c_basename + "_get" + else: + template_dict['methoddef_name'] = f.c_basename.upper() + "_METHODDEF" + template_dict['c_basename'] = f.c_basename template_dict['docstring'] = self.docstring_for_c_string(f) @@ -2932,6 +2953,7 @@ class FunctionKind(enum.Enum): CLASS_METHOD = enum.auto() METHOD_INIT = enum.auto() METHOD_NEW = enum.auto() + GETTER = enum.auto() @functools.cached_property def new_or_init(self) -> bool: @@ -2947,6 +2969,7 @@ def __repr__(self) -> str: CLASS_METHOD: Final = FunctionKind.CLASS_METHOD METHOD_INIT: Final = FunctionKind.METHOD_INIT METHOD_NEW: Final = FunctionKind.METHOD_NEW +GETTER: Final = FunctionKind.GETTER ParamDict = dict[str, "Parameter"] ReturnConverterType = Callable[..., "CReturnConverter"] @@ -3033,7 +3056,8 @@ def methoddef_flags(self) -> str | None: case FunctionKind.STATIC_METHOD: flags.append('METH_STATIC') case _ as kind: - assert kind is FunctionKind.CALLABLE, f"unknown kind: {kind!r}" + acceptable_kinds = {FunctionKind.CALLABLE, FunctionKind.GETTER} + assert kind in acceptable_kinds, f"unknown kind: {kind!r}" if self.coexist: flags.append('METH_COEXIST') return '|'.join(flags) @@ -4678,7 +4702,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st def correct_name_for_self( f: Function ) -> tuple[str, str]: - if f.kind in (CALLABLE, METHOD_INIT): + if f.kind in {CALLABLE, METHOD_INIT, GETTER}: if f.cls: return "PyObject *", "self" return "PyObject *", "module" @@ -5310,6 +5334,9 @@ def at_critical_section(self, *args: str) -> None: self.target_critical_section.extend(args) self.critical_section = True + def at_getter(self) -> None: + self.kind = GETTER + def at_staticmethod(self) -> None: if self.kind is not CALLABLE: fail("Can't set @staticmethod, function is not a normal callable") @@ -5419,14 +5446,20 @@ def update_function_kind(self, fullname: str) -> None: _, cls = self.clinic._module_and_class(fields) if name in unsupported_special_methods: fail(f"{name!r} is a special method and cannot be converted to Argument Clinic!") + if name == '__new__': - if (self.kind is not CLASS_METHOD) or (not cls): + if (self.kind is CLASS_METHOD) and cls: + self.kind = METHOD_NEW + else: fail("'__new__' must be a class method!") - self.kind = METHOD_NEW elif name == '__init__': - if (self.kind is not CALLABLE) or (not cls): - fail("'__init__' must be a normal method, not a class or static method!") - self.kind = METHOD_INIT + if (self.kind is CALLABLE) and cls: + self.kind = METHOD_INIT + else: + fail( + "'__init__' must be a normal method; " + f"got '{self.kind}'!" + ) def state_modulename_name(self, line: str) -> None: # looking for declaration, which establishes the leftmost column From 07ebd46f9e55ed2f18c5ea2a79ec5054bc26b915 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Thu, 30 Nov 2023 11:03:30 +0000 Subject: [PATCH 107/228] gh-112519: Make it possible to specify instruction flags for pseudo instructions in bytecodes.c (#112520) --- Include/internal/pycore_opcode_metadata.h | 8 ++--- Lib/test/test_generated_cases.py | 38 +++++++++++++++++++++++ Python/bytecodes.c | 6 ++-- Python/flowgraph.c | 2 +- Tools/cases_generator/analysis.py | 9 ++++-- Tools/cases_generator/parsing.py | 25 +++++++++++++-- 6 files changed, 76 insertions(+), 12 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 4e45725d393479..4ae15e71e8d318 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1661,7 +1661,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE] = { [IMPORT_FROM] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [JUMP_FORWARD] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG }, [JUMP_BACKWARD] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [JUMP] = { true, 0, HAS_ARG_FLAG | HAS_JUMP_FLAG }, + [JUMP] = { true, 0, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [JUMP_NO_INTERRUPT] = { true, 0, HAS_ARG_FLAG | HAS_JUMP_FLAG }, [ENTER_EXECUTOR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG }, [_POP_JUMP_IF_FALSE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG }, @@ -1703,9 +1703,9 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE] = { [BEFORE_ASYNC_WITH] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [BEFORE_WITH] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [WITH_EXCEPT_START] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [SETUP_FINALLY] = { true, 0, 0 }, - [SETUP_CLEANUP] = { true, 0, 0 }, - [SETUP_WITH] = { true, 0, 0 }, + [SETUP_FINALLY] = { true, 0, HAS_ARG_FLAG }, + [SETUP_CLEANUP] = { true, 0, HAS_ARG_FLAG }, + [SETUP_WITH] = { true, 0, HAS_ARG_FLAG }, [POP_BLOCK] = { true, 0, 0 }, [PUSH_EXC_INFO] = { true, INSTR_FMT_IX, 0 }, [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG }, diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 98a8fff4268746..de96a8764594ba 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -466,6 +466,44 @@ def test_macro_instruction(self): """ self.run_cases_test(input, output) + def test_pseudo_instruction_no_flags(self): + input = """ + pseudo(OP) = { + OP1, + }; + + inst(OP1, (--)) { + } + """ + output = """ + TARGET(OP1) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(OP1); + DISPATCH(); + } + """ + self.run_cases_test(input, output) + + def test_pseudo_instruction_with_flags(self): + input = """ + pseudo(OP, (HAS_ARG, HAS_JUMP)) = { + OP1, + }; + + inst(OP1, (--)) { + } + """ + output = """ + TARGET(OP1) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(OP1); + DISPATCH(); + } + """ + self.run_cases_test(input, output) + def test_array_input(self): input = """ inst(OP, (below, values[oparg*2], above --)) { diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 2e5f6c8d0d10e2..2075c195df3d38 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2831,15 +2831,15 @@ dummy_func( ERROR_IF(res == NULL, error); } - pseudo(SETUP_FINALLY) = { + pseudo(SETUP_FINALLY, (HAS_ARG)) = { NOP, }; - pseudo(SETUP_CLEANUP) = { + pseudo(SETUP_CLEANUP, (HAS_ARG)) = { NOP, }; - pseudo(SETUP_WITH) = { + pseudo(SETUP_WITH, (HAS_ARG)) = { NOP, }; diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 87401e14f97f02..fe632082d5a66c 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -97,6 +97,7 @@ static const jump_target_label NO_LABEL = {-1}; static inline int is_block_push(cfg_instr *i) { + assert(OPCODE_HAS_ARG(i->i_opcode) || !IS_BLOCK_PUSH_OPCODE(i->i_opcode)); return IS_BLOCK_PUSH_OPCODE(i->i_opcode); } @@ -2239,7 +2240,6 @@ convert_pseudo_ops(basicblock *entryblock) for (int i = 0; i < b->b_iused; i++) { cfg_instr *instr = &b->b_instr[i]; if (is_block_push(instr) || instr->i_opcode == POP_BLOCK) { - assert(SAME_OPCODE_METADATA(instr->i_opcode, NOP)); INSTR_SET_OP0(instr, NOP); } else if (instr->i_opcode == LOAD_CLOSURE) { diff --git a/Tools/cases_generator/analysis.py b/Tools/cases_generator/analysis.py index 603b15596f16de..26d92c13cd82ab 100644 --- a/Tools/cases_generator/analysis.py +++ b/Tools/cases_generator/analysis.py @@ -390,9 +390,14 @@ def analyze_pseudo(self, pseudo: parsing.Pseudo) -> PseudoInstruction: else: targets.append(self.macro_instrs[target_name]) assert targets - ignored_flags = {"HAS_EVAL_BREAK_FLAG", "HAS_DEOPT_FLAG", "HAS_ERROR_FLAG", "HAS_ESCAPES_FLAG"} + ignored_flags = {"HAS_EVAL_BREAK_FLAG", "HAS_DEOPT_FLAG", "HAS_ERROR_FLAG", + "HAS_ESCAPES_FLAG"} assert len({t.instr_flags.bitmap(ignore=ignored_flags) for t in targets}) == 1 - return PseudoInstruction(pseudo.name, targets, targets[0].instr_flags) + + flags = InstructionFlags(**{f"{f}_FLAG" : True for f in pseudo.flags}) + for t in targets: + flags.add(t.instr_flags) + return PseudoInstruction(pseudo.name, targets, flags) def analyze_instruction( self, instr: Instruction, offset: int diff --git a/Tools/cases_generator/parsing.py b/Tools/cases_generator/parsing.py index d36bd52b022ea9..7800adf16794bb 100644 --- a/Tools/cases_generator/parsing.py +++ b/Tools/cases_generator/parsing.py @@ -138,7 +138,8 @@ class Family(Node): @dataclass class Pseudo(Node): name: str - targets: list[str] # opcodes this can be replaced by + flags: list[str] # instr flags to set on the pseudo instruction + targets: list[str] # opcodes this can be replaced by class Parser(PLexer): @@ -374,19 +375,39 @@ def family_def(self) -> Family | None: ) return None + def flags(self) -> list[str]: + here = self.getpos() + if self.expect(lx.LPAREN): + if tkn := self.expect(lx.IDENTIFIER): + flags = [tkn.text] + while self.expect(lx.COMMA): + if tkn := self.expect(lx.IDENTIFIER): + flags.append(tkn.text) + else: + break + if not self.expect(lx.RPAREN): + raise self.make_syntax_error("Expected comma or right paren") + return flags + self.setpos(here) + return [] + @contextual def pseudo_def(self) -> Pseudo | None: if (tkn := self.expect(lx.IDENTIFIER)) and tkn.text == "pseudo": size = None if self.expect(lx.LPAREN): if tkn := self.expect(lx.IDENTIFIER): + if self.expect(lx.COMMA): + flags = self.flags() + else: + flags = [] if self.expect(lx.RPAREN): if self.expect(lx.EQUALS): if not self.expect(lx.LBRACE): raise self.make_syntax_error("Expected {") if members := self.members(): if self.expect(lx.RBRACE) and self.expect(lx.SEMI): - return Pseudo(tkn.text, members) + return Pseudo(tkn.text, flags, members) return None def members(self) -> list[str] | None: From 1ff212debdc094c28928011cff9f4eea8de34d44 Mon Sep 17 00:00:00 2001 From: Matt Prodani Date: Thu, 30 Nov 2023 07:53:19 -0500 Subject: [PATCH 108/228] gh-111699: Move smtpd note to dedicated section in What's New Python 3.12 doc (GH-112544) Relocate smtpd deprecation notice to it's own section rather than under 'locale' in docs for What's New in Python 3.12 doc --- Doc/whatsnew/3.12.rst | 5 ++++- Misc/ACKS | 1 + .../2023-11-30-02-33-59.gh-issue-111699._O5G_y.rst | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Documentation/2023-11-30-02-33-59.gh-issue-111699._O5G_y.rst diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index a4b3a6d12970b4..96893527cc91ed 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -1640,7 +1640,10 @@ locale use :func:`locale.format_string` instead. (Contributed by Victor Stinner in :gh:`94226`.) -* ``smtpd``: The module has been removed according to the schedule in :pep:`594`, +smtpd +----- + +* The ``smtpd`` module has been removed according to the schedule in :pep:`594`, having been deprecated in Python 3.4.7 and 3.5.4. Use aiosmtpd_ PyPI module or any other :mod:`asyncio`-based server instead. diff --git a/Misc/ACKS b/Misc/ACKS index 5fe3a177a26292..1c67d96ed3a528 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1459,6 +1459,7 @@ Paul Prescod Donovan Preston Eric Price Paul Price +Matt Prodani Iuliia Proskurnia Dorian Pula Jyrki Pulliainen diff --git a/Misc/NEWS.d/next/Documentation/2023-11-30-02-33-59.gh-issue-111699._O5G_y.rst b/Misc/NEWS.d/next/Documentation/2023-11-30-02-33-59.gh-issue-111699._O5G_y.rst new file mode 100644 index 00000000000000..2d31345e6c2044 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-11-30-02-33-59.gh-issue-111699._O5G_y.rst @@ -0,0 +1 @@ +Relocate ``smtpd`` deprecation notice to its own section rather than under ``locale`` in What's New in Python 3.12 document From 2223899adce858a09ebeaaf82111e6cda9b42415 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 30 Nov 2023 17:22:04 +0200 Subject: [PATCH 109/228] gh-104231: Add more tests for str(), repr(), ascii(), and bytes() (GH-112551) --- Lib/test/test_bytes.py | 81 ++++++++++++++++++++++++++----------- Lib/test/test_str.py | 90 +++++++++++++++++++++++++++++------------- 2 files changed, 121 insertions(+), 50 deletions(-) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index afd506f07520d8..a3804a945f2e3a 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -46,6 +46,10 @@ def __index__(self): class BaseBytesTest: + def assertTypedEqual(self, actual, expected): + self.assertIs(type(actual), type(expected)) + self.assertEqual(actual, expected) + def test_basics(self): b = self.type2test() self.assertEqual(type(b), self.type2test) @@ -1023,36 +1027,63 @@ def test_buffer_is_readonly(self): self.assertRaises(TypeError, f.readinto, b"") def test_custom(self): - class A: - def __bytes__(self): - return b'abc' - self.assertEqual(bytes(A()), b'abc') - class A: pass - self.assertRaises(TypeError, bytes, A()) - class A: - def __bytes__(self): - return None - self.assertRaises(TypeError, bytes, A()) - class A: + self.assertEqual(bytes(BytesSubclass(b'abc')), b'abc') + self.assertEqual(BytesSubclass(OtherBytesSubclass(b'abc')), + BytesSubclass(b'abc')) + self.assertEqual(bytes(WithBytes(b'abc')), b'abc') + self.assertEqual(BytesSubclass(WithBytes(b'abc')), BytesSubclass(b'abc')) + + class NoBytes: pass + self.assertRaises(TypeError, bytes, NoBytes()) + self.assertRaises(TypeError, bytes, WithBytes('abc')) + self.assertRaises(TypeError, bytes, WithBytes(None)) + class IndexWithBytes: def __bytes__(self): return b'a' def __index__(self): return 42 - self.assertEqual(bytes(A()), b'a') + self.assertEqual(bytes(IndexWithBytes()), b'a') # Issue #25766 - class A(str): + class StrWithBytes(str): + def __new__(cls, value): + self = str.__new__(cls, '\u20ac') + self.value = value + return self def __bytes__(self): - return b'abc' - self.assertEqual(bytes(A('\u20ac')), b'abc') - self.assertEqual(bytes(A('\u20ac'), 'iso8859-15'), b'\xa4') + return self.value + self.assertEqual(bytes(StrWithBytes(b'abc')), b'abc') + self.assertEqual(bytes(StrWithBytes(b'abc'), 'iso8859-15'), b'\xa4') + self.assertEqual(bytes(StrWithBytes(BytesSubclass(b'abc'))), b'abc') + self.assertEqual(BytesSubclass(StrWithBytes(b'abc')), BytesSubclass(b'abc')) + self.assertEqual(BytesSubclass(StrWithBytes(b'abc'), 'iso8859-15'), + BytesSubclass(b'\xa4')) + self.assertEqual(BytesSubclass(StrWithBytes(BytesSubclass(b'abc'))), + BytesSubclass(b'abc')) + self.assertEqual(BytesSubclass(StrWithBytes(OtherBytesSubclass(b'abc'))), + BytesSubclass(b'abc')) # Issue #24731 - class A: + self.assertTypedEqual(bytes(WithBytes(BytesSubclass(b'abc'))), BytesSubclass(b'abc')) + self.assertTypedEqual(BytesSubclass(WithBytes(BytesSubclass(b'abc'))), + BytesSubclass(b'abc')) + self.assertTypedEqual(BytesSubclass(WithBytes(OtherBytesSubclass(b'abc'))), + BytesSubclass(b'abc')) + + class BytesWithBytes(bytes): + def __new__(cls, value): + self = bytes.__new__(cls, b'\xa4') + self.value = value + return self def __bytes__(self): - return OtherBytesSubclass(b'abc') - self.assertEqual(bytes(A()), b'abc') - self.assertIs(type(bytes(A())), OtherBytesSubclass) - self.assertEqual(BytesSubclass(A()), b'abc') - self.assertIs(type(BytesSubclass(A())), BytesSubclass) + return self.value + self.assertTypedEqual(bytes(BytesWithBytes(b'abc')), b'abc') + self.assertTypedEqual(BytesSubclass(BytesWithBytes(b'abc')), + BytesSubclass(b'abc')) + self.assertTypedEqual(bytes(BytesWithBytes(BytesSubclass(b'abc'))), + BytesSubclass(b'abc')) + self.assertTypedEqual(BytesSubclass(BytesWithBytes(BytesSubclass(b'abc'))), + BytesSubclass(b'abc')) + self.assertTypedEqual(BytesSubclass(BytesWithBytes(OtherBytesSubclass(b'abc'))), + BytesSubclass(b'abc')) # Test PyBytes_FromFormat() def test_from_format(self): @@ -2069,6 +2100,12 @@ class BytesSubclass(bytes): class OtherBytesSubclass(bytes): pass +class WithBytes: + def __init__(self, value): + self.value = value + def __bytes__(self): + return self.value + class ByteArraySubclassTest(SubclassTest, unittest.TestCase): basetype = bytearray type2test = ByteArraySubclass diff --git a/Lib/test/test_str.py b/Lib/test/test_str.py index 814ef111c5bec8..b4927113db44e3 100644 --- a/Lib/test/test_str.py +++ b/Lib/test/test_str.py @@ -55,6 +55,21 @@ def duplicate_string(text): class StrSubclass(str): pass +class OtherStrSubclass(str): + pass + +class WithStr: + def __init__(self, value): + self.value = value + def __str__(self): + return self.value + +class WithRepr: + def __init__(self, value): + self.value = value + def __repr__(self): + return self.value + class StrTest(string_tests.StringLikeTest, string_tests.MixinStrUnicodeTest, unittest.TestCase): @@ -83,6 +98,10 @@ def __repr__(self): self.assertEqual(realresult, result) self.assertTrue(object is not realresult) + def assertTypedEqual(self, actual, expected): + self.assertIs(type(actual), type(expected)) + self.assertEqual(actual, expected) + def test_literals(self): self.assertEqual('\xff', '\u00ff') self.assertEqual('\uffff', '\U0000ffff') @@ -127,10 +146,13 @@ def test_ascii(self): self.assertEqual(ascii("\U00010000" * 39 + "\uffff" * 4096), ascii("\U00010000" * 39 + "\uffff" * 4096)) - class WrongRepr: - def __repr__(self): - return b'byte-repr' - self.assertRaises(TypeError, ascii, WrongRepr()) + self.assertTypedEqual(ascii('\U0001f40d'), r"'\U0001f40d'") + self.assertTypedEqual(ascii(StrSubclass('abc')), "'abc'") + self.assertTypedEqual(ascii(WithRepr('')), '') + self.assertTypedEqual(ascii(WithRepr(StrSubclass(''))), StrSubclass('')) + self.assertTypedEqual(ascii(WithRepr('<\U0001f40d>')), r'<\U0001f40d>') + self.assertTypedEqual(ascii(WithRepr(StrSubclass('<\U0001f40d>'))), r'<\U0001f40d>') + self.assertRaises(TypeError, ascii, WithRepr(b'byte-repr')) def test_repr(self): # Test basic sanity of repr() @@ -168,10 +190,13 @@ def test_repr(self): self.assertEqual(repr("\U00010000" * 39 + "\uffff" * 4096), repr("\U00010000" * 39 + "\uffff" * 4096)) - class WrongRepr: - def __repr__(self): - return b'byte-repr' - self.assertRaises(TypeError, repr, WrongRepr()) + self.assertTypedEqual(repr('\U0001f40d'), "'\U0001f40d'") + self.assertTypedEqual(repr(StrSubclass('abc')), "'abc'") + self.assertTypedEqual(repr(WithRepr('')), '') + self.assertTypedEqual(repr(WithRepr(StrSubclass(''))), StrSubclass('')) + self.assertTypedEqual(repr(WithRepr('<\U0001f40d>')), '<\U0001f40d>') + self.assertTypedEqual(repr(WithRepr(StrSubclass('<\U0001f40d>'))), StrSubclass('<\U0001f40d>')) + self.assertRaises(TypeError, repr, WithRepr(b'byte-repr')) def test_iterators(self): # Make sure unicode objects have an __iter__ method @@ -2367,28 +2392,37 @@ def test_ucs4(self): def test_conversion(self): # Make sure __str__() works properly - class ObjectToStr: - def __str__(self): - return "foo" - - class StrSubclassToStr(str): - def __str__(self): - return "foo" - - class StrSubclassToStrSubclass(str): - def __new__(cls, content=""): - return str.__new__(cls, 2*content) - def __str__(self): + class StrWithStr(str): + def __new__(cls, value): + self = str.__new__(cls, "") + self.value = value return self + def __str__(self): + return self.value - self.assertEqual(str(ObjectToStr()), "foo") - self.assertEqual(str(StrSubclassToStr("bar")), "foo") - s = str(StrSubclassToStrSubclass("foo")) - self.assertEqual(s, "foofoo") - self.assertIs(type(s), StrSubclassToStrSubclass) - s = StrSubclass(StrSubclassToStrSubclass("foo")) - self.assertEqual(s, "foofoo") - self.assertIs(type(s), StrSubclass) + self.assertTypedEqual(str(WithStr('abc')), 'abc') + self.assertTypedEqual(str(WithStr(StrSubclass('abc'))), StrSubclass('abc')) + self.assertTypedEqual(StrSubclass(WithStr('abc')), StrSubclass('abc')) + self.assertTypedEqual(StrSubclass(WithStr(StrSubclass('abc'))), + StrSubclass('abc')) + self.assertTypedEqual(StrSubclass(WithStr(OtherStrSubclass('abc'))), + StrSubclass('abc')) + + self.assertTypedEqual(str(StrWithStr('abc')), 'abc') + self.assertTypedEqual(str(StrWithStr(StrSubclass('abc'))), StrSubclass('abc')) + self.assertTypedEqual(StrSubclass(StrWithStr('abc')), StrSubclass('abc')) + self.assertTypedEqual(StrSubclass(StrWithStr(StrSubclass('abc'))), + StrSubclass('abc')) + self.assertTypedEqual(StrSubclass(StrWithStr(OtherStrSubclass('abc'))), + StrSubclass('abc')) + + self.assertTypedEqual(str(WithRepr('')), '') + self.assertTypedEqual(str(WithRepr(StrSubclass(''))), StrSubclass('')) + self.assertTypedEqual(StrSubclass(WithRepr('')), StrSubclass('')) + self.assertTypedEqual(StrSubclass(WithRepr(StrSubclass(''))), + StrSubclass('')) + self.assertTypedEqual(StrSubclass(WithRepr(OtherStrSubclass(''))), + StrSubclass('')) def test_unicode_repr(self): class s1: From 482b0ee8f6cdecd96c246c8bcbda93292f4d08cc Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Thu, 30 Nov 2023 13:01:07 -0800 Subject: [PATCH 110/228] Clarify that WASI tool requirements are included in the devcontainer (GH-112561) --- Tools/wasm/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Tools/wasm/README.md b/Tools/wasm/README.md index acbece214ed9bd..beb857f69e40da 100644 --- a/Tools/wasm/README.md +++ b/Tools/wasm/README.md @@ -302,20 +302,20 @@ AddType application/wasm wasm ### Prerequisites -Developing for WASI requires two things to be installed: +Developing for WASI requires two additional tools to be installed beyond the typical tools required to build CPython: 1. The [WASI SDK](https://github.com/WebAssembly/wasi-sdk) 16.0+ - (see `.devcontainer/Dockerfile` for an example of how to download and install the WASI SDK) 2. A WASI host/runtime ([wasmtime](https://wasmtime.dev) 14+ is recommended and what the instructions below assume) +All of this is provided in the [devcontainer](https://devguide.python.org/getting-started/setup-building/#contribute-using-github-codespaces) if you don't want to install these tools locally. ### Building Building for WASI requires doing a cross-build where you have a "build" Python to help produce a WASI build of CPython (technically it's a "host x host" cross-build because the build Python is also the target Python while the host build is the WASI build; yes, it's confusing terminology). In the end you should have a build Python in `cross-build/build` and a WASI build in `cross-build/wasm32-wasi`. -The easiest way to do a build is to use the `wasi.py` script. You can either have it perform the entire build process from start to finish in one step, or you can do it in discrete steps that mirror running `configure` and `make` for each of the two builds of Python you end up producing (which are beneficial when you only need to do a specific step after getting a complete build, e.g. editing some code and you just need to run `make` for the WASI build). +The easiest way to do a build is to use the `wasi.py` script. You can either have it perform the entire build process from start to finish in one step, or you can do it in discrete steps that mirror running `configure` and `make` for each of the two builds of Python you end up producing (which are beneficial when you only need to do a specific step after getting a complete build, e.g. editing some code and you just need to run `make` for the WASI build). The script is designed to self-document what actions it is performing on your behalf, both as a way to check its work but also for educaitonal purposes. -The discrete steps are: +The discrete steps for building via `wasi.py` are: ```shell python Tools/wasm/wasi.py configure-build-python python Tools/wasm/wasi.py make-build-python @@ -323,7 +323,7 @@ python Tools/wasm/wasi.py configure-host python Tools/wasm/wasi.py make-host ``` -To do it in a single command, run: +To do it all in a single command, run: ```shell python Tools/wasm/wasi.py build ``` @@ -335,12 +335,12 @@ That will: 3. Run `configure` for the WASI build (`wasi.py configure-host`) 4. Run `make` for the WASI build (`wasi.py make-host`) -See the `--help` for the various options available for each of the subcommands which controls things like the location of the WASI SDK, the command to use with the WASI host/runtime, etc. Also note that you can use `--` as a separtor for any of the `configure`-related commands -- including `build` -- to pass arguments to `configure` itself. For example, if you want a pydebug build that also caches the results from `configure`, you can do: +See the `--help` for the various options available for each of the subcommands which controls things like the location of the WASI SDK, the command to use with the WASI host/runtime, etc. Also note that you can use `--` as a separator for any of the `configure`-related commands -- including `build` itself -- to pass arguments to the underlying `configure` call. For example, if you want a pydebug build that also caches the results from `configure`, you can do: ```shell python Tools/wasm/wasi.py build -- -C --with-pydebug ``` -The `wasi.py` script is able to infer details from the build Python, and so you only technically need to specify `--with-pydebug` once for `configure-build-python` and `configure-host` will detect its use if you use the discrete steps: +The `wasi.py` script is able to infer details from the build Python, and so you only technically need to specify `--with-pydebug` once via `configure-build-python` as this will lead to `configure-host` detecting its use if you use the discrete steps: ```shell python Tools/wasm/wasi.py configure-build-python -- -C --with-pydebug python Tools/wasm/wasi.py make-build-python @@ -359,7 +359,7 @@ cross-build/wasm32-wasi/python.sh --version While you _can_ run `python.wasm` directly, Python will fail to start up without certain things being set (e.g. `PYTHONPATH` for `sysconfig` data). As such, the `python.sh` file records these details for you. -## Detect WebAssembly builds +## Detecting WebAssembly builds ### Python code From 730d450d4334978f07e3cf39e1b320f2954e7963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alperen=20Serkan=20Aks=C3=B6z?= <61659936+Sekomer@users.noreply.github.com> Date: Fri, 1 Dec 2023 00:04:00 +0300 Subject: [PATCH 111/228] gh-112502: Docs: Improve docs for gc.collect method (#112562) * Docs: Improve docs for gc.collect method * Update gc.rst --- Doc/library/gc.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/gc.rst b/Doc/library/gc.rst index 331c071cda7692..82277aa52aee01 100644 --- a/Doc/library/gc.rst +++ b/Doc/library/gc.rst @@ -42,8 +42,8 @@ The :mod:`gc` module provides the following functions: With no arguments, run a full collection. The optional argument *generation* may be an integer specifying which generation to collect (from 0 to 2). A - :exc:`ValueError` is raised if the generation number is invalid. The number of - unreachable objects found is returned. + :exc:`ValueError` is raised if the generation number is invalid. The sum of + collected objects and uncollectable objects is returned. The free lists maintained for a number of built-in types are cleared whenever a full collection or collection of the highest generation (2) From 6d5e0dc0e330f4009e8dc3d1642e46b129788877 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Thu, 30 Nov 2023 13:38:10 -0800 Subject: [PATCH 112/228] Clarify a comment for `test.support.Py_C_RECURSION_LIMIT` to point out where a value came from but that it doesn't need to stay in sync (#112224) --- Lib/test/support/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index eec5498e633eb6..318a0599a75acd 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2380,7 +2380,8 @@ def _get_c_recursion_limit(): import _testcapi return _testcapi.Py_C_RECURSION_LIMIT except (ImportError, AttributeError): - return 1500 # (from Include/cpython/pystate.h) + # Originally taken from Include/cpython/pystate.h . + return 1500 # The default C recursion limit. Py_C_RECURSION_LIMIT = _get_c_recursion_limit() From 674c288b1c29b5d838c0cb6de0ea7a64caf294ff Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 30 Nov 2023 23:00:14 +0000 Subject: [PATCH 113/228] gh-109413: Run mypy on `libregrtest` in CI (#112558) Co-authored-by: Hugo van Kemenade --- .github/workflows/mypy.yml | 2 ++ Lib/test/libregrtest/cmdline.py | 4 ++-- Lib/test/libregrtest/main.py | 12 +++++++++--- Lib/test/libregrtest/mypy.ini | 2 +- Lib/test/libregrtest/refleak.py | 3 ++- Lib/test/libregrtest/results.py | 2 +- Lib/test/libregrtest/run_workers.py | 9 ++++++--- Lib/test/libregrtest/runtests.py | 3 ++- Lib/test/libregrtest/setup.py | 3 ++- Lib/test/libregrtest/utils.py | 9 +++++---- 10 files changed, 32 insertions(+), 17 deletions(-) diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 405511ca6820b3..72ae67aa02aa96 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -8,6 +8,7 @@ on: pull_request: paths: - ".github/workflows/mypy.yml" + - "Lib/test/libregrtest/**" - "Tools/cases_generator/**" - "Tools/clinic/**" - "Tools/peg_generator/**" @@ -32,6 +33,7 @@ jobs: strategy: matrix: target: [ + "Lib/test/libregrtest", "Tools/cases_generator", "Tools/clinic", "Tools/peg_generator", diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index a5f02d6335f58f..0053bce4292f64 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -3,7 +3,7 @@ import shlex import sys from test.support import os_helper, Py_DEBUG -from .utils import ALL_RESOURCES, RESOURCE_NAMES +from .utils import ALL_RESOURCES, RESOURCE_NAMES, TestFilter USAGE = """\ @@ -161,7 +161,7 @@ def __init__(self, **kwargs) -> None: self.forever = False self.header = False self.failfast = False - self.match_tests = [] + self.match_tests: TestFilter = [] self.pgo = False self.pgo_extended = False self.worker_json = None diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 86428945a6def2..55fc3a820d3451 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -295,7 +295,9 @@ def run_test( namespace = dict(locals()) tracer.runctx(cmd, globals=globals(), locals=namespace) result = namespace['result'] - result.covered_lines = list(tracer.counts) + # Mypy doesn't know about this attribute yet, + # but it will do soon: https://github.com/python/typeshed/pull/11091 + result.covered_lines = list(tracer.counts) # type: ignore[attr-defined] else: result = run_single_test(test_name, runtests) @@ -371,7 +373,8 @@ def finalize_tests(self, coverage: trace.CoverageResults | None) -> None: os.unlink(self.next_single_filename) if coverage is not None: - coverage.write_results(show_missing=True, summary=True, + # uses a new-in-Python 3.13 keyword argument that mypy doesn't know about yet: + coverage.write_results(show_missing=True, summary=True, # type: ignore[call-arg] coverdir=self.coverage_dir, ignore_missing_files=True) @@ -432,7 +435,10 @@ def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int: if self.num_workers < 0: # Use all CPUs + 2 extra worker processes for tests # that like to sleep - self.num_workers = (os.process_cpu_count() or 1) + 2 + # + # os.process.cpu_count() is new in Python 3.13; + # mypy doesn't know about it yet + self.num_workers = (os.process_cpu_count() or 1) + 2 # type: ignore[attr-defined] # For a partial run, we do not need to clutter the output. if (self.want_header diff --git a/Lib/test/libregrtest/mypy.ini b/Lib/test/libregrtest/mypy.ini index fefc347728a701..331fe681b9f56b 100644 --- a/Lib/test/libregrtest/mypy.ini +++ b/Lib/test/libregrtest/mypy.ini @@ -5,7 +5,7 @@ [mypy] files = Lib/test/libregrtest explicit_package_bases = True -python_version = 3.11 +python_version = 3.12 platform = linux pretty = True diff --git a/Lib/test/libregrtest/refleak.py b/Lib/test/libregrtest/refleak.py index ada1a65b867ee6..5836a8421cb42d 100644 --- a/Lib/test/libregrtest/refleak.py +++ b/Lib/test/libregrtest/refleak.py @@ -52,7 +52,8 @@ def runtest_refleak(test_name, test_func, except ImportError: zdc = None # Run unmodified on platforms without zipimport support else: - zdc = zipimport._zip_directory_cache.copy() + # private attribute that mypy doesn't know about: + zdc = zipimport._zip_directory_cache.copy() # type: ignore[attr-defined] abcs = {} for abc in [getattr(collections.abc, a) for a in collections.abc.__all__]: if not isabstract(abc): diff --git a/Lib/test/libregrtest/results.py b/Lib/test/libregrtest/results.py index 71aaef3ae9ae61..59a566c032847e 100644 --- a/Lib/test/libregrtest/results.py +++ b/Lib/test/libregrtest/results.py @@ -34,7 +34,7 @@ def __init__(self): self.test_times: list[tuple[float, TestName]] = [] self.stats = TestStats() # used by --junit-xml - self.testsuite_xml: list[str] = [] + self.testsuite_xml: list = [] # used by -T with -j self.covered_lines: set[Location] = set() diff --git a/Lib/test/libregrtest/run_workers.py b/Lib/test/libregrtest/run_workers.py index 99c2cf34d206d0..35aaf90ffc4299 100644 --- a/Lib/test/libregrtest/run_workers.py +++ b/Lib/test/libregrtest/run_workers.py @@ -10,7 +10,7 @@ import threading import time import traceback -from typing import Literal, TextIO +from typing import Any, Literal, TextIO from test import support from test.support import os_helper, MS_WINDOWS @@ -243,7 +243,9 @@ def create_json_file(self, stack: contextlib.ExitStack) -> tuple[JsonFile, TextI json_fd = json_tmpfile.fileno() if MS_WINDOWS: - json_handle = msvcrt.get_osfhandle(json_fd) + # The msvcrt module is only available on Windows; + # we run mypy with `--platform=linux` in CI + json_handle: int = msvcrt.get_osfhandle(json_fd) # type: ignore[attr-defined] json_file = JsonFile(json_handle, JsonFileType.WINDOWS_HANDLE) else: @@ -259,7 +261,7 @@ def create_worker_runtests(self, test_name: TestName, json_file: JsonFile) -> Ru else: match_tests = None - kwargs = {} + kwargs: dict[str, Any] = {} if match_tests: kwargs['match_tests'] = [(test, True) for test in match_tests] if self.runtests.output_on_failure: @@ -345,6 +347,7 @@ def _runtest(self, test_name: TestName) -> MultiprocessResult: json_file, json_tmpfile = self.create_json_file(stack) worker_runtests = self.create_worker_runtests(test_name, json_file) + retcode: str | int | None retcode, tmp_files = self.run_tmp_files(worker_runtests, stdout_file.fileno()) diff --git a/Lib/test/libregrtest/runtests.py b/Lib/test/libregrtest/runtests.py index ac47c07f8d4341..b765ba5b41d236 100644 --- a/Lib/test/libregrtest/runtests.py +++ b/Lib/test/libregrtest/runtests.py @@ -33,7 +33,8 @@ def configure_subprocess(self, popen_kwargs: dict) -> None: popen_kwargs['pass_fds'] = [self.file] case JsonFileType.WINDOWS_HANDLE: # Windows handle - startupinfo = subprocess.STARTUPINFO() + # We run mypy with `--platform=linux` so it complains about this: + startupinfo = subprocess.STARTUPINFO() # type: ignore[attr-defined] startupinfo.lpAttributeList = {"handle_list": [self.file]} popen_kwargs['startupinfo'] = startupinfo diff --git a/Lib/test/libregrtest/setup.py b/Lib/test/libregrtest/setup.py index 97edba9f87d7f9..9e9741493e9a5b 100644 --- a/Lib/test/libregrtest/setup.py +++ b/Lib/test/libregrtest/setup.py @@ -124,7 +124,8 @@ def setup_tests(runtests: RunTests): support.LONG_TIMEOUT = min(support.LONG_TIMEOUT, timeout) if runtests.hunt_refleak: - unittest.BaseTestSuite._cleanup = False + # private attribute that mypy doesn't know about: + unittest.BaseTestSuite._cleanup = False # type: ignore[attr-defined] if runtests.gc_threshold is not None: gc.set_threshold(runtests.gc_threshold) diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index e4a28af381ee2d..d47e9388e62db2 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -12,7 +12,7 @@ import sysconfig import tempfile import textwrap -from collections.abc import Callable +from collections.abc import Callable, Iterable from test import support from test.support import os_helper @@ -547,7 +547,7 @@ def is_cross_compiled(): return ('_PYTHON_HOST_PLATFORM' in os.environ) -def format_resources(use_resources: tuple[str, ...]): +def format_resources(use_resources: Iterable[str]): use_resources = set(use_resources) all_resources = set(ALL_RESOURCES) @@ -580,9 +580,10 @@ def display_header(use_resources: tuple[str, ...], print("== Python build:", ' '.join(get_build_info())) print("== cwd:", os.getcwd()) - cpu_count = os.cpu_count() + cpu_count: object = os.cpu_count() if cpu_count: - process_cpu_count = os.process_cpu_count() + # The function is new in Python 3.13; mypy doesn't know about it yet: + process_cpu_count = os.process_cpu_count() # type: ignore[attr-defined] if process_cpu_count and process_cpu_count != cpu_count: cpu_count = f"{process_cpu_count} (process) / {cpu_count} (system)" print("== CPU count:", cpu_count) From 5b0629966f47542527400b03498d5156846f0da6 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Thu, 30 Nov 2023 23:37:30 +0000 Subject: [PATCH 114/228] gh-112205: Update stringio module to use AC for the thread-safe (gh-112549) --- Modules/_io/clinic/stringio.c.h | 56 ++++++++++++++++++++++++++++++- Modules/_io/stringio.c | 58 ++++++++++++++------------------- 2 files changed, 79 insertions(+), 35 deletions(-) diff --git a/Modules/_io/clinic/stringio.c.h b/Modules/_io/clinic/stringio.c.h index 8e5c687dc6a55f..ed505ae67589a8 100644 --- a/Modules/_io/clinic/stringio.c.h +++ b/Modules/_io/clinic/stringio.c.h @@ -474,4 +474,58 @@ _io_StringIO___setstate__(stringio *self, PyObject *state) return return_value; } -/*[clinic end generated code: output=5c8d67f4408a1e6e input=a9049054013a1b77]*/ + +#define _IO_STRINGIO_CLOSED_GETTERDEF \ + {"closed", (getter)_io_StringIO_closed_get, NULL, NULL}, + +static PyObject * +_io_StringIO_closed_get_impl(stringio *self); + +static PyObject * +_io_StringIO_closed_get(stringio *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_closed_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#define _IO_STRINGIO_LINE_BUFFERING_GETTERDEF \ + {"line_buffering", (getter)_io_StringIO_line_buffering_get, NULL, NULL}, + +static PyObject * +_io_StringIO_line_buffering_get_impl(stringio *self); + +static PyObject * +_io_StringIO_line_buffering_get(stringio *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_line_buffering_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#define _IO_STRINGIO_NEWLINES_GETTERDEF \ + {"newlines", (getter)_io_StringIO_newlines_get, NULL, NULL}, + +static PyObject * +_io_StringIO_newlines_get_impl(stringio *self); + +static PyObject * +_io_StringIO_newlines_get(stringio *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_newlines_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} +/*[clinic end generated code: output=3a92e8b6c322f61b input=a9049054013a1b77]*/ diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index 0aa5e34cd7c8b2..74dcee23730306 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -970,44 +970,44 @@ _io_StringIO___setstate___impl(stringio *self, PyObject *state) Py_RETURN_NONE; } +/*[clinic input] +@critical_section +@getter +_io.StringIO.closed +[clinic start generated code]*/ static PyObject * -stringio_closed_impl(stringio *self, void *context) +_io_StringIO_closed_get_impl(stringio *self) +/*[clinic end generated code: output=531ddca7954331d6 input=178d2ef24395fd49]*/ { CHECK_INITIALIZED(self); return PyBool_FromLong(self->closed); } -static PyObject * -stringio_closed(stringio *self, void *context) -{ - PyObject *result; - Py_BEGIN_CRITICAL_SECTION(self); - result = stringio_closed_impl(self, context); - Py_END_CRITICAL_SECTION(); - return result; -} +/*[clinic input] +@critical_section +@getter +_io.StringIO.line_buffering +[clinic start generated code]*/ static PyObject * -stringio_line_buffering_impl(stringio *self, void *context) +_io_StringIO_line_buffering_get_impl(stringio *self) +/*[clinic end generated code: output=360710e0112966ae input=6a7634e7f890745e]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); Py_RETURN_FALSE; } -static PyObject * -stringio_line_buffering(stringio *self, void *context) -{ - PyObject *result; - Py_BEGIN_CRITICAL_SECTION(self); - result = stringio_line_buffering_impl(self, context); - Py_END_CRITICAL_SECTION(); - return result; -} +/*[clinic input] +@critical_section +@getter +_io.StringIO.newlines +[clinic start generated code]*/ static PyObject * -stringio_newlines_impl(stringio *self, void *context) +_io_StringIO_newlines_get_impl(stringio *self) +/*[clinic end generated code: output=35d7c0b66d7e0160 input=092a14586718244b]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -1017,16 +1017,6 @@ stringio_newlines_impl(stringio *self, void *context) return PyObject_GetAttr(self->decoder, &_Py_ID(newlines)); } -static PyObject * -stringio_newlines(stringio *self, void *context) -{ - PyObject *result; - Py_BEGIN_CRITICAL_SECTION(self); - result = stringio_newlines_impl(self, context); - Py_END_CRITICAL_SECTION(); - return result; -} - static struct PyMethodDef stringio_methods[] = { _IO_STRINGIO_CLOSE_METHODDEF _IO_STRINGIO_GETVALUE_METHODDEF @@ -1047,15 +1037,15 @@ static struct PyMethodDef stringio_methods[] = { }; static PyGetSetDef stringio_getset[] = { - {"closed", (getter)stringio_closed, NULL, NULL}, - {"newlines", (getter)stringio_newlines, NULL, NULL}, + _IO_STRINGIO_CLOSED_GETTERDEF + _IO_STRINGIO_NEWLINES_GETTERDEF /* (following comments straight off of the original Python wrapper:) XXX Cruft to support the TextIOWrapper API. This would only be meaningful if StringIO supported the buffer attribute. Hopefully, a better solution, than adding these pseudo-attributes, will be found. */ - {"line_buffering", (getter)stringio_line_buffering, NULL, NULL}, + _IO_STRINGIO_LINE_BUFFERING_GETTERDEF {NULL} }; From e44f1940bd6d2ba4a3f8ac4585b3cf4f9cb93e49 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 1 Dec 2023 02:02:31 -0500 Subject: [PATCH 115/228] gh-66819: More IDLE htest updates (#112574) Revise htest.py docstring and move 2 specs to alphabetical position. --- Lib/idlelib/idle_test/htest.py | 107 ++++++++++++++++----------------- 1 file changed, 52 insertions(+), 55 deletions(-) diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index 595e51d3699f6e..e21ab98d8aab89 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -1,38 +1,35 @@ -'''Run human tests of Idle's window, dialog, and popup widgets. - -run(*tests) -Create a master Tk window. Within that, run each callable in tests -after finding the matching test spec in this file. If tests is empty, -run an htest for each spec dict in this file after finding the matching -callable in the module named in the spec. Close the window to skip or -end the test. - -In a tested module, let X be a global name bound to a callable (class -or function) whose .__name__ attribute is also X (the usual situation). -The first parameter of X must be 'parent'. When called, the parent -argument will be the root window. X must create a child Toplevel -window (or subclass thereof). The Toplevel may be a test widget or -dialog, in which case the callable is the corresponding class. Or the -Toplevel may contain the widget to be tested or set up a context in -which a test widget is invoked. In this latter case, the callable is a -wrapper function that sets up the Toplevel and other objects. Wrapper -function names, such as _editor_window', should start with '_'. +"""Run human tests of Idle's window, dialog, and popup widgets. + +run(*tests) Create a master Tk() htest window. Within that, run each +callable in tests after finding the matching test spec in this file. If +tests is empty, run an htest for each spec dict in this file after +finding the matching callable in the module named in the spec. Close +the master window to end testing. + +In a tested module, let X be a global name bound to a callable (class or +function) whose .__name__ attribute is also X (the usual situation). The +first parameter of X must be 'parent'. When called, the parent argument +will be the root window. X must create a child Toplevel(parent) window +(or subclass thereof). The Toplevel may be a test widget or dialog, in +which case the callable is the corresponding class. Or the Toplevel may +contain the widget to be tested or set up a context in which a test +widget is invoked. In this latter case, the callable is a wrapper +function that sets up the Toplevel and other objects. Wrapper function +names, such as _editor_window', should start with '_' and be lowercase. End the module with if __name__ == '__main__': - + from idlelib.idle_test.htest import run - run(X) + run(callable) # There could be multiple comma-separated callables. -To have wrapper functions and test invocation code ignored by coveragepy -reports, put '# htest #' on the def statement header line. - -def _wrapper(parent): # htest # - -Also make sure that the 'if __name__' line matches the above. Then have -make sure that .coveragerc includes the following. +To have wrapper functions ignored by coverage reports, tag the def +header like so: "def _wrapper(parent): # htest #". Use the same tag +for htest lines in widget code. Make sure that the 'if __name__' line +matches the above. Then have make sure that .coveragerc includes the +following: [report] exclude_lines = @@ -46,7 +43,7 @@ def _wrapper(parent): # htest # following template, with X.__name__ prepended to '_spec'. When all tests are run, the prefix is use to get X. -_spec = { +callable_spec = { 'file': '', 'kwds': {'title': ''}, 'msg': "" @@ -54,16 +51,16 @@ def _wrapper(parent): # htest # file (no .py): run() imports file.py. kwds: augmented with {'parent':root} and passed to X as **kwds. -title: an example kwd; some widgets need this, delete if not. +title: an example kwd; some widgets need this, delete line if not. msg: master window hints about testing the widget. -Modules and classes not being tested at the moment: -pyshell.PyShellEditorWindow -debugger.Debugger -autocomplete_w.AutoCompleteWindow -outwin.OutputWindow (indirectly being tested with grep test) -''' +TODO test these modules and classes: + autocomplete_w.AutoCompleteWindow + debugger.Debugger + outwin.OutputWindow (indirectly being tested with grep test) + pyshell.PyShellEditorWindow +""" import idlelib.pyshell # Set Windows DPI awareness before Tk(). from importlib import import_module @@ -91,15 +88,6 @@ def _wrapper(parent): # htest # "Force-open-calltip does not work here.\n" } -_module_browser_spec = { - 'file': 'browser', - 'kwds': {}, - 'msg': "Inspect names of module, class(with superclass if " - "applicable), methods and functions.\nToggle nested items.\n" - "Double clicking on items prints a traceback for an exception " - "that is ignored." - } - _color_delegator_spec = { 'file': 'colorizer', 'kwds': {}, @@ -109,16 +97,6 @@ def _wrapper(parent): # htest # "The default color scheme is in idlelib/config-highlight.def" } -CustomRun_spec = { - 'file': 'query', - 'kwds': {'title': 'Customize query.py Run', - '_htest': True}, - 'msg': "Enter with or [Run]. Print valid entry to Shell\n" - "Arguments are parsed into a list\n" - "Mode is currently restart True or False\n" - "Close dialog with valid entry, , [Cancel], [X]" - } - ConfigDialog_spec = { 'file': 'configdialog', 'kwds': {'title': 'ConfigDialogTest', @@ -135,6 +113,16 @@ def _wrapper(parent): # htest # "changes made have persisted." } +CustomRun_spec = { + 'file': 'query', + 'kwds': {'title': 'Customize query.py Run', + '_htest': True}, + 'msg': "Enter with or [Run]. Print valid entry to Shell\n" + "Arguments are parsed into a list\n" + "Mode is currently restart True or False\n" + "Close dialog with valid entry, , [Cancel], [X]" + } + # TODO Improve message _dyn_option_menu_spec = { 'file': 'dynoption', @@ -236,6 +224,15 @@ def _wrapper(parent): # htest # "focusing out of the window\nare sequences to be tested." } +_module_browser_spec = { + 'file': 'browser', + 'kwds': {}, + 'msg': "Inspect names of module, class(with superclass if " + "applicable), methods and functions.\nToggle nested items.\n" + "Double clicking on items prints a traceback for an exception " + "that is ignored." + } + _multistatus_bar_spec = { 'file': 'statusbar', 'kwds': {}, From f6afa426d8e84d9bac0f7caaf11ea47759b8389c Mon Sep 17 00:00:00 2001 From: William Andrea Date: Fri, 1 Dec 2023 03:41:11 -0500 Subject: [PATCH 116/228] Add links under "generator expression" in glossary (#112537) --- Doc/glossary.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 6b517b95f97013..29f2f80cebd5f0 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -502,7 +502,7 @@ Glossary .. index:: single: generator expression generator expression - An expression that returns an iterator. It looks like a normal expression + An :term:`expression` that returns an :term:`iterator`. It looks like a normal expression followed by a :keyword:`!for` clause defining a loop variable, range, and an optional :keyword:`!if` clause. The combined expression generates values for an enclosing function:: From 19a86148e6dc20752283536c3a41a7f997ddd36e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 09:21:45 +0000 Subject: [PATCH 117/228] build(deps-dev): bump mypy from 1.7.0 to 1.7.1 in /Tools (#112581) Bumps [mypy](https://github.com/python/mypy) from 1.7.0 to 1.7.1. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/v1.7.0...v1.7.1) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Tools/requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/requirements-dev.txt b/Tools/requirements-dev.txt index 591baac33c7e8f..4107dc823e7aaa 100644 --- a/Tools/requirements-dev.txt +++ b/Tools/requirements-dev.txt @@ -1,6 +1,6 @@ # Requirements file for external linters and checks we run on # Tools/clinic, Tools/cases_generator/, and Tools/peg_generator/ in CI -mypy==1.7.0 +mypy==1.7.1 # needed for peg_generator: types-psutil==5.9.5.17 From 467e3f94171f84221272011d40e4ee32fba47f73 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 09:29:04 +0000 Subject: [PATCH 118/228] build(deps-dev): bump types-setuptools from 68.2.0.0 to 69.0.0.0 in /Tools (#112582) build(deps-dev): bump types-setuptools in /Tools Bumps [types-setuptools](https://github.com/python/typeshed) from 68.2.0.0 to 69.0.0.0. - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-setuptools dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Tools/requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/requirements-dev.txt b/Tools/requirements-dev.txt index 4107dc823e7aaa..3a2e62f70bbb60 100644 --- a/Tools/requirements-dev.txt +++ b/Tools/requirements-dev.txt @@ -4,4 +4,4 @@ mypy==1.7.1 # needed for peg_generator: types-psutil==5.9.5.17 -types-setuptools==68.2.0.0 +types-setuptools==69.0.0.0 From 707c37e373d7ea4e3f06b24c719fa45f70fbfa49 Mon Sep 17 00:00:00 2001 From: Yang Hau Date: Fri, 1 Dec 2023 17:37:40 +0800 Subject: [PATCH 119/228] Fix typos in variable names, function names, and comments (GH-101868) --- Lib/zipimport.py | 2 +- Mac/BuildScript/build-installer.py | 2 +- PC/launcher2.c | 2 +- Parser/pegen_errors.c | 2 +- Tools/build/stable_abi.py | 12 ++++++------ Tools/c-analyzer/c_analyzer/__init__.py | 2 +- Tools/clinic/clinic.py | 4 ++-- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Lib/zipimport.py b/Lib/zipimport.py index 5b9f614f02f7af..823a82ee830465 100644 --- a/Lib/zipimport.py +++ b/Lib/zipimport.py @@ -352,7 +352,7 @@ def _read_directory(archive): with fp: # GH-87235: On macOS all file descriptors for /dev/fd/N share the same - # file offset, reset the file offset after scanning the zipfile diretory + # file offset, reset the file offset after scanning the zipfile directory # to not cause problems when some runs 'python3 /dev/fd/9 9 None: flush() return tuple(version) -def version_comparitor(version1: str, version2: str) -> Literal[-1, 0, 1]: +def version_comparator(version1: str, version2: str) -> Literal[-1, 0, 1]: iterator = itertools.zip_longest( version_splitter(version1), version_splitter(version2), fillvalue=0 ) @@ -5203,7 +5203,7 @@ def reset(self) -> None: def directive_version(self, required: str) -> None: global version - if version_comparitor(version, required) < 0: + if version_comparator(version, required) < 0: fail("Insufficient Clinic version!\n" f" Version: {version}\n" f" Required: {required}") From f21e2f4b12298210d7ef60be97c7d2e6b07070d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 11:41:09 +0100 Subject: [PATCH 120/228] build(deps): bump actions/github-script from 6 to 7 (#112584) Bumps [actions/github-script](https://github.com/actions/github-script) from 6 to 7. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/github-script dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/add-issue-header.yml | 2 +- .github/workflows/new-bugs-announce-notifier.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/add-issue-header.yml b/.github/workflows/add-issue-header.yml index 1ef9178b95e5f6..570b8779994a0f 100644 --- a/.github/workflows/add-issue-header.yml +++ b/.github/workflows/add-issue-header.yml @@ -19,7 +19,7 @@ jobs: permissions: issues: write steps: - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 with: # language=JavaScript script: | diff --git a/.github/workflows/new-bugs-announce-notifier.yml b/.github/workflows/new-bugs-announce-notifier.yml index 4599b21ef35f05..9f1a8a824e5f19 100644 --- a/.github/workflows/new-bugs-announce-notifier.yml +++ b/.github/workflows/new-bugs-announce-notifier.yml @@ -18,7 +18,7 @@ jobs: node-version: 20 - run: npm install mailgun.js form-data - name: Send notification - uses: actions/github-script@v6 + uses: actions/github-script@v7 env: MAILGUN_API_KEY: ${{ secrets.MAILGUN_PYTHON_ORG_MAILGUN_KEY }} with: From 847e4fe0e81f0e6e54ef52a9be63e3fb74b0779a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 11:17:24 +0000 Subject: [PATCH 121/228] build(deps): bump hypothesis from 6.88.1 to 6.91.0 in /Tools (#112580) Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.88.1 to 6.91.0. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.88.1...hypothesis-python-6.91.0) --- updated-dependencies: - dependency-name: hypothesis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Tools/requirements-hypothesis.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/requirements-hypothesis.txt b/Tools/requirements-hypothesis.txt index 0fcc72ac69073d..1bca5d2367f4b2 100644 --- a/Tools/requirements-hypothesis.txt +++ b/Tools/requirements-hypothesis.txt @@ -1,4 +1,4 @@ # Requirements file for hypothesis that # we use to run our property-based tests in CI. -hypothesis==6.88.1 +hypothesis==6.91.0 From a65a3d4806a4087f229b5ab6ab28d3e0b0a2d840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Lapeyre?= Date: Fri, 1 Dec 2023 12:17:47 +0100 Subject: [PATCH 122/228] bpo-39912: Raise appropriate exceptions in filterwarnings() and simplefilter() (GH-18878) --- Lib/test/test_warnings/__init__.py | 22 ++++++++++++++ Lib/warnings.py | 30 +++++++++++-------- .../2020-03-09-15-08-29.bpo-39912.xPOBBY.rst | 3 ++ 3 files changed, 43 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-03-09-15-08-29.bpo-39912.xPOBBY.rst diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index cd989fe36bf26b..232480c46e0a00 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -375,6 +375,28 @@ def test_append_duplicate(self): "appended duplicate changed order of filters" ) + def test_argument_validation(self): + with self.assertRaises(ValueError): + self.module.filterwarnings(action='foo') + with self.assertRaises(TypeError): + self.module.filterwarnings('ignore', message=0) + with self.assertRaises(TypeError): + self.module.filterwarnings('ignore', category=0) + with self.assertRaises(TypeError): + self.module.filterwarnings('ignore', category=int) + with self.assertRaises(TypeError): + self.module.filterwarnings('ignore', module=0) + with self.assertRaises(TypeError): + self.module.filterwarnings('ignore', lineno=int) + with self.assertRaises(ValueError): + self.module.filterwarnings('ignore', lineno=-1) + with self.assertRaises(ValueError): + self.module.simplefilter(action='foo') + with self.assertRaises(TypeError): + self.module.simplefilter('ignore', lineno=int) + with self.assertRaises(ValueError): + self.module.simplefilter('ignore', lineno=-1) + def test_catchwarnings_with_simplefilter_ignore(self): with original_warnings.catch_warnings(module=self.module): self.module.resetwarnings() diff --git a/Lib/warnings.py b/Lib/warnings.py index 924f872172d4d1..b8ff078569d2ce 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -139,14 +139,18 @@ def filterwarnings(action, message="", category=Warning, module="", lineno=0, 'lineno' -- an integer line number, 0 matches all warnings 'append' -- if true, append to the list of filters """ - assert action in ("error", "ignore", "always", "default", "module", - "once"), "invalid action: %r" % (action,) - assert isinstance(message, str), "message must be a string" - assert isinstance(category, type), "category must be a class" - assert issubclass(category, Warning), "category must be a Warning subclass" - assert isinstance(module, str), "module must be a string" - assert isinstance(lineno, int) and lineno >= 0, \ - "lineno must be an int >= 0" + if action not in {"error", "ignore", "always", "default", "module", "once"}: + raise ValueError(f"invalid action: {action!r}") + if not isinstance(message, str): + raise TypeError("message must be a string") + if not isinstance(category, type) or not issubclass(category, Warning): + raise TypeError("category must be a Warning subclass") + if not isinstance(module, str): + raise TypeError("module must be a string") + if not isinstance(lineno, int): + raise TypeError("lineno must be an int") + if lineno < 0: + raise ValueError("lineno must be an int >= 0") if message or module: import re @@ -172,10 +176,12 @@ def simplefilter(action, category=Warning, lineno=0, append=False): 'lineno' -- an integer line number, 0 matches all warnings 'append' -- if true, append to the list of filters """ - assert action in ("error", "ignore", "always", "default", "module", - "once"), "invalid action: %r" % (action,) - assert isinstance(lineno, int) and lineno >= 0, \ - "lineno must be an int >= 0" + if action not in {"error", "ignore", "always", "default", "module", "once"}: + raise ValueError(f"invalid action: {action!r}") + if not isinstance(lineno, int): + raise TypeError("lineno must be an int") + if lineno < 0: + raise ValueError("lineno must be an int >= 0") _add_filter(action, None, category, None, lineno, append=append) def _add_filter(*item, append): diff --git a/Misc/NEWS.d/next/Library/2020-03-09-15-08-29.bpo-39912.xPOBBY.rst b/Misc/NEWS.d/next/Library/2020-03-09-15-08-29.bpo-39912.xPOBBY.rst new file mode 100644 index 00000000000000..fb8579725a2d7d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-03-09-15-08-29.bpo-39912.xPOBBY.rst @@ -0,0 +1,3 @@ +:func:`warnings.filterwarnings()` and :func:`warnings.simplefilter()` now raise +appropriate exceptions instead of ``AssertionError``. Patch contributed by +Rémi Lapeyre. From bfb576ee23c133bec0ce7c26a8ecea76926b9d8e Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:57:31 +0000 Subject: [PATCH 123/228] gh-111058: Change coro.cr_frame/gen.gi_frame to be None for a closed coroutine/generator. (#112428) --- Lib/test/test_coroutines.py | 8 ++++++++ Lib/test/test_inspect/test_inspect.py | 8 ++++++++ .../2023-11-26-21-30-11.gh-issue-111058.q4DqDY.rst | 3 +++ Objects/genobject.c | 2 +- 4 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-26-21-30-11.gh-issue-111058.q4DqDY.rst diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index 47145782c0f04f..25c981d1511bc1 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -2216,6 +2216,14 @@ async def f(): gen.cr_frame.clear() gen.close() + def test_cr_frame_after_close(self): + async def f(): + pass + gen = f() + self.assertIsNotNone(gen.cr_frame) + gen.close() + self.assertIsNone(gen.cr_frame) + def test_stack_in_coroutine_throw(self): # Regression test for https://github.com/python/cpython/issues/93592 async def a(): diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index becbb0498bbb3f..e75682f881ab34 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -2384,6 +2384,10 @@ def test_closed_after_immediate_exception(self): self.generator.throw(RuntimeError) self.assertEqual(self._generatorstate(), inspect.GEN_CLOSED) + def test_closed_after_close(self): + self.generator.close() + self.assertEqual(self._generatorstate(), inspect.GEN_CLOSED) + def test_running(self): # As mentioned on issue #10220, checking for the RUNNING state only # makes sense inside the generator itself. @@ -2493,6 +2497,10 @@ def test_closed_after_immediate_exception(self): self.coroutine.throw(RuntimeError) self.assertEqual(self._coroutinestate(), inspect.CORO_CLOSED) + def test_closed_after_close(self): + self.coroutine.close() + self.assertEqual(self._coroutinestate(), inspect.CORO_CLOSED) + def test_easy_debugging(self): # repr() and str() of a coroutine state should contain the state name names = 'CORO_CREATED CORO_RUNNING CORO_SUSPENDED CORO_CLOSED'.split() diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-26-21-30-11.gh-issue-111058.q4DqDY.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-26-21-30-11.gh-issue-111058.q4DqDY.rst new file mode 100644 index 00000000000000..de5661f911aa82 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-26-21-30-11.gh-issue-111058.q4DqDY.rst @@ -0,0 +1,3 @@ +Change coro.cr_frame/gen.gi_frame to return ``None`` after the coroutine/generator has been closed. +This fixes a bug where :func:`~inspect.getcoroutinestate` and :func:`~inspect.getgeneratorstate` +return the wrong state for a closed coroutine/generator. diff --git a/Objects/genobject.c b/Objects/genobject.c index f98aa357cd2ce1..9614713883741c 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -732,7 +732,7 @@ _gen_getframe(PyGenObject *gen, const char *const name) if (PySys_Audit("object.__getattr__", "Os", gen, name) < 0) { return NULL; } - if (gen->gi_frame_state == FRAME_CLEARED) { + if (FRAME_STATE_FINISHED(gen->gi_frame_state)) { Py_RETURN_NONE; } return _Py_XNewRef((PyObject *)_PyFrame_GetFrameObject((_PyInterpreterFrame *)gen->gi_iframe)); From a73aa48e6bec900be7edd3431deaa5fc1d809e6f Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Fri, 1 Dec 2023 13:20:51 +0000 Subject: [PATCH 124/228] gh-112367: Only free perf trampoline arenas at shutdown (#112368) Signed-off-by: Pablo Galindo --- Include/internal/pycore_ceval.h | 1 + ...-11-24-14-10-57.gh-issue-112367.9z1IDp.rst | 2 + Python/perf_trampoline.c | 40 ++++++++++++++++--- Python/pylifecycle.c | 2 +- 4 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-11-24-14-10-57.gh-issue-112367.9z1IDp.rst diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index c372b7224fb047..3f7ac922bdf451 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -101,6 +101,7 @@ extern int _PyPerfTrampoline_SetCallbacks(_PyPerf_Callbacks *); extern void _PyPerfTrampoline_GetCallbacks(_PyPerf_Callbacks *); extern int _PyPerfTrampoline_Init(int activate); extern int _PyPerfTrampoline_Fini(void); +extern void _PyPerfTrampoline_FreeArenas(void); extern int _PyIsPerfTrampolineActive(void); extern PyStatus _PyPerfTrampoline_AfterFork_Child(void); #ifdef PY_HAVE_PERF_TRAMPOLINE diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-24-14-10-57.gh-issue-112367.9z1IDp.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-24-14-10-57.gh-issue-112367.9z1IDp.rst new file mode 100644 index 00000000000000..991e45ad47fabe --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-24-14-10-57.gh-issue-112367.9z1IDp.rst @@ -0,0 +1,2 @@ +Avoid undefined behaviour when using the perf trampolines by not freeing the +code arenas until shutdown. Patch by Pablo Galindo diff --git a/Python/perf_trampoline.c b/Python/perf_trampoline.c index 208ced6c101dce..540b650192ed34 100644 --- a/Python/perf_trampoline.c +++ b/Python/perf_trampoline.c @@ -216,10 +216,24 @@ perf_map_write_entry(void *state, const void *code_addr, PyMem_RawFree(perf_map_entry); } +static void* +perf_map_init_state(void) +{ + PyUnstable_PerfMapState_Init(); + return NULL; +} + +static int +perf_map_free_state(void *state) +{ + PyUnstable_PerfMapState_Fini(); + return 0; +} + _PyPerf_Callbacks _Py_perfmap_callbacks = { - NULL, + &perf_map_init_state, &perf_map_write_entry, - NULL, + &perf_map_free_state, }; static int @@ -415,7 +429,6 @@ _PyPerfTrampoline_SetCallbacks(_PyPerf_Callbacks *callbacks) trampoline_api.write_state = callbacks->write_state; trampoline_api.free_state = callbacks->free_state; trampoline_api.state = NULL; - perf_status = PERF_STATUS_OK; #endif return 0; } @@ -434,6 +447,7 @@ _PyPerfTrampoline_Init(int activate) } if (!activate) { tstate->interp->eval_frame = NULL; + perf_status = PERF_STATUS_NO_INIT; } else { tstate->interp->eval_frame = py_trampoline_evaluator; @@ -444,6 +458,9 @@ _PyPerfTrampoline_Init(int activate) if (extra_code_index == -1) { return -1; } + if (trampoline_api.state == NULL && trampoline_api.init_state != NULL) { + trampoline_api.state = trampoline_api.init_state(); + } perf_status = PERF_STATUS_OK; } #endif @@ -454,16 +471,29 @@ int _PyPerfTrampoline_Fini(void) { #ifdef PY_HAVE_PERF_TRAMPOLINE + if (perf_status != PERF_STATUS_OK) { + return 0; + } PyThreadState *tstate = _PyThreadState_GET(); if (tstate->interp->eval_frame == py_trampoline_evaluator) { tstate->interp->eval_frame = NULL; } - free_code_arenas(); + if (perf_status == PERF_STATUS_OK) { + trampoline_api.free_state(trampoline_api.state); + } extra_code_index = -1; + perf_status = PERF_STATUS_NO_INIT; #endif return 0; } +void _PyPerfTrampoline_FreeArenas(void) { +#ifdef PY_HAVE_PERF_TRAMPOLINE + free_code_arenas(); +#endif + return; +} + int PyUnstable_PerfTrampoline_SetPersistAfterFork(int enable){ #ifdef PY_HAVE_PERF_TRAMPOLINE @@ -477,8 +507,8 @@ PyStatus _PyPerfTrampoline_AfterFork_Child(void) { #ifdef PY_HAVE_PERF_TRAMPOLINE - PyUnstable_PerfMapState_Fini(); if (persist_after_fork) { + _PyPerfTrampoline_Fini(); char filename[256]; pid_t parent_pid = getppid(); snprintf(filename, sizeof(filename), "/tmp/perf-%d.map", parent_pid); diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index ac8d5208322882..aff67d7a835e89 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1797,6 +1797,7 @@ finalize_interp_clear(PyThreadState *tstate) _PyArg_Fini(); _Py_ClearFileSystemEncoding(); _PyPerfTrampoline_Fini(); + _PyPerfTrampoline_FreeArenas(); } finalize_interp_types(tstate->interp); @@ -1854,7 +1855,6 @@ Py_FinalizeEx(void) */ _PyAtExit_Call(tstate->interp); - PyUnstable_PerfMapState_Fini(); /* Copy the core config, PyInterpreterState_Delete() free the core config memory */ From 058444308abee79bb1b3358883adfa8c97bd043a Mon Sep 17 00:00:00 2001 From: Zackery Spytz Date: Fri, 1 Dec 2023 05:36:37 -0800 Subject: [PATCH 125/228] gh-82565: Add tests for pickle and unpickle with bad files (GH-16606) --- Lib/test/pickletester.py | 78 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index ddb180ef5ef825..fd446c8145850c 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -3514,6 +3514,84 @@ def __init__(self): pass self.assertRaises(pickle.PicklingError, BadPickler().dump, 0) self.assertRaises(pickle.UnpicklingError, BadUnpickler().load) + def test_unpickler_bad_file(self): + # bpo-38384: Crash in _pickle if the read attribute raises an error. + def raises_oserror(self, *args, **kwargs): + raise OSError + @property + def bad_property(self): + 1/0 + + # File without read and readline + class F: + pass + self.assertRaises((AttributeError, TypeError), self.Unpickler, F()) + + # File without read + class F: + readline = raises_oserror + self.assertRaises((AttributeError, TypeError), self.Unpickler, F()) + + # File without readline + class F: + read = raises_oserror + self.assertRaises((AttributeError, TypeError), self.Unpickler, F()) + + # File with bad read + class F: + read = bad_property + readline = raises_oserror + self.assertRaises(ZeroDivisionError, self.Unpickler, F()) + + # File with bad readline + class F: + readline = bad_property + read = raises_oserror + self.assertRaises(ZeroDivisionError, self.Unpickler, F()) + + # File with bad readline, no read + class F: + readline = bad_property + self.assertRaises(ZeroDivisionError, self.Unpickler, F()) + + # File with bad read, no readline + class F: + read = bad_property + self.assertRaises((AttributeError, ZeroDivisionError), self.Unpickler, F()) + + # File with bad peek + class F: + peek = bad_property + read = raises_oserror + readline = raises_oserror + try: + self.Unpickler(F()) + except ZeroDivisionError: + pass + + # File with bad readinto + class F: + readinto = bad_property + read = raises_oserror + readline = raises_oserror + try: + self.Unpickler(F()) + except ZeroDivisionError: + pass + + def test_pickler_bad_file(self): + # File without write + class F: + pass + self.assertRaises(TypeError, self.Pickler, F()) + + # File with bad write + class F: + @property + def write(self): + 1/0 + self.assertRaises(ZeroDivisionError, self.Pickler, F()) + def check_dumps_loads_oob_buffers(self, dumps, loads): # No need to do the full gamut of tests here, just enough to # check that dumps() and loads() redirect their arguments From f8ff80f63536e96b004d29112452a8f1738fde37 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 1 Dec 2023 14:46:50 +0100 Subject: [PATCH 126/228] gh-109413: regrtest: add WorkerRunTests class (#112588) --- Lib/test/libregrtest/main.py | 1 - Lib/test/libregrtest/run_workers.py | 12 +++++------ Lib/test/libregrtest/runtests.py | 31 +++++++++++++++++++---------- Lib/test/libregrtest/worker.py | 6 +++--- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 55fc3a820d3451..16f6974ae32465 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -423,7 +423,6 @@ def create_run_tests(self, tests: TestTuple): python_cmd=self.python_cmd, randomize=self.randomize, random_seed=self.random_seed, - json_file=None, ) def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int: diff --git a/Lib/test/libregrtest/run_workers.py b/Lib/test/libregrtest/run_workers.py index 35aaf90ffc4299..18a0342f0611cf 100644 --- a/Lib/test/libregrtest/run_workers.py +++ b/Lib/test/libregrtest/run_workers.py @@ -18,7 +18,7 @@ from .logger import Logger from .result import TestResult, State from .results import TestResults -from .runtests import RunTests, JsonFile, JsonFileType +from .runtests import RunTests, WorkerRunTests, JsonFile, JsonFileType from .single import PROGRESS_MIN_TIME from .utils import ( StrPath, TestName, @@ -162,7 +162,7 @@ def stop(self) -> None: self._stopped = True self._kill() - def _run_process(self, runtests: RunTests, output_fd: int, + def _run_process(self, runtests: WorkerRunTests, output_fd: int, tmp_dir: StrPath | None = None) -> int | None: popen = create_worker_process(runtests, output_fd, tmp_dir) self._popen = popen @@ -252,9 +252,7 @@ def create_json_file(self, stack: contextlib.ExitStack) -> tuple[JsonFile, TextI json_file = JsonFile(json_fd, JsonFileType.UNIX_FD) return (json_file, json_tmpfile) - def create_worker_runtests(self, test_name: TestName, json_file: JsonFile) -> RunTests: - """Create the worker RunTests.""" - + def create_worker_runtests(self, test_name: TestName, json_file: JsonFile) -> WorkerRunTests: tests = (test_name,) if self.runtests.rerun: match_tests = self.runtests.get_match_tests(test_name) @@ -267,12 +265,12 @@ def create_worker_runtests(self, test_name: TestName, json_file: JsonFile) -> Ru if self.runtests.output_on_failure: kwargs['verbose'] = True kwargs['output_on_failure'] = False - return self.runtests.copy( + return self.runtests.create_worker_runtests( tests=tests, json_file=json_file, **kwargs) - def run_tmp_files(self, worker_runtests: RunTests, + def run_tmp_files(self, worker_runtests: WorkerRunTests, stdout_fd: int) -> tuple[int | None, list[StrPath]]: # gh-93353: Check for leaked temporary files in the parent process, # since the deletion of temporary files can happen late during diff --git a/Lib/test/libregrtest/runtests.py b/Lib/test/libregrtest/runtests.py index b765ba5b41d236..edd72276320e41 100644 --- a/Lib/test/libregrtest/runtests.py +++ b/Lib/test/libregrtest/runtests.py @@ -93,13 +93,17 @@ class RunTests: python_cmd: tuple[str, ...] | None randomize: bool random_seed: int | str - json_file: JsonFile | None - def copy(self, **override): + def copy(self, **override) -> 'RunTests': state = dataclasses.asdict(self) state.update(override) return RunTests(**state) + def create_worker_runtests(self, **override): + state = dataclasses.asdict(self) + state.update(override) + return WorkerRunTests(**state) + def get_match_tests(self, test_name) -> FilterTuple | None: if self.match_tests_dict is not None: return self.match_tests_dict.get(test_name, None) @@ -120,13 +124,6 @@ def iter_tests(self): else: yield from self.tests - def as_json(self) -> StrJSON: - return json.dumps(self, cls=_EncodeRunTests) - - @staticmethod - def from_json(worker_json: StrJSON) -> 'RunTests': - return json.loads(worker_json, object_hook=_decode_runtests) - def json_file_use_stdout(self) -> bool: # Use STDOUT in two cases: # @@ -141,9 +138,21 @@ def json_file_use_stdout(self) -> bool: ) +@dataclasses.dataclass(slots=True, frozen=True) +class WorkerRunTests(RunTests): + json_file: JsonFile + + def as_json(self) -> StrJSON: + return json.dumps(self, cls=_EncodeRunTests) + + @staticmethod + def from_json(worker_json: StrJSON) -> 'WorkerRunTests': + return json.loads(worker_json, object_hook=_decode_runtests) + + class _EncodeRunTests(json.JSONEncoder): def default(self, o: Any) -> dict[str, Any]: - if isinstance(o, RunTests): + if isinstance(o, WorkerRunTests): result = dataclasses.asdict(o) result["__runtests__"] = True return result @@ -158,6 +167,6 @@ def _decode_runtests(data: dict[str, Any]) -> RunTests | dict[str, Any]: data['hunt_refleak'] = HuntRefleak(**data['hunt_refleak']) if data['json_file']: data['json_file'] = JsonFile(**data['json_file']) - return RunTests(**data) + return WorkerRunTests(**data) else: return data diff --git a/Lib/test/libregrtest/worker.py b/Lib/test/libregrtest/worker.py index b3bb0b7f34a060..7a6d33d4499943 100644 --- a/Lib/test/libregrtest/worker.py +++ b/Lib/test/libregrtest/worker.py @@ -7,7 +7,7 @@ from test.support import os_helper, Py_DEBUG from .setup import setup_process, setup_test_dir -from .runtests import RunTests, JsonFile, JsonFileType +from .runtests import WorkerRunTests, JsonFile, JsonFileType from .single import run_single_test from .utils import ( StrPath, StrJSON, TestFilter, @@ -17,7 +17,7 @@ USE_PROCESS_GROUP = (hasattr(os, "setsid") and hasattr(os, "killpg")) -def create_worker_process(runtests: RunTests, output_fd: int, +def create_worker_process(runtests: WorkerRunTests, output_fd: int, tmp_dir: StrPath | None = None) -> subprocess.Popen: python_cmd = runtests.python_cmd worker_json = runtests.as_json() @@ -73,7 +73,7 @@ def create_worker_process(runtests: RunTests, output_fd: int, def worker_process(worker_json: StrJSON) -> NoReturn: - runtests = RunTests.from_json(worker_json) + runtests = WorkerRunTests.from_json(worker_json) test_name = runtests.tests[0] match_tests: TestFilter = runtests.match_tests json_file: JsonFile = runtests.json_file From c2982380f827e53057068eccf9f1a16b5a653728 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Fri, 1 Dec 2023 05:05:55 -0900 Subject: [PATCH 127/228] gh-112510: Add `readline.backend` for the backend readline uses (GH-112511) Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Donghee Na --- Doc/library/readline.rst | 15 ++++++++++----- Lib/site.py | 3 +-- Lib/test/test_pdb.py | 2 +- Lib/test/test_readline.py | 9 ++++++--- ...2023-11-29-02-26-32.gh-issue-112510.j-zXGc.rst | 1 + Modules/readline.c | 9 ++++++++- 6 files changed, 27 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-29-02-26-32.gh-issue-112510.j-zXGc.rst diff --git a/Doc/library/readline.rst b/Doc/library/readline.rst index 8fb0eca8df74d8..2e0f45ced30b9c 100644 --- a/Doc/library/readline.rst +++ b/Doc/library/readline.rst @@ -27,16 +27,15 @@ Readline library in general. .. note:: The underlying Readline library API may be implemented by - the ``libedit`` library instead of GNU readline. + the ``editline`` (``libedit``) library instead of GNU readline. On macOS the :mod:`readline` module detects which library is being used at run time. - The configuration file for ``libedit`` is different from that + The configuration file for ``editline`` is different from that of GNU readline. If you programmatically load configuration strings - you can check for the text "libedit" in :const:`readline.__doc__` - to differentiate between GNU readline and libedit. + you can use :data:`backend` to determine which library is being used. - If you use *editline*/``libedit`` readline emulation on macOS, the + If you use ``editline``/``libedit`` readline emulation on macOS, the initialization file located in your home directory is named ``.editrc``. For example, the following content in ``~/.editrc`` will turn ON *vi* keybindings and TAB completion:: @@ -44,6 +43,12 @@ Readline library in general. python:bind -v python:bind ^I rl_complete +.. data:: backend + + The name of the underlying Readline library being used, either + ``"readline"`` or ``"editline"``. + + .. versionadded:: 3.13 Init file --------- diff --git a/Lib/site.py b/Lib/site.py index 672fa7b000ad02..2517b7e5f1d22a 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -444,8 +444,7 @@ def register_readline(): # Reading the initialization (config) file may not be enough to set a # completion key, so we set one first and then read the file. - readline_doc = getattr(readline, '__doc__', '') - if readline_doc is not None and 'libedit' in readline_doc: + if readline.backend == 'editline': readline.parse_and_bind('bind ^I rl_complete') else: readline.parse_and_bind('tab: complete') diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 2a279ca869e9c7..50d8c8f52a909d 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -3293,7 +3293,7 @@ def setUpClass(): # Ensure that the readline module is loaded # If this fails, the test is skipped because SkipTest will be raised readline = import_module('readline') - if readline.__doc__ and "libedit" in readline.__doc__: + if readline.backend == "editline": raise unittest.SkipTest("libedit readline is not supported for pdb") def test_basic_completion(self): diff --git a/Lib/test/test_readline.py b/Lib/test/test_readline.py index 6c2726d3209ecf..5e0e6f8dfac651 100644 --- a/Lib/test/test_readline.py +++ b/Lib/test/test_readline.py @@ -19,7 +19,7 @@ if hasattr(readline, "_READLINE_LIBRARY_VERSION"): is_editline = ("EditLine wrapper" in readline._READLINE_LIBRARY_VERSION) else: - is_editline = (readline.__doc__ and "libedit" in readline.__doc__) + is_editline = readline.backend == "editline" def setUpModule(): @@ -145,6 +145,9 @@ def test_init(self): TERM='xterm-256color') self.assertEqual(stdout, b'') + def test_backend(self): + self.assertIn(readline.backend, ("readline", "editline")) + auto_history_script = """\ import readline readline.set_auto_history({}) @@ -171,7 +174,7 @@ def complete(text, state): if state == 0 and text == "$": return "$complete" return None - if "libedit" in getattr(readline, "__doc__", ""): + if readline.backend == "editline": readline.parse_and_bind(r'bind "\\t" rl_complete') else: readline.parse_and_bind(r'"\\t": complete') @@ -198,7 +201,7 @@ def test_nonascii(self): script = r"""import readline -is_editline = readline.__doc__ and "libedit" in readline.__doc__ +is_editline = readline.backend == "editline" inserted = "[\xEFnserted]" macro = "|t\xEB[after]" set_pre_input_hook = getattr(readline, "set_pre_input_hook", None) diff --git a/Misc/NEWS.d/next/Library/2023-11-29-02-26-32.gh-issue-112510.j-zXGc.rst b/Misc/NEWS.d/next/Library/2023-11-29-02-26-32.gh-issue-112510.j-zXGc.rst new file mode 100644 index 00000000000000..02de6fa80c1b3e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-29-02-26-32.gh-issue-112510.j-zXGc.rst @@ -0,0 +1 @@ +Add :data:`readline.backend` for the backend readline uses (``editline`` or ``readline``) diff --git a/Modules/readline.c b/Modules/readline.c index eb9a3d4693ee90..afbb7f8f0ec18f 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -1538,6 +1538,7 @@ static struct PyModuleDef readlinemodule = { PyMODINIT_FUNC PyInit_readline(void) { + const char *backend = "readline"; PyObject *m; readlinestate *mod_state; @@ -1545,8 +1546,10 @@ PyInit_readline(void) using_libedit_emulation = 1; } - if (using_libedit_emulation) + if (using_libedit_emulation) { readlinemodule.m_doc = doc_module_le; + backend = "editline"; + } m = PyModule_Create(&readlinemodule); @@ -1568,6 +1571,10 @@ PyInit_readline(void) goto error; } + if (PyModule_AddStringConstant(m, "backend", backend) < 0) { + goto error; + } + mod_state = (readlinestate *) PyModule_GetState(m); if (mod_state == NULL){ goto error; From 5f6ac2d88a49b8a7c764691365cd41ee6226a8d0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 1 Dec 2023 15:50:16 +0100 Subject: [PATCH 128/228] gh-110481: Fix Py_SET_REFCNT() integer overflow (#112174) If Py_NOGIL is defined and Py_SET_REFCNT() is called with a reference count larger than UINT32_MAX, make the object immortal. Set _Py_IMMORTAL_REFCNT constant type to Py_ssize_t to fix the following compiler warning: Include/internal/pycore_global_objects_fini_generated.h:14:24: warning: comparison of integers of different signs: 'Py_ssize_t' (aka 'long') and 'unsigned int' [-Wsign-compare] if (Py_REFCNT(obj) < _Py_IMMORTAL_REFCNT) { ~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~ --- Doc/c-api/refcounting.rst | 3 +++ Doc/using/configure.rst | 2 ++ Include/object.h | 31 +++++++++++++++++++++---------- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/Doc/c-api/refcounting.rst b/Doc/c-api/refcounting.rst index 68119a27b18ec2..75e1d46474f1e7 100644 --- a/Doc/c-api/refcounting.rst +++ b/Doc/c-api/refcounting.rst @@ -34,6 +34,9 @@ of Python objects. Set the object *o* reference counter to *refcnt*. + On :ref:`Python build with Free Threading `, if + *refcnt* is larger than ``UINT32_MAX``, the object is made :term:`immortal`. + This function has no effect on :term:`immortal` objects. .. versionadded:: 3.9 diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index b51546e072a353..56d2d6dc4ab5f1 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -287,6 +287,8 @@ General Options .. versionadded:: 3.11 +.. _free-threading-build: + .. option:: --disable-gil Enables **experimental** support for running Python without the diff --git a/Include/object.h b/Include/object.h index 6b70a494844476..86fcba21caa9c8 100644 --- a/Include/object.h +++ b/Include/object.h @@ -88,7 +88,7 @@ having all the lower 32 bits set, which will avoid the reference count to go beyond the refcount limit. Immortality checks for reference count decreases will be done by checking the bit sign flag in the lower 32 bits. */ -#define _Py_IMMORTAL_REFCNT UINT_MAX +#define _Py_IMMORTAL_REFCNT _Py_CAST(Py_ssize_t, UINT_MAX) #else /* @@ -103,7 +103,7 @@ immortality, but the execution would still be correct. Reference count increases and decreases will first go through an immortality check by comparing the reference count field to the immortality reference count. */ -#define _Py_IMMORTAL_REFCNT (UINT_MAX >> 2) +#define _Py_IMMORTAL_REFCNT _Py_CAST(Py_ssize_t, UINT_MAX >> 2) #endif // Py_GIL_DISABLED builds indicate immortal objects using `ob_ref_local`, which is @@ -317,11 +317,11 @@ static inline Py_ssize_t Py_SIZE(PyObject *ob) { static inline Py_ALWAYS_INLINE int _Py_IsImmortal(PyObject *op) { #if defined(Py_GIL_DISABLED) - return op->ob_ref_local == _Py_IMMORTAL_REFCNT_LOCAL; + return (op->ob_ref_local == _Py_IMMORTAL_REFCNT_LOCAL); #elif SIZEOF_VOID_P > 4 - return _Py_CAST(PY_INT32_T, op->ob_refcnt) < 0; + return (_Py_CAST(PY_INT32_T, op->ob_refcnt) < 0); #else - return op->ob_refcnt == _Py_IMMORTAL_REFCNT; + return (op->ob_refcnt == _Py_IMMORTAL_REFCNT); #endif } #define _Py_IsImmortal(op) _Py_IsImmortal(_PyObject_CAST(op)) @@ -350,15 +350,23 @@ static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { if (_Py_IsImmortal(ob)) { return; } + #ifndef Py_GIL_DISABLED ob->ob_refcnt = refcnt; #else if (_Py_IsOwnedByCurrentThread(ob)) { - // Set local refcount to desired refcount and shared refcount to zero, - // but preserve the shared refcount flags. - assert(refcnt < UINT32_MAX); - ob->ob_ref_local = _Py_STATIC_CAST(uint32_t, refcnt); - ob->ob_ref_shared &= _Py_REF_SHARED_FLAG_MASK; + if ((size_t)refcnt > (size_t)UINT32_MAX) { + // On overflow, make the object immortal + op->ob_tid = _Py_UNOWNED_TID; + op->ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL; + op->ob_ref_shared = 0; + } + else { + // Set local refcount to desired refcount and shared refcount + // to zero, but preserve the shared refcount flags. + ob->ob_ref_local = _Py_STATIC_CAST(uint32_t, refcnt); + ob->ob_ref_shared &= _Py_REF_SHARED_FLAG_MASK; + } } else { // Set local refcount to zero and shared refcount to desired refcount. @@ -750,6 +758,7 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op) uint32_t local = _Py_atomic_load_uint32_relaxed(&op->ob_ref_local); uint32_t new_local = local + 1; if (new_local == 0) { + // local is equal to _Py_IMMORTAL_REFCNT: do nothing return; } if (_Py_IsOwnedByCurrentThread(op)) { @@ -763,6 +772,8 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op) PY_UINT32_T cur_refcnt = op->ob_refcnt_split[PY_BIG_ENDIAN]; PY_UINT32_T new_refcnt = cur_refcnt + 1; if (new_refcnt == 0) { + // cur_refcnt is equal to _Py_IMMORTAL_REFCNT: the object is immortal, + // do nothing return; } op->ob_refcnt_split[PY_BIG_ENDIAN] = new_refcnt; From 70a38ffb3d712f973eb17bd1bda541f238ae70d2 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 1 Dec 2023 14:54:33 +0000 Subject: [PATCH 129/228] gh-109413: libregrtest: enable mypy's `--strict-optional` check on most files (#112586) Co-authored-by: Victor Stinner --- Lib/test/libregrtest/mypy.ini | 2 +- Lib/test/libregrtest/results.py | 2 ++ Lib/test/libregrtest/single.py | 8 ++++---- Lib/test/libregrtest/utils.py | 9 +++++++++ 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Lib/test/libregrtest/mypy.ini b/Lib/test/libregrtest/mypy.ini index 331fe681b9f56b..22c7c7a9acef14 100644 --- a/Lib/test/libregrtest/mypy.ini +++ b/Lib/test/libregrtest/mypy.ini @@ -25,7 +25,7 @@ warn_return_any = False disable_error_code = return # Enable --strict-optional for these ASAP: -[mypy-Lib.test.libregrtest.main.*,Lib.test.libregrtest.run_workers.*,Lib.test.libregrtest.worker.*,Lib.test.libregrtest.single.*,Lib.test.libregrtest.results.*,Lib.test.libregrtest.utils.*] +[mypy-Lib.test.libregrtest.main.*,Lib.test.libregrtest.run_workers.*] strict_optional = False # Various internal modules that typeshed deliberately doesn't have stubs for: diff --git a/Lib/test/libregrtest/results.py b/Lib/test/libregrtest/results.py index 59a566c032847e..a41ea8aba028c3 100644 --- a/Lib/test/libregrtest/results.py +++ b/Lib/test/libregrtest/results.py @@ -117,6 +117,8 @@ def accumulate_result(self, result: TestResult, runtests: RunTests): self.worker_bug = True if result.has_meaningful_duration() and not rerun: + if result.duration is None: + raise ValueError("result.duration is None") self.test_times.append((result.duration, test_name)) if result.stats is not None: self.stats.accumulate(result.stats) diff --git a/Lib/test/libregrtest/single.py b/Lib/test/libregrtest/single.py index 5c7bc7d40fb394..eafeb5fe26f3f3 100644 --- a/Lib/test/libregrtest/single.py +++ b/Lib/test/libregrtest/single.py @@ -237,11 +237,11 @@ def _runtest(result: TestResult, runtests: RunTests) -> None: output_on_failure = runtests.output_on_failure timeout = runtests.timeout - use_timeout = ( - timeout is not None and threading_helper.can_start_thread - ) - if use_timeout: + if timeout is not None and threading_helper.can_start_thread: + use_timeout = True faulthandler.dump_traceback_later(timeout, exit=True) + else: + use_timeout = False try: setup_tests(runtests) diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index d47e9388e62db2..d4972ce4a50d2a 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -377,10 +377,19 @@ def get_temp_dir(tmp_dir: StrPath | None = None) -> StrPath: # Python out of the source tree, especially when the # source tree is read only. tmp_dir = sysconfig.get_config_var('srcdir') + if not tmp_dir: + raise RuntimeError( + "Could not determine the correct value for tmp_dir" + ) tmp_dir = os.path.join(tmp_dir, 'build') else: # WASI platform tmp_dir = sysconfig.get_config_var('projectbase') + if not tmp_dir: + raise RuntimeError( + "sysconfig.get_config_var('projectbase') " + f"unexpectedly returned {tmp_dir!r} on WASI" + ) tmp_dir = os.path.join(tmp_dir, 'build') # When get_temp_dir() is called in a worker process, From 0daf555c6fb3feba77989382135a58215e1d70a5 Mon Sep 17 00:00:00 2001 From: Zackery Spytz Date: Fri, 1 Dec 2023 07:16:49 -0800 Subject: [PATCH 130/228] bpo-37013: Fix the error handling in socket.if_indextoname() (GH-13503) * Fix a crash when pass UINT_MAX. * Fix an integer overflow on 64-bit non-Windows platforms. --- Lib/test/test_socket.py | 13 +++++++++++++ ...2023-12-01-16-09-59.gh-issue-81194.FFad1c.rst | 3 +++ Modules/socketmodule.c | 16 +++++++++++----- 3 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-12-01-16-09-59.gh-issue-81194.FFad1c.rst diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 86701caf05399e..4eb5af99d6674c 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1082,7 +1082,20 @@ def testInterfaceNameIndex(self): 'socket.if_indextoname() not available.') def testInvalidInterfaceIndexToName(self): self.assertRaises(OSError, socket.if_indextoname, 0) + self.assertRaises(OverflowError, socket.if_indextoname, -1) + self.assertRaises(OverflowError, socket.if_indextoname, 2**1000) self.assertRaises(TypeError, socket.if_indextoname, '_DEADBEEF') + if hasattr(socket, 'if_nameindex'): + indices = dict(socket.if_nameindex()) + for index in indices: + index2 = index + 2**32 + if index2 not in indices: + with self.assertRaises((OverflowError, OSError)): + socket.if_indextoname(index2) + for index in 2**32-1, 2**64-1: + if index not in indices: + with self.assertRaises((OverflowError, OSError)): + socket.if_indextoname(index) @unittest.skipUnless(hasattr(socket, 'if_nametoindex'), 'socket.if_nametoindex() not available.') diff --git a/Misc/NEWS.d/next/Library/2023-12-01-16-09-59.gh-issue-81194.FFad1c.rst b/Misc/NEWS.d/next/Library/2023-12-01-16-09-59.gh-issue-81194.FFad1c.rst new file mode 100644 index 00000000000000..feb7a8643b97f6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-01-16-09-59.gh-issue-81194.FFad1c.rst @@ -0,0 +1,3 @@ +Fix a crash in :func:`socket.if_indextoname` with specific value (UINT_MAX). +Fix an integer overflow in :func:`socket.if_indextoname` on 64-bit +non-Windows platforms. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 9ac2001c0132d3..0a0e0e78656f76 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -7071,17 +7071,23 @@ _socket_socket_if_nametoindex_impl(PySocketSockObject *self, PyObject *oname) static PyObject * socket_if_indextoname(PyObject *self, PyObject *arg) { + unsigned long index_long = PyLong_AsUnsignedLong(arg); + if (index_long == (unsigned long) -1 && PyErr_Occurred()) { + return NULL; + } + #ifdef MS_WINDOWS - NET_IFINDEX index; + NET_IFINDEX index = (NET_IFINDEX)index_long; #else - unsigned long index; + unsigned int index = (unsigned int)index_long; #endif - char name[IF_NAMESIZE + 1]; - index = PyLong_AsUnsignedLong(arg); - if (index == (unsigned long) -1) + if ((unsigned long)index != index_long) { + PyErr_SetString(PyExc_OverflowError, "index is too large"); return NULL; + } + char name[IF_NAMESIZE + 1]; if (if_indextoname(index, name) == NULL) { PyErr_SetFromErrno(PyExc_OSError); return NULL; From a9073564ee50bc610e1fd36e45b0a5204618883a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 1 Dec 2023 16:54:40 +0100 Subject: [PATCH 131/228] gh-110481: Fix typo in Py_SET_REFCNT() (#112595) --- Include/object.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Include/object.h b/Include/object.h index 86fcba21caa9c8..81f777ad21f2f9 100644 --- a/Include/object.h +++ b/Include/object.h @@ -357,9 +357,9 @@ static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { if (_Py_IsOwnedByCurrentThread(ob)) { if ((size_t)refcnt > (size_t)UINT32_MAX) { // On overflow, make the object immortal - op->ob_tid = _Py_UNOWNED_TID; - op->ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL; - op->ob_ref_shared = 0; + ob->ob_tid = _Py_UNOWNED_TID; + ob->ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL; + ob->ob_ref_shared = 0; } else { // Set local refcount to desired refcount and shared refcount From 05a370abd6cdfe4b54be60b3b911f3a441026bb2 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 1 Dec 2023 17:05:56 +0100 Subject: [PATCH 132/228] gh-112567: Add _Py_GetTicksPerSecond() function (#112587) * Move _PyRuntimeState.time to _posixstate.ticks_per_second and time_module_state.ticks_per_second. * Add time_module_state.clocks_per_second. * Rename _PyTime_GetClockWithInfo() to py_clock(). * Rename _PyTime_GetProcessTimeWithInfo() to py_process_time(). * Add process_time_times() helper function, called by py_process_time(). * os.times() is now always built: no longer rely on HAVE_TIMES. --- Include/internal/pycore_fileutils.h | 4 + Include/internal/pycore_pylifecycle.h | 1 - Include/internal/pycore_runtime.h | 2 - Include/internal/pycore_time.h | 10 -- Modules/clinic/posixmodule.c.h | 10 +- Modules/posixmodule.c | 53 +++++---- Modules/timemodule.c | 158 ++++++++++++++------------ Python/fileutils.c | 24 ++++ Python/pylifecycle.c | 5 - 9 files changed, 142 insertions(+), 125 deletions(-) diff --git a/Include/internal/pycore_fileutils.h b/Include/internal/pycore_fileutils.h index 2f89da2c6ecd91..5c55282fa39e6f 100644 --- a/Include/internal/pycore_fileutils.h +++ b/Include/internal/pycore_fileutils.h @@ -320,6 +320,10 @@ PyAPI_FUNC(char*) _Py_UniversalNewlineFgetsWithSize(char *, int, FILE*, PyObject extern int _PyFile_Flush(PyObject *); +#ifndef MS_WINDOWS +extern int _Py_GetTicksPerSecond(long *ticks_per_second); +#endif + #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h index 61e0150e89009c..daf7cb77dcc63a 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -40,7 +40,6 @@ extern void _PySys_FiniTypes(PyInterpreterState *interp); extern int _PyBuiltins_AddExceptions(PyObject * bltinmod); extern PyStatus _Py_HashRandomization_Init(const PyConfig *); -extern PyStatus _PyTime_Init(void); extern PyStatus _PyGC_Init(PyInterpreterState *interp); extern PyStatus _PyAtExit_Init(PyInterpreterState *interp); extern int _Py_Deepfreeze_Init(void); diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index e6efe8b646e86f..36743723f8afd8 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -21,7 +21,6 @@ extern "C" { #include "pycore_pymem.h" // struct _pymem_allocators #include "pycore_pythread.h" // struct _pythread_runtime_state #include "pycore_signal.h" // struct _signals_runtime_state -#include "pycore_time.h" // struct _time_runtime_state #include "pycore_tracemalloc.h" // struct _tracemalloc_runtime_state #include "pycore_typeobject.h" // struct _types_runtime_state #include "pycore_unicodeobject.h" // struct _Py_unicode_runtime_state @@ -205,7 +204,6 @@ typedef struct pyruntimestate { struct _pymem_allocators allocators; struct _obmalloc_global_state obmalloc; struct pyhash_runtime_state pyhash_state; - struct _time_runtime_state time; struct _pythread_runtime_state threads; struct _signals_runtime_state signals; diff --git a/Include/internal/pycore_time.h b/Include/internal/pycore_time.h index 46713f91d190ff..7ea3485107572e 100644 --- a/Include/internal/pycore_time.h +++ b/Include/internal/pycore_time.h @@ -52,16 +52,6 @@ extern "C" { #endif -struct _time_runtime_state { -#ifdef HAVE_TIMES - int ticks_per_second_initialized; - long ticks_per_second; -#else - int _not_used; -#endif -}; - - #ifdef __clang__ struct timeval; #endif diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 9c54935bafa617..a6c76370f241be 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -5997,8 +5997,6 @@ os_symlink(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * #endif /* defined(HAVE_SYMLINK) */ -#if defined(HAVE_TIMES) - PyDoc_STRVAR(os_times__doc__, "times($module, /)\n" "--\n" @@ -6021,8 +6019,6 @@ os_times(PyObject *module, PyObject *Py_UNUSED(ignored)) return os_times_impl(module); } -#endif /* defined(HAVE_TIMES) */ - #if defined(HAVE_TIMERFD_CREATE) PyDoc_STRVAR(os_timerfd_create__doc__, @@ -12116,10 +12112,6 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #define OS_SYMLINK_METHODDEF #endif /* !defined(OS_SYMLINK_METHODDEF) */ -#ifndef OS_TIMES_METHODDEF - #define OS_TIMES_METHODDEF -#endif /* !defined(OS_TIMES_METHODDEF) */ - #ifndef OS_TIMERFD_CREATE_METHODDEF #define OS_TIMERFD_CREATE_METHODDEF #endif /* !defined(OS_TIMERFD_CREATE_METHODDEF) */ @@ -12403,4 +12395,4 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF #define OS_WAITSTATUS_TO_EXITCODE_METHODDEF #endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */ -/*[clinic end generated code: output=0f216bf44ea358f9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2900675ac5219924 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index d99b5335b6989a..70d107a297f315 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -1030,6 +1030,10 @@ typedef struct { PyObject *struct_rusage; #endif PyObject *st_mode; +#ifndef MS_WINDOWS + // times() clock frequency in hertz; used by os.times() + long ticks_per_second; +#endif } _posixstate; @@ -9986,8 +9990,6 @@ os_symlink_impl(PyObject *module, path_t *src, path_t *dst, #endif /* HAVE_SYMLINK */ - - static PyStructSequence_Field times_result_fields[] = { {"user", "user time"}, {"system", "system time"}, @@ -10013,12 +10015,6 @@ static PyStructSequence_Desc times_result_desc = { 5 }; -#ifdef MS_WINDOWS -#define HAVE_TIMES /* mandatory, for the method table */ -#endif - -#ifdef HAVE_TIMES - static PyObject * build_times_result(PyObject *module, double user, double system, double children_user, double children_system, @@ -10064,8 +10060,8 @@ All fields are floating point numbers. static PyObject * os_times_impl(PyObject *module) /*[clinic end generated code: output=35f640503557d32a input=2bf9df3d6ab2e48b]*/ -#ifdef MS_WINDOWS { +#ifdef MS_WINDOWS FILETIME create, exit, kernel, user; HANDLE hProc; hProc = GetCurrentProcess(); @@ -10083,28 +10079,26 @@ os_times_impl(PyObject *module) (double)0, (double)0, (double)0); -} #else /* MS_WINDOWS */ -{ - struct tms t; - clock_t c; + _posixstate *state = get_posix_state(module); + long ticks_per_second = state->ticks_per_second; + + struct tms process; + clock_t elapsed; errno = 0; - c = times(&t); - if (c == (clock_t) -1) { + elapsed = times(&process); + if (elapsed == (clock_t) -1) { return posix_error(); } - assert(_PyRuntime.time.ticks_per_second_initialized); -#define ticks_per_second _PyRuntime.time.ticks_per_second + return build_times_result(module, - (double)t.tms_utime / ticks_per_second, - (double)t.tms_stime / ticks_per_second, - (double)t.tms_cutime / ticks_per_second, - (double)t.tms_cstime / ticks_per_second, - (double)c / ticks_per_second); -#undef ticks_per_second -} + (double)process.tms_utime / ticks_per_second, + (double)process.tms_stime / ticks_per_second, + (double)process.tms_cutime / ticks_per_second, + (double)process.tms_cstime / ticks_per_second, + (double)elapsed / ticks_per_second); #endif /* MS_WINDOWS */ -#endif /* HAVE_TIMES */ +} #if defined(HAVE_TIMERFD_CREATE) @@ -17279,6 +17273,15 @@ posixmodule_exec(PyObject *m) Py_DECREF(unicode); } +#ifndef MS_WINDOWS + if (_Py_GetTicksPerSecond(&state->ticks_per_second) < 0) { + PyErr_SetString(PyExc_RuntimeError, + "cannot read ticks_per_second"); + return -1; + } + assert(state->ticks_per_second >= 1); +#endif + return PyModule_Add(m, "_have_functions", list); } diff --git a/Modules/timemodule.c b/Modules/timemodule.c index bc3901e0d7a621..aa0cdc5f026e7c 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -73,51 +73,20 @@ module time static int check_ticks_per_second(long tps, const char *context) { - /* Effectively, check that _PyTime_MulDiv(t, SEC_TO_NS, ticks_per_second) + /* Effectively, check that _PyTime_MulDiv(t, SEC_TO_NS, tps) cannot overflow. */ if (tps >= 0 && (_PyTime_t)tps > _PyTime_MAX / SEC_TO_NS) { PyErr_Format(PyExc_OverflowError, "%s is too large", context); return -1; } + if (tps < 1) { + PyErr_Format(PyExc_RuntimeError, "invalid %s", context); + return -1; + } return 0; } #endif /* HAVE_TIMES || HAVE_CLOCK */ -#ifdef HAVE_TIMES - -# define ticks_per_second _PyRuntime.time.ticks_per_second - -static void -ensure_ticks_per_second(void) -{ - if (_PyRuntime.time.ticks_per_second_initialized) { - return; - } - _PyRuntime.time.ticks_per_second_initialized = 1; -# if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK) - ticks_per_second = sysconf(_SC_CLK_TCK); - if (ticks_per_second < 1) { - ticks_per_second = -1; - } -# elif defined(HZ) - ticks_per_second = HZ; -# else - ticks_per_second = 60; /* magic fallback value; may be bogus */ -# endif -} - -#endif /* HAVE_TIMES */ - - -PyStatus -_PyTime_Init(void) -{ -#ifdef HAVE_TIMES - ensure_ticks_per_second(); -#endif - return PyStatus_Ok(); -} - /* Forward declarations */ static int pysleep(_PyTime_t timeout); @@ -125,6 +94,14 @@ static int pysleep(_PyTime_t timeout); typedef struct { PyTypeObject *struct_time_type; +#ifdef HAVE_TIMES + // times() clock frequency in hertz + long ticks_per_second; +#endif +#ifdef HAVE_CLOCK + // clock() frequency in hertz + long clocks_per_second; +#endif } time_module_state; static inline time_module_state* @@ -184,7 +161,7 @@ PyDoc_STRVAR(time_ns_doc, \n\ Return the current time in nanoseconds since the Epoch."); -#if defined(HAVE_CLOCK) +#ifdef HAVE_CLOCK #ifndef CLOCKS_PER_SEC # ifdef CLK_TCK @@ -195,15 +172,12 @@ Return the current time in nanoseconds since the Epoch."); #endif static int -_PyTime_GetClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) +py_clock(time_module_state *state, _PyTime_t *tp, _Py_clock_info_t *info) { - if (check_ticks_per_second(CLOCKS_PER_SEC, "CLOCKS_PER_SEC") < 0) { - return -1; - } - + long clocks_per_second = state->clocks_per_second; if (info) { info->implementation = "clock()"; - info->resolution = 1.0 / (double)CLOCKS_PER_SEC; + info->resolution = 1.0 / (double)clocks_per_second; info->monotonic = 1; info->adjustable = 0; } @@ -215,7 +189,7 @@ _PyTime_GetClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) "or its value cannot be represented"); return -1; } - _PyTime_t ns = _PyTime_MulDiv(ticks, SEC_TO_NS, (_PyTime_t)CLOCKS_PER_SEC); + _PyTime_t ns = _PyTime_MulDiv(ticks, SEC_TO_NS, clocks_per_second); *tp = _PyTime_FromNanoseconds(ns); return 0; } @@ -1277,8 +1251,38 @@ PyDoc_STRVAR(perf_counter_ns_doc, \n\ Performance counter for benchmarking as nanoseconds."); + +#ifdef HAVE_TIMES static int -_PyTime_GetProcessTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) +process_time_times(time_module_state *state, _PyTime_t *tp, + _Py_clock_info_t *info) +{ + long ticks_per_second = state->ticks_per_second; + + struct tms process; + if (times(&process) == (clock_t)-1) { + return 0; + } + + if (info) { + info->implementation = "times()"; + info->monotonic = 1; + info->adjustable = 0; + info->resolution = 1.0 / (double)ticks_per_second; + } + + _PyTime_t ns; + ns = _PyTime_MulDiv(process.tms_utime, SEC_TO_NS, ticks_per_second); + ns += _PyTime_MulDiv(process.tms_stime, SEC_TO_NS, ticks_per_second); + *tp = _PyTime_FromNanoseconds(ns); + return 1; +} +#endif + + +static int +py_process_time(time_module_state *state, _PyTime_t *tp, + _Py_clock_info_t *info) { #if defined(MS_WINDOWS) HANDLE process; @@ -1381,41 +1385,28 @@ _PyTime_GetProcessTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) /* times() */ #ifdef HAVE_TIMES - struct tms t; - - if (times(&t) != (clock_t)-1) { - assert(_PyRuntime.time.ticks_per_second_initialized); - if (check_ticks_per_second(ticks_per_second, "_SC_CLK_TCK") < 0) { - return -1; - } - if (ticks_per_second != -1) { - if (info) { - info->implementation = "times()"; - info->monotonic = 1; - info->adjustable = 0; - info->resolution = 1.0 / (double)ticks_per_second; - } - - _PyTime_t ns; - ns = _PyTime_MulDiv(t.tms_utime, SEC_TO_NS, ticks_per_second); - ns += _PyTime_MulDiv(t.tms_stime, SEC_TO_NS, ticks_per_second); - *tp = _PyTime_FromNanoseconds(ns); - return 0; - } + int res = process_time_times(state, tp, info); + if (res < 0) { + return -1; } + if (res == 1) { + return 0; + } + // times() failed, ignore failure #endif /* clock */ /* Currently, Python 3 requires clock() to build: see issue #22624 */ - return _PyTime_GetClockWithInfo(tp, info); + return py_clock(state, tp, info); #endif } static PyObject * -time_process_time(PyObject *self, PyObject *unused) +time_process_time(PyObject *module, PyObject *unused) { + time_module_state *state = get_time_state(module); _PyTime_t t; - if (_PyTime_GetProcessTimeWithInfo(&t, NULL) < 0) { + if (py_process_time(state, &t, NULL) < 0) { return NULL; } return _PyFloat_FromPyTime(t); @@ -1427,10 +1418,11 @@ PyDoc_STRVAR(process_time_doc, Process time for profiling: sum of the kernel and user-space CPU time."); static PyObject * -time_process_time_ns(PyObject *self, PyObject *unused) +time_process_time_ns(PyObject *module, PyObject *unused) { + time_module_state *state = get_time_state(module); _PyTime_t t; - if (_PyTime_GetProcessTimeWithInfo(&t, NULL) < 0) { + if (py_process_time(state, &t, NULL) < 0) { return NULL; } return _PyTime_AsNanosecondsObject(t); @@ -1617,7 +1609,7 @@ sum of the kernel and user-space CPU time."); static PyObject * -time_get_clock_info(PyObject *self, PyObject *args) +time_get_clock_info(PyObject *module, PyObject *args) { char *name; _Py_clock_info_t info; @@ -1656,7 +1648,8 @@ time_get_clock_info(PyObject *self, PyObject *args) } } else if (strcmp(name, "process_time") == 0) { - if (_PyTime_GetProcessTimeWithInfo(&t, &info) < 0) { + time_module_state *state = get_time_state(module); + if (py_process_time(state, &t, &info) < 0) { return NULL; } } @@ -2116,6 +2109,25 @@ time_exec(PyObject *module) } #endif +#ifdef HAVE_TIMES + if (_Py_GetTicksPerSecond(&state->ticks_per_second) < 0) { + PyErr_SetString(PyExc_RuntimeError, + "cannot read ticks_per_second"); + return -1; + } + + if (check_ticks_per_second(state->ticks_per_second, "_SC_CLK_TCK") < 0) { + return -1; + } +#endif + +#ifdef HAVE_CLOCK + state->clocks_per_second = CLOCKS_PER_SEC; + if (check_ticks_per_second(state->clocks_per_second, "CLOCKS_PER_SEC") < 0) { + return -1; + } +#endif + return 0; } diff --git a/Python/fileutils.c b/Python/fileutils.c index 649b188b5167d0..9d12bc89c95436 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -2943,3 +2943,27 @@ _Py_closerange(int first, int last) #endif /* USE_FDWALK */ _Py_END_SUPPRESS_IPH } + + +#ifndef MS_WINDOWS +// Ticks per second used by clock() and times() functions. +// See os.times() and time.process_time() implementations. +int +_Py_GetTicksPerSecond(long *ticks_per_second) +{ +#if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK) + long value = sysconf(_SC_CLK_TCK); + if (value < 1) { + return -1; + } + *ticks_per_second = value; +#elif defined(HZ) + assert(HZ >= 1); + *ticks_per_second = HZ; +#else + // Magic fallback value; may be bogus + *ticks_per_second = 60; +#endif + return 0; +} +#endif diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index aff67d7a835e89..95a72eb47048f2 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -528,11 +528,6 @@ pycore_init_runtime(_PyRuntimeState *runtime, return status; } - status = _PyTime_Init(); - if (_PyStatus_EXCEPTION(status)) { - return status; - } - status = _PyImport_Init(); if (_PyStatus_EXCEPTION(status)) { return status; From 5c5022b8625e34f0035ad5a23bc4c2f16649d134 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 1 Dec 2023 19:50:10 +0100 Subject: [PATCH 133/228] gh-112567: Add _PyTimeFraction C API (#112568) Use a fraction internally in the _PyTime API to reduce the risk of integer overflow: simplify the fraction using Greatest Common Divisor (GCD). The fraction API is used by time functions: perf_counter(), monotonic() and process_time(). For example, QueryPerformanceFrequency() usually returns 10 MHz on Windows 10 and newer. The fraction SEC_TO_NS / frequency = 1_000_000_000 / 10_000_000 can be simplified to 100 / 1. * Add _PyTimeFraction type. * Add functions: * _PyTimeFraction_Set() * _PyTimeFraction_Mul() * _PyTimeFraction_Resolution() * No longer check "numer * denom <= _PyTime_MAX" in _PyTimeFraction_Set(). _PyTimeFraction_Mul() uses _PyTime_Mul() which handles integer overflow. --- Include/internal/pycore_time.h | 33 ++++++-- Modules/timemodule.c | 54 +++++------- Python/pytime.c | 150 +++++++++++++++++++-------------- 3 files changed, 130 insertions(+), 107 deletions(-) diff --git a/Include/internal/pycore_time.h b/Include/internal/pycore_time.h index 7ea3485107572e..dabbd7b41556cd 100644 --- a/Include/internal/pycore_time.h +++ b/Include/internal/pycore_time.h @@ -253,13 +253,6 @@ PyAPI_FUNC(void) _PyTime_AsTimespec_clamp(_PyTime_t t, struct timespec *ts); // Compute t1 + t2. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow. extern _PyTime_t _PyTime_Add(_PyTime_t t1, _PyTime_t t2); -// Compute ticks * mul / div. -// Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow. -// The caller must ensure that ((div - 1) * mul) cannot overflow. -extern _PyTime_t _PyTime_MulDiv(_PyTime_t ticks, - _PyTime_t mul, - _PyTime_t div); - // Structure used by time.get_clock_info() typedef struct { const char *implementation; @@ -355,6 +348,32 @@ PyAPI_FUNC(_PyTime_t) _PyDeadline_Init(_PyTime_t timeout); PyAPI_FUNC(_PyTime_t) _PyDeadline_Get(_PyTime_t deadline); +// --- _PyTimeFraction ------------------------------------------------------- + +typedef struct { + _PyTime_t numer; + _PyTime_t denom; +} _PyTimeFraction; + +// Set a fraction. +// Return 0 on success. +// Return -1 if the fraction is invalid. +extern int _PyTimeFraction_Set( + _PyTimeFraction *frac, + _PyTime_t numer, + _PyTime_t denom); + +// Compute ticks * frac.numer / frac.denom. +// Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow. +extern _PyTime_t _PyTimeFraction_Mul( + _PyTime_t ticks, + const _PyTimeFraction *frac); + +// Compute a clock resolution: frac.numer / frac.denom / 1e9. +extern double _PyTimeFraction_Resolution( + const _PyTimeFraction *frac); + + #ifdef __cplusplus } #endif diff --git a/Modules/timemodule.c b/Modules/timemodule.c index aa0cdc5f026e7c..b3fe175d9b184a 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -69,25 +69,6 @@ module time /*[clinic end generated code: output=da39a3ee5e6b4b0d input=a668a08771581f36]*/ -#if defined(HAVE_TIMES) || defined(HAVE_CLOCK) -static int -check_ticks_per_second(long tps, const char *context) -{ - /* Effectively, check that _PyTime_MulDiv(t, SEC_TO_NS, tps) - cannot overflow. */ - if (tps >= 0 && (_PyTime_t)tps > _PyTime_MAX / SEC_TO_NS) { - PyErr_Format(PyExc_OverflowError, "%s is too large", context); - return -1; - } - if (tps < 1) { - PyErr_Format(PyExc_RuntimeError, "invalid %s", context); - return -1; - } - return 0; -} -#endif /* HAVE_TIMES || HAVE_CLOCK */ - - /* Forward declarations */ static int pysleep(_PyTime_t timeout); @@ -96,11 +77,11 @@ typedef struct { PyTypeObject *struct_time_type; #ifdef HAVE_TIMES // times() clock frequency in hertz - long ticks_per_second; + _PyTimeFraction times_base; #endif #ifdef HAVE_CLOCK // clock() frequency in hertz - long clocks_per_second; + _PyTimeFraction clock_base; #endif } time_module_state; @@ -174,10 +155,11 @@ Return the current time in nanoseconds since the Epoch."); static int py_clock(time_module_state *state, _PyTime_t *tp, _Py_clock_info_t *info) { - long clocks_per_second = state->clocks_per_second; + _PyTimeFraction *base = &state->clock_base; + if (info) { info->implementation = "clock()"; - info->resolution = 1.0 / (double)clocks_per_second; + info->resolution = _PyTimeFraction_Resolution(base); info->monotonic = 1; info->adjustable = 0; } @@ -189,7 +171,7 @@ py_clock(time_module_state *state, _PyTime_t *tp, _Py_clock_info_t *info) "or its value cannot be represented"); return -1; } - _PyTime_t ns = _PyTime_MulDiv(ticks, SEC_TO_NS, clocks_per_second); + _PyTime_t ns = _PyTimeFraction_Mul(ticks, base); *tp = _PyTime_FromNanoseconds(ns); return 0; } @@ -1257,7 +1239,7 @@ static int process_time_times(time_module_state *state, _PyTime_t *tp, _Py_clock_info_t *info) { - long ticks_per_second = state->ticks_per_second; + _PyTimeFraction *base = &state->times_base; struct tms process; if (times(&process) == (clock_t)-1) { @@ -1266,14 +1248,14 @@ process_time_times(time_module_state *state, _PyTime_t *tp, if (info) { info->implementation = "times()"; + info->resolution = _PyTimeFraction_Resolution(base); info->monotonic = 1; info->adjustable = 0; - info->resolution = 1.0 / (double)ticks_per_second; } _PyTime_t ns; - ns = _PyTime_MulDiv(process.tms_utime, SEC_TO_NS, ticks_per_second); - ns += _PyTime_MulDiv(process.tms_stime, SEC_TO_NS, ticks_per_second); + ns = _PyTimeFraction_Mul(process.tms_utime, base); + ns += _PyTimeFraction_Mul(process.tms_stime, base); *tp = _PyTime_FromNanoseconds(ns); return 1; } @@ -1395,8 +1377,7 @@ py_process_time(time_module_state *state, _PyTime_t *tp, // times() failed, ignore failure #endif - /* clock */ - /* Currently, Python 3 requires clock() to build: see issue #22624 */ + /* clock(). Python 3 requires clock() to build (see gh-66814) */ return py_clock(state, tp, info); #endif } @@ -2110,20 +2091,23 @@ time_exec(PyObject *module) #endif #ifdef HAVE_TIMES - if (_Py_GetTicksPerSecond(&state->ticks_per_second) < 0) { + long ticks_per_second; + if (_Py_GetTicksPerSecond(&ticks_per_second) < 0) { PyErr_SetString(PyExc_RuntimeError, "cannot read ticks_per_second"); return -1; } - - if (check_ticks_per_second(state->ticks_per_second, "_SC_CLK_TCK") < 0) { + if (_PyTimeFraction_Set(&state->times_base, SEC_TO_NS, + ticks_per_second) < 0) { + PyErr_Format(PyExc_OverflowError, "ticks_per_second is too large"); return -1; } #endif #ifdef HAVE_CLOCK - state->clocks_per_second = CLOCKS_PER_SEC; - if (check_ticks_per_second(state->clocks_per_second, "CLOCKS_PER_SEC") < 0) { + if (_PyTimeFraction_Set(&state->clock_base, SEC_TO_NS, + CLOCKS_PER_SEC) < 0) { + PyErr_Format(PyExc_OverflowError, "CLOCKS_PER_SEC is too large"); return -1; } #endif diff --git a/Python/pytime.c b/Python/pytime.c index e4813d4a9c2a2a..77cb95f8feb179 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -55,6 +55,43 @@ #endif +static _PyTime_t +_PyTime_GCD(_PyTime_t x, _PyTime_t y) +{ + // Euclidean algorithm + assert(x >= 1); + assert(y >= 1); + while (y != 0) { + _PyTime_t tmp = y; + y = x % y; + x = tmp; + } + assert(x >= 1); + return x; +} + + +int +_PyTimeFraction_Set(_PyTimeFraction *frac, _PyTime_t numer, _PyTime_t denom) +{ + if (numer < 1 || denom < 1) { + return -1; + } + + _PyTime_t gcd = _PyTime_GCD(numer, denom); + frac->numer = numer / gcd; + frac->denom = denom / gcd; + return 0; +} + + +double +_PyTimeFraction_Resolution(const _PyTimeFraction *frac) +{ + return (double)frac->numer / (double)frac->denom / 1e9; +} + + static void pytime_time_t_overflow(void) { @@ -152,11 +189,17 @@ _PyTime_Mul(_PyTime_t t, _PyTime_t k) } - - _PyTime_t -_PyTime_MulDiv(_PyTime_t ticks, _PyTime_t mul, _PyTime_t div) +_PyTimeFraction_Mul(_PyTime_t ticks, const _PyTimeFraction *frac) { + const _PyTime_t mul = frac->numer; + const _PyTime_t div = frac->denom; + + if (div == 1) { + // Fast-path taken by mach_absolute_time() with 1/1 time base. + return _PyTime_Mul(ticks, mul); + } + /* Compute (ticks * mul / div) in two parts to reduce the risk of integer overflow: compute the integer part, and then the remaining part. @@ -1016,51 +1059,34 @@ _PyTime_GetSystemClockWithInfo(_PyTime_t *t, _Py_clock_info_t *info) #ifdef __APPLE__ static int -py_mach_timebase_info(_PyTime_t *pnumer, _PyTime_t *pdenom, int raise) +py_mach_timebase_info(_PyTimeFraction *base, int raise) { - static mach_timebase_info_data_t timebase; - /* According to the Technical Q&A QA1398, mach_timebase_info() cannot - fail: https://developer.apple.com/library/mac/#qa/qa1398/ */ + mach_timebase_info_data_t timebase; + // According to the Technical Q&A QA1398, mach_timebase_info() cannot + // fail: https://developer.apple.com/library/mac/#qa/qa1398/ (void)mach_timebase_info(&timebase); - /* Sanity check: should never occur in practice */ - if (timebase.numer < 1 || timebase.denom < 1) { + // Check that timebase.numer and timebase.denom can be casted to + // _PyTime_t. In practice, timebase uses uint32_t, so casting cannot + // overflow. At the end, only make sure that the type is uint32_t + // (_PyTime_t is 64-bit long). + Py_BUILD_ASSERT(sizeof(timebase.numer) <= sizeof(_PyTime_t)); + Py_BUILD_ASSERT(sizeof(timebase.denom) <= sizeof(_PyTime_t)); + _PyTime_t numer = (_PyTime_t)timebase.numer; + _PyTime_t denom = (_PyTime_t)timebase.denom; + + // Known time bases: + // + // * (1, 1) on Intel: 1 ns + // * (1000000000, 33333335) on PowerPC: ~30 ns + // * (1000000000, 25000000) on PowerPC: 40 ns + if (_PyTimeFraction_Set(base, numer, denom) < 0) { if (raise) { PyErr_SetString(PyExc_RuntimeError, "invalid mach_timebase_info"); } return -1; } - - /* Check that timebase.numer and timebase.denom can be casted to - _PyTime_t. In practice, timebase uses uint32_t, so casting cannot - overflow. At the end, only make sure that the type is uint32_t - (_PyTime_t is 64-bit long). */ - static_assert(sizeof(timebase.numer) <= sizeof(_PyTime_t), - "timebase.numer is larger than _PyTime_t"); - static_assert(sizeof(timebase.denom) <= sizeof(_PyTime_t), - "timebase.denom is larger than _PyTime_t"); - - /* Make sure that _PyTime_MulDiv(ticks, timebase_numer, timebase_denom) - cannot overflow. - - Known time bases: - - * (1, 1) on Intel - * (1000000000, 33333335) or (1000000000, 25000000) on PowerPC - - None of these time bases can overflow with 64-bit _PyTime_t, but - check for overflow, just in case. */ - if ((_PyTime_t)timebase.numer > _PyTime_MAX / (_PyTime_t)timebase.denom) { - if (raise) { - PyErr_SetString(PyExc_OverflowError, - "mach_timebase_info is too large"); - } - return -1; - } - - *pnumer = (_PyTime_t)timebase.numer; - *pdenom = (_PyTime_t)timebase.denom; return 0; } #endif @@ -1109,17 +1135,16 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) } #elif defined(__APPLE__) - static _PyTime_t timebase_numer = 0; - static _PyTime_t timebase_denom = 0; - if (timebase_denom == 0) { - if (py_mach_timebase_info(&timebase_numer, &timebase_denom, raise_exc) < 0) { + static _PyTimeFraction base = {0, 0}; + if (base.denom == 0) { + if (py_mach_timebase_info(&base, raise_exc) < 0) { return -1; } } if (info) { info->implementation = "mach_absolute_time()"; - info->resolution = (double)timebase_numer / (double)timebase_denom * 1e-9; + info->resolution = _PyTimeFraction_Resolution(&base); info->monotonic = 1; info->adjustable = 0; } @@ -1129,7 +1154,7 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) assert(uticks <= (uint64_t)_PyTime_MAX); _PyTime_t ticks = (_PyTime_t)uticks; - _PyTime_t ns = _PyTime_MulDiv(ticks, timebase_numer, timebase_denom); + _PyTime_t ns = _PyTimeFraction_Mul(ticks, &base); *tp = pytime_from_nanoseconds(ns); #elif defined(__hpux) @@ -1213,7 +1238,7 @@ _PyTime_GetMonotonicClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) #ifdef MS_WINDOWS static int -py_win_perf_counter_frequency(LONGLONG *pfrequency, int raise) +py_win_perf_counter_frequency(_PyTimeFraction *base, int raise) { LONGLONG frequency; @@ -1225,25 +1250,20 @@ py_win_perf_counter_frequency(LONGLONG *pfrequency, int raise) // Since Windows XP, frequency cannot be zero. assert(frequency >= 1); - /* Make also sure that (ticks * SEC_TO_NS) cannot overflow in - _PyTime_MulDiv(), with ticks < frequency. + Py_BUILD_ASSERT(sizeof(_PyTime_t) == sizeof(frequency)); + _PyTime_t denom = (_PyTime_t)frequency; - Known QueryPerformanceFrequency() values: - - * 10,000,000 (10 MHz): 100 ns resolution - * 3,579,545 Hz (3.6 MHz): 279 ns resolution - - None of these frequencies can overflow with 64-bit _PyTime_t, but - check for integer overflow just in case. */ - if (frequency > _PyTime_MAX / SEC_TO_NS) { + // Known QueryPerformanceFrequency() values: + // + // * 10,000,000 (10 MHz): 100 ns resolution + // * 3,579,545 Hz (3.6 MHz): 279 ns resolution + if (_PyTimeFraction_Set(base, SEC_TO_NS, denom) < 0) { if (raise) { - PyErr_SetString(PyExc_OverflowError, - "QueryPerformanceFrequency is too large"); + PyErr_SetString(PyExc_RuntimeError, + "invalid QueryPerformanceFrequency"); } return -1; } - - *pfrequency = frequency; return 0; } @@ -1253,16 +1273,16 @@ py_get_win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) { assert(info == NULL || raise_exc); - static LONGLONG frequency = 0; - if (frequency == 0) { - if (py_win_perf_counter_frequency(&frequency, raise_exc) < 0) { + static _PyTimeFraction base = {0, 0}; + if (base.denom == 0) { + if (py_win_perf_counter_frequency(&base, raise_exc) < 0) { return -1; } } if (info) { info->implementation = "QueryPerformanceCounter()"; - info->resolution = 1.0 / (double)frequency; + info->resolution = _PyTimeFraction_Resolution(&base); info->monotonic = 1; info->adjustable = 0; } @@ -1278,7 +1298,7 @@ py_get_win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) "LONGLONG is larger than _PyTime_t"); ticks = (_PyTime_t)ticksll; - _PyTime_t ns = _PyTime_MulDiv(ticks, SEC_TO_NS, (_PyTime_t)frequency); + _PyTime_t ns = _PyTimeFraction_Mul(ticks, &base); *tp = pytime_from_nanoseconds(ns); return 0; } From 939fc6d6eab9b7ea8c244d513610dbdd556503a7 Mon Sep 17 00:00:00 2001 From: William Wen Date: Fri, 1 Dec 2023 14:18:16 -0800 Subject: [PATCH 134/228] gh-106922: Support multi-line error locations in traceback (attempt 2) (#112097) --- Doc/library/traceback.rst | 13 +- Lib/test/test_doctest.py | 3 + Lib/test/test_exceptions.py | 3 +- Lib/test/test_repl.py | 3 +- Lib/test/test_sys.py | 6 +- Lib/test/test_traceback.py | 435 ++++++++++++++++-- Lib/test/test_warnings/__init__.py | 4 +- Lib/traceback.py | 368 +++++++++++---- ...-11-15-01-36-04.gh-issue-106922.qslOVH.rst | 1 + 9 files changed, 709 insertions(+), 127 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-15-01-36-04.gh-issue-106922.qslOVH.rst diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst index 80dda5ec520d7a..2d5ea19b2cb892 100644 --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -523,27 +523,32 @@ The output for the example would look similar to this: *** print_tb: File "", line 10, in lumberjack() + ~~~~~~~~~~^^ *** print_exception: Traceback (most recent call last): File "", line 10, in lumberjack() + ~~~~~~~~~~^^ File "", line 4, in lumberjack bright_side_of_life() + ~~~~~~~~~~~~~~~~~~~^^ IndexError: tuple index out of range *** print_exc: Traceback (most recent call last): File "", line 10, in lumberjack() + ~~~~~~~~~~^^ File "", line 4, in lumberjack bright_side_of_life() + ~~~~~~~~~~~~~~~~~~~^^ IndexError: tuple index out of range *** format_exc, first and last line: Traceback (most recent call last): IndexError: tuple index out of range *** format_exception: ['Traceback (most recent call last):\n', - ' File "", line 10, in \n lumberjack()\n', - ' File "", line 4, in lumberjack\n bright_side_of_life()\n', + ' File "", line 10, in \n lumberjack()\n ~~~~~~~~~~^^\n', + ' File "", line 4, in lumberjack\n bright_side_of_life()\n ~~~~~~~~~~~~~~~~~~~^^\n', ' File "", line 7, in bright_side_of_life\n return tuple()[0]\n ~~~~~~~^^^\n', 'IndexError: tuple index out of range\n'] *** extract_tb: @@ -551,8 +556,8 @@ The output for the example would look similar to this: , line 4 in lumberjack>, , line 7 in bright_side_of_life>] *** format_tb: - [' File "", line 10, in \n lumberjack()\n', - ' File "", line 4, in lumberjack\n bright_side_of_life()\n', + [' File "", line 10, in \n lumberjack()\n ~~~~~~~~~~^^\n', + ' File "", line 4, in lumberjack\n bright_side_of_life()\n ~~~~~~~~~~~~~~~~~~~^^\n', ' File "", line 7, in bright_side_of_life\n return tuple()[0]\n ~~~~~~~^^^\n'] *** tb_lineno: 10 diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index 772dbd1d021305..36328f8086c7ad 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -2922,6 +2922,9 @@ def test_unicode(): """ Traceback (most recent call last): File ... exec(compile(example.source, filename, "single", + ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + compileflags, True), test.globs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "", line 1, in raise Exception('clé') Exception: clé diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 8ccf08703e5389..c57488e44aecc6 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -2080,6 +2080,7 @@ def test_multiline_not_highlighted(self): """, [ ' 1 < 2 and', + ' 3 > 4', 'AssertionError', ], ), @@ -2087,7 +2088,7 @@ def test_multiline_not_highlighted(self): for source, expected in cases: with self.subTest(source): result = self.write_source(source) - self.assertEqual(result[-2:], expected) + self.assertEqual(result[-len(expected):], expected) class SyntaxErrorTests(unittest.TestCase): diff --git a/Lib/test/test_repl.py b/Lib/test/test_repl.py index 7533376e015e73..a28d1595f44533 100644 --- a/Lib/test/test_repl.py +++ b/Lib/test/test_repl.py @@ -161,10 +161,11 @@ def foo(x): output = kill_python(p) self.assertEqual(p.returncode, 0) - traceback_lines = output.splitlines()[-7:-1] + traceback_lines = output.splitlines()[-8:-1] expected_lines = [ ' File "", line 1, in ', ' foo(0)', + ' ~~~^^^', ' File "", line 2, in foo', ' 1 / x', ' ~~^~~', diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 7f49fb004272bb..0028281596fa4b 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1115,8 +1115,10 @@ def check(tracebacklimit, expected): b'Traceback (most recent call last):', b' File "", line 8, in ', b' f2()', + b' ~~^^', b' File "", line 6, in f2', b' f1()', + b' ~~^^', b' File "", line 4, in f1', b' 1 / 0', b' ~~^~~', @@ -1124,8 +1126,8 @@ def check(tracebacklimit, expected): ] check(10, traceback) check(3, traceback) - check(2, traceback[:1] + traceback[3:]) - check(1, traceback[:1] + traceback[5:]) + check(2, traceback[:1] + traceback[4:]) + check(1, traceback[:1] + traceback[7:]) check(0, [traceback[-1]]) check(-1, [traceback[-1]]) check(1<<1000, traceback) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index c58d979bdd0115..b60e06ff37f494 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -578,6 +578,7 @@ def f(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+1}, in f\n' ' if True: raise ValueError("basic caret tests")\n' ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' @@ -596,6 +597,7 @@ def f_with_unicode(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+1}, in f_with_unicode\n' ' if True: raise ValueError("Ĥellö Wörld")\n' ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' @@ -613,6 +615,7 @@ def foo(a: THIS_DOES_NOT_EXIST ) -> int: 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+1}, in f_with_type\n' ' def foo(a: THIS_DOES_NOT_EXIST ) -> int:\n' ' ^^^^^^^^^^^^^^^^^^^\n' @@ -633,9 +636,14 @@ def f_with_multiline(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+1}, in f_with_multiline\n' ' if True: raise ValueError(\n' - ' ^^^^^^^^^^^^^^^^^' + ' ^^^^^^^^^^^^^^^^^\n' + ' "error over multiple lines"\n' + ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' + ' )\n' + ' ^' ) result_lines = self.get_exception(f_with_multiline) self.assertEqual(result_lines, expected_f.splitlines()) @@ -664,9 +672,10 @@ def f_with_multiline(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+2}, in f_with_multiline\n' ' return compile(code, "?", "exec")\n' - ' ^^^^^^^^^^^^^^^^^^^^^^^^^^\n' + ' ~~~~~~~^^^^^^^^^^^^^^^^^^^\n' ' File "?", line 7\n' ' foo(a, z\n' ' ^' @@ -689,9 +698,12 @@ def f_with_multiline(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+2}, in f_with_multiline\n' ' 2 + 1 /\n' - ' ^^^' + ' ~~^\n' + ' 0\n' + ' ~' ) result_lines = self.get_exception(f_with_multiline) self.assertEqual(result_lines, expected_f.splitlines()) @@ -706,6 +718,7 @@ def f_with_binary_operator(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+2}, in f_with_binary_operator\n' ' return 10 + divisor / 0 + 30\n' ' ~~~~~~~~^~~\n' @@ -723,6 +736,7 @@ def f_with_binary_operator(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+2}, in f_with_binary_operator\n' ' return 10 + áóí / 0 + 30\n' ' ~~~~^~~\n' @@ -740,6 +754,7 @@ def f_with_binary_operator(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+2}, in f_with_binary_operator\n' ' return 10 + divisor // 0 + 30\n' ' ~~~~~~~~^^~~\n' @@ -751,16 +766,102 @@ def test_caret_for_binary_operators_with_spaces_and_parenthesis(self): def f_with_binary_operator(): a = 1 b = "" - return ( a ) + b + return ( a ) +b lineno_f = f_with_binary_operator.__code__.co_firstlineno expected_error = ( 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n' - ' return ( a ) + b\n' - ' ~~~~~~~~~~^~~\n' + ' return ( a ) +b\n' + ' ~~~~~~~~~~^~\n' + ) + result_lines = self.get_exception(f_with_binary_operator) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_caret_for_binary_operators_multiline(self): + def f_with_binary_operator(): + b = 1 + c = "" + a = b \ + +\ + c # test + return a + + lineno_f = f_with_binary_operator.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n' + ' a = b \\\n' + ' ~~~~~~\n' + ' +\\\n' + ' ^~\n' + ' c # test\n' + ' ~\n' + ) + result_lines = self.get_exception(f_with_binary_operator) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_caret_for_binary_operators_multiline_two_char(self): + def f_with_binary_operator(): + b = 1 + c = "" + a = ( + (b # test + + ) \ + # + + << (c # test + \ + ) # test + ) + return a + + lineno_f = f_with_binary_operator.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+4}, in f_with_binary_operator\n' + ' (b # test +\n' + ' ~~~~~~~~~~~~\n' + ' ) \\\n' + ' ~~~~\n' + ' # +\n' + ' ~~~\n' + ' << (c # test\n' + ' ^^~~~~~~~~~~~\n' + ' \\\n' + ' ~\n' + ' ) # test\n' + ' ~\n' + ) + result_lines = self.get_exception(f_with_binary_operator) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_caret_for_binary_operators_multiline_with_unicode(self): + def f_with_binary_operator(): + b = 1 + a = ("ááá" + + "áá") + b + return a + + lineno_f = f_with_binary_operator.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+2}, in f_with_binary_operator\n' + ' a = ("ááá" +\n' + ' ~~~~~~~~\n' + ' "áá") + b\n' + ' ~~~~~~^~~\n' ) result_lines = self.get_exception(f_with_binary_operator) self.assertEqual(result_lines, expected_error.splitlines()) @@ -775,6 +876,7 @@ def f_with_subscript(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+2}, in f_with_subscript\n' " return some_dict['x']['y']['z']\n" ' ~~~~~~~~~~~~~~~~~~~^^^^^\n' @@ -792,6 +894,7 @@ def f_with_subscript(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+2}, in f_with_subscript\n' " return some_dict['ó']['á']['í']['beta']\n" ' ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^\n' @@ -810,6 +913,7 @@ def f_with_binary_operator(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n' ' return b [ a ] + c\n' ' ~~~~~~^^^^^^^^^\n' @@ -817,6 +921,226 @@ def f_with_binary_operator(): result_lines = self.get_exception(f_with_binary_operator) self.assertEqual(result_lines, expected_error.splitlines()) + def test_caret_for_subscript_multiline(self): + def f_with_subscript(): + bbbbb = {} + ccc = 1 + ddd = 2 + b = bbbbb \ + [ ccc # test + + + ddd \ + + ] # test + return b + + lineno_f = f_with_subscript.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+4}, in f_with_subscript\n' + ' b = bbbbb \\\n' + ' ~~~~~~~\n' + ' [ ccc # test\n' + ' ^^^^^^^^^^^^^\n' + ' \n' + ' \n' + ' + ddd \\\n' + ' ^^^^^^^^\n' + ' \n' + ' \n' + ' ] # test\n' + ' ^\n' + ) + result_lines = self.get_exception(f_with_subscript) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_caret_for_call(self): + def f_with_call(): + def f1(a): + def f2(b): + raise RuntimeError("fail") + return f2 + return f1("x")("y") + + lineno_f = f_with_call.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+5}, in f_with_call\n' + ' return f1("x")("y")\n' + ' ~~~~~~~^^^^^\n' + f' File "{__file__}", line {lineno_f+3}, in f2\n' + ' raise RuntimeError("fail")\n' + ) + result_lines = self.get_exception(f_with_call) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_caret_for_call_unicode(self): + def f_with_call(): + def f1(a): + def f2(b): + raise RuntimeError("fail") + return f2 + return f1("ó")("á") + + lineno_f = f_with_call.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+5}, in f_with_call\n' + ' return f1("ó")("á")\n' + ' ~~~~~~~^^^^^\n' + f' File "{__file__}", line {lineno_f+3}, in f2\n' + ' raise RuntimeError("fail")\n' + ) + result_lines = self.get_exception(f_with_call) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_caret_for_call_with_spaces_and_parenthesis(self): + def f_with_binary_operator(): + def f(a): + raise RuntimeError("fail") + return f ( "x" ) + 2 + + lineno_f = f_with_binary_operator.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n' + ' return f ( "x" ) + 2\n' + ' ~~~~~~^^^^^^^^^^^\n' + f' File "{__file__}", line {lineno_f+2}, in f\n' + ' raise RuntimeError("fail")\n' + ) + result_lines = self.get_exception(f_with_binary_operator) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_caret_for_call_multiline(self): + def f_with_call(): + class C: + def y(self, a): + def f(b): + raise RuntimeError("fail") + return f + def g(x): + return C() + a = (g(1).y)( + 2 + )(3)(4) + return a + + lineno_f = f_with_call.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+8}, in f_with_call\n' + ' a = (g(1).y)(\n' + ' ~~~~~~~~~\n' + ' 2\n' + ' ~\n' + ' )(3)(4)\n' + ' ~^^^\n' + f' File "{__file__}", line {lineno_f+4}, in f\n' + ' raise RuntimeError("fail")\n' + ) + result_lines = self.get_exception(f_with_call) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_many_lines(self): + def f(): + x = 1 + if True: x += ( + "a" + + "a" + ) # test + + lineno_f = f.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+2}, in f\n' + ' if True: x += (\n' + ' ^^^^^^\n' + ' ...<2 lines>...\n' + ' ) # test\n' + ' ^\n' + ) + result_lines = self.get_exception(f) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_many_lines_no_caret(self): + def f(): + x = 1 + x += ( + "a" + + "a" + ) + + lineno_f = f.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+2}, in f\n' + ' x += (\n' + ' ...<2 lines>...\n' + ' )\n' + ) + result_lines = self.get_exception(f) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_many_lines_binary_op(self): + def f_with_binary_operator(): + b = 1 + c = "a" + a = ( + b + + b + ) + ( + c + + c + + c + ) + return a + + lineno_f = f_with_binary_operator.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n' + ' a = (\n' + ' ~\n' + ' b +\n' + ' ~~~\n' + ' b\n' + ' ~\n' + ' ) + (\n' + ' ~~^~~\n' + ' c +\n' + ' ~~~\n' + ' ...<2 lines>...\n' + ' )\n' + ' ~\n' + ) + result_lines = self.get_exception(f_with_binary_operator) + self.assertEqual(result_lines, expected_error.splitlines()) + def test_traceback_specialization_with_syntax_error(self): bytecode = compile("1 / 0 / 1 / 2\n", TESTFN, "exec") @@ -833,6 +1157,7 @@ def test_traceback_specialization_with_syntax_error(self): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{TESTFN}", line {lineno_f}, in \n' " 1 $ 0 / 1 / 2\n" ' ^^^^^\n' @@ -855,6 +1180,7 @@ def test_traceback_very_long_line(self): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{TESTFN}", line {lineno_f}, in \n' f' {source}\n' f' {" "*len("if True: ") + "^"*256}\n' @@ -872,6 +1198,7 @@ def f_with_subscript(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+2}, in f_with_subscript\n' " some_dict['x']['y']['z']\n" ' ~~~~~~~~~~~~~~~~~~~^^^^^\n' @@ -891,6 +1218,7 @@ def exc(): f' + Exception Group Traceback (most recent call last):\n' f' | File "{__file__}", line {self.callable_line}, in get_exception\n' f' | callable()\n' + f' | ~~~~~~~~^^\n' f' | File "{__file__}", line {exc.__code__.co_firstlineno + 1}, in exc\n' f' | if True: raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])\n' f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' @@ -956,6 +1284,7 @@ def g(): pass 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_applydescs + 1}, in applydecs\n' ' @dec_error\n' ' ^^^^^^^^^\n' @@ -974,6 +1303,7 @@ class A: pass 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_applydescs_class + 1}, in applydecs_class\n' ' @dec_error\n' ' ^^^^^^^^^\n' @@ -992,6 +1322,7 @@ def f(): "Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", " callable()", + " ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f", " .method", " ^^^^^^", @@ -1008,6 +1339,7 @@ def f(): "Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", " callable()", + " ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f", " method", ] @@ -1023,6 +1355,7 @@ def f(): "Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", " callable()", + " ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f", " . method", " ^^^^^^", @@ -1038,6 +1371,7 @@ def f(): "Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", " callable()", + " ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f", " width", ] @@ -1054,6 +1388,7 @@ def f(): "Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", " callable()", + " ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f", " raise ValueError(width)", ] @@ -1072,9 +1407,12 @@ def f(): "Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", " callable()", + " ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 4}, in f", f" print(1, www(", - f" ^^^^^^^", + f" ~~~~~~^", + f" th))", + f" ^^^^^", ] self.assertEqual(actual, expected) @@ -1089,6 +1427,7 @@ def f(): f"Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", f" callable()", + f" ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 3}, in f", f" return 说明说明 / şçöğıĤellö", f" ~~~~~~~~~^~~~~~~~~~~~", @@ -1105,6 +1444,7 @@ def f(): f"Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", f" callable()", + f" ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f", f' return "✨🐍" + func_说明说明("📗🚛",', f" ^^^^^^^^^^^^^", @@ -1127,6 +1467,7 @@ def f(): f"Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", f" callable()", + f" ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 8}, in f", f' return my_dct["✨🚛✨"]["说明"]["🐍"]["说明"]["🐍🐍"]', f" ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^", @@ -1141,6 +1482,7 @@ def f(): expected = ['Traceback (most recent call last):', f' File "{__file__}", line {self.callable_line}, in get_exception', ' callable()', + ' ~~~~~~~~^^', f' File "{__file__}", line {f.__code__.co_firstlineno + 1}, in f', ' raise MemoryError()'] self.assertEqual(actual, expected) @@ -1187,6 +1529,14 @@ class TracebackFormatMixin: def some_exception(self): raise KeyError('blah') + def _filter_debug_ranges(self, expected): + return [line for line in expected if not set(line.strip()) <= set("^~")] + + def _maybe_filter_debug_ranges(self, expected): + if not self.DEBUG_RANGES: + return self._filter_debug_ranges(expected) + return expected + @cpython_only def check_traceback_format(self, cleanup_func=None): from _testcapi import traceback_print @@ -1199,6 +1549,11 @@ def check_traceback_format(self, cleanup_func=None): cleanup_func(tb.tb_next) traceback_fmt = 'Traceback (most recent call last):\n' + \ ''.join(traceback.format_tb(tb)) + # clear caret lines from traceback_fmt since internal API does + # not emit them + traceback_fmt = "\n".join( + self._filter_debug_ranges(traceback_fmt.splitlines()) + ) + "\n" file_ = StringIO() traceback_print(tb, file_) python_fmt = file_.getvalue() @@ -1291,12 +1646,16 @@ def f(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n' ' f()\n' + ' ~^^\n' f' File "{__file__}", line {lineno_f+1}, in f\n' ' f()\n' + ' ~^^\n' f' File "{__file__}", line {lineno_f+1}, in f\n' ' f()\n' + ' ~^^\n' f' File "{__file__}", line {lineno_f+1}, in f\n' ' f()\n' + ' ~^^\n' # XXX: The following line changes depending on whether the tests # are run through the interactive interpreter or with -m # It also varies depending on the platform (stack size) @@ -1305,7 +1664,7 @@ def f(): 'RecursionError: maximum recursion depth exceeded\n' ) - expected = result_f.splitlines() + expected = self._maybe_filter_debug_ranges(result_f.splitlines()) actual = stderr_f.getvalue().splitlines() # Check the output text matches expectations @@ -1337,13 +1696,13 @@ def g(count=10): result_g = ( f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' ' [Previous line repeated 7 more times]\n' f' File "{__file__}", line {lineno_g+3}, in g\n' ' raise ValueError\n' @@ -1353,11 +1712,10 @@ def g(count=10): 'Traceback (most recent call last):\n' f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n' ' g()\n' + ' ~^^\n' ) - expected = (tb_line + result_g).splitlines() + expected = self._maybe_filter_debug_ranges((tb_line + result_g).splitlines()) actual = stderr_g.getvalue().splitlines() - if not self.DEBUG_RANGES: - expected = [line for line in expected if not set(line.strip()) == {"^"}] self.assertEqual(actual, expected) # Check 2 different repetitive sections @@ -1379,23 +1737,23 @@ def h(count=10): 'Traceback (most recent call last):\n' f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n' ' h()\n' + ' ~^^\n' f' File "{__file__}", line {lineno_h+2}, in h\n' ' return h(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_h+2}, in h\n' ' return h(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_h+2}, in h\n' ' return h(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' ' [Previous line repeated 7 more times]\n' f' File "{__file__}", line {lineno_h+3}, in h\n' ' g()\n' + ' ~^^\n' ) - expected = (result_h + result_g).splitlines() + expected = self._maybe_filter_debug_ranges((result_h + result_g).splitlines()) actual = stderr_h.getvalue().splitlines() - if not self.DEBUG_RANGES: - expected = [line for line in expected if not set(line.strip()) == {"^"}] self.assertEqual(actual, expected) # Check the boundary conditions. First, test just below the cutoff. @@ -1409,26 +1767,25 @@ def h(count=10): result_g = ( f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+3}, in g\n' ' raise ValueError\n' 'ValueError\n' ) tb_line = ( 'Traceback (most recent call last):\n' - f' File "{__file__}", line {lineno_g+81}, in _check_recursive_traceback_display\n' + f' File "{__file__}", line {lineno_g+80}, in _check_recursive_traceback_display\n' ' g(traceback._RECURSIVE_CUTOFF)\n' + ' ~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' ) - expected = (tb_line + result_g).splitlines() + expected = self._maybe_filter_debug_ranges((tb_line + result_g).splitlines()) actual = stderr_g.getvalue().splitlines() - if not self.DEBUG_RANGES: - expected = [line for line in expected if not set(line.strip()) == {"^"}] self.assertEqual(actual, expected) # Second, test just above the cutoff. @@ -1442,13 +1799,13 @@ def h(count=10): result_g = ( f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' ' [Previous line repeated 1 more time]\n' f' File "{__file__}", line {lineno_g+3}, in g\n' ' raise ValueError\n' @@ -1456,13 +1813,12 @@ def h(count=10): ) tb_line = ( 'Traceback (most recent call last):\n' - f' File "{__file__}", line {lineno_g+114}, in _check_recursive_traceback_display\n' + f' File "{__file__}", line {lineno_g+112}, in _check_recursive_traceback_display\n' ' g(traceback._RECURSIVE_CUTOFF + 1)\n' + ' ~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' ) - expected = (tb_line + result_g).splitlines() + expected = self._maybe_filter_debug_ranges((tb_line + result_g).splitlines()) actual = stderr_g.getvalue().splitlines() - if not self.DEBUG_RANGES: - expected = [line for line in expected if not set(line.strip()) == {"^"}] self.assertEqual(actual, expected) @requires_debug_ranges() @@ -1942,6 +2298,7 @@ def exc(): f' + Exception Group Traceback (most recent call last):\n' f' | File "{__file__}", line {self.callable_line}, in get_exception\n' f' | exception_or_callable()\n' + f' | ~~~~~~~~~~~~~~~~~~~~~^^\n' f' | File "{__file__}", line {exc.__code__.co_firstlineno + 1}, in exc\n' f' | raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])\n' f' | ExceptionGroup: eg (2 sub-exceptions)\n' @@ -1977,6 +2334,7 @@ def exc(): f' + Exception Group Traceback (most recent call last):\n' f' | File "{__file__}", line {self.callable_line}, in get_exception\n' f' | exception_or_callable()\n' + f' | ~~~~~~~~~~~~~~~~~~~~~^^\n' f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n' f' | raise EG("eg2", [ValueError(3), TypeError(4)]) from e\n' f' | ExceptionGroup: eg2 (2 sub-exceptions)\n' @@ -2028,6 +2386,7 @@ def exc(): f'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' f' exception_or_callable()\n' + f' ~~~~~~~~~~~~~~~~~~~~~^^\n' f' File "{__file__}", line {exc.__code__.co_firstlineno + 8}, in exc\n' f' raise ImportError(5)\n' f'ImportError: 5\n') @@ -2074,6 +2433,7 @@ def exc(): f' + Exception Group Traceback (most recent call last):\n' f' | File "{__file__}", line {self.callable_line}, in get_exception\n' f' | exception_or_callable()\n' + f' | ~~~~~~~~~~~~~~~~~~~~~^^\n' f' | File "{__file__}", line {exc.__code__.co_firstlineno + 11}, in exc\n' f' | raise EG("top", [VE(5)])\n' f' | ExceptionGroup: top (1 sub-exception)\n' @@ -2233,6 +2593,7 @@ def exc(): expected = (f' + Exception Group Traceback (most recent call last):\n' f' | File "{__file__}", line {self.callable_line}, in get_exception\n' f' | exception_or_callable()\n' + f' | ~~~~~~~~~~~~~~~~~~~~~^^\n' f' | File "{__file__}", line {exc.__code__.co_firstlineno + 9}, in exc\n' f' | raise ExceptionGroup("nested", excs)\n' f' | ExceptionGroup: nested (2 sub-exceptions)\n' @@ -2284,6 +2645,7 @@ def exc(): expected = (f' + Exception Group Traceback (most recent call last):\n' f' | File "{__file__}", line {self.callable_line}, in get_exception\n' f' | exception_or_callable()\n' + f' | ~~~~~~~~~~~~~~~~~~~~~^^\n' f' | File "{__file__}", line {exc.__code__.co_firstlineno + 10}, in exc\n' f' | raise ExceptionGroup("nested", excs)\n' f' | ExceptionGroup: nested (2 sub-exceptions)\n' @@ -2552,7 +2914,7 @@ def test_basics(self): def test_lazy_lines(self): linecache.clearcache() f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False) - self.assertEqual(None, f._line) + self.assertEqual(None, f._lines) linecache.lazycache("f", globals()) self.assertEqual( '"""Test cases for traceback module"""', @@ -3143,6 +3505,7 @@ def test_exception_group_format(self): f' | Traceback (most recent call last):', f' | File "{__file__}", line {lno_g+9}, in _get_exception_group', f' | f()', + f' | ~^^', f' | File "{__file__}", line {lno_f+1}, in f', f' | 1/0', f' | ~^~', @@ -3151,6 +3514,7 @@ def test_exception_group_format(self): f' | Traceback (most recent call last):', f' | File "{__file__}", line {lno_g+13}, in _get_exception_group', f' | g(42)', + f' | ~^^^^', f' | File "{__file__}", line {lno_g+1}, in g', f' | raise ValueError(v)', f' | ValueError: 42', @@ -3159,6 +3523,7 @@ def test_exception_group_format(self): f' | Traceback (most recent call last):', f' | File "{__file__}", line {lno_g+20}, in _get_exception_group', f' | g(24)', + f' | ~^^^^', f' | File "{__file__}", line {lno_g+1}, in g', f' | raise ValueError(v)', f' | ValueError: 24', diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index 232480c46e0a00..50b0f3fff04c57 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -1260,8 +1260,8 @@ def test_conflicting_envvar_and_command_line(self): b" File \"\", line 1, in ", b' import sys, warnings; sys.stdout.write(str(sys.warnoptions)); warnings.w' b"arn('Message', DeprecationWarning)", - b' ^^^^^^^^^^' - b'^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^', + b' ~~~~~~~~~~' + b'~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^', b"DeprecationWarning: Message"]) def test_default_filter_configuration(self): diff --git a/Lib/traceback.py b/Lib/traceback.py index 5d83f85ac3edb0..a0485a7023d07d 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -274,7 +274,7 @@ class FrameSummary: """ __slots__ = ('filename', 'lineno', 'end_lineno', 'colno', 'end_colno', - 'name', '_line', 'locals') + 'name', '_lines', '_lines_dedented', 'locals') def __init__(self, filename, lineno, name, *, lookup_line=True, locals=None, line=None, @@ -290,15 +290,16 @@ def __init__(self, filename, lineno, name, *, lookup_line=True, """ self.filename = filename self.lineno = lineno + self.end_lineno = lineno if end_lineno is None else end_lineno + self.colno = colno + self.end_colno = end_colno self.name = name - self._line = line + self._lines = line + self._lines_dedented = None if lookup_line: self.line self.locals = {k: _safe_string(v, 'local', func=repr) for k, v in locals.items()} if locals else None - self.end_lineno = end_lineno - self.colno = colno - self.end_colno = end_colno def __eq__(self, other): if isinstance(other, FrameSummary): @@ -323,19 +324,39 @@ def __repr__(self): def __len__(self): return 4 + def _set_lines(self): + if ( + self._lines is None + and self.lineno is not None + and self.end_lineno is not None + ): + lines = [] + for lineno in range(self.lineno, self.end_lineno + 1): + # treat errors (empty string) and empty lines (newline) as the same + lines.append(linecache.getline(self.filename, lineno).rstrip()) + self._lines = "\n".join(lines) + "\n" + @property - def _original_line(self): + def _original_lines(self): # Returns the line as-is from the source, without modifying whitespace. - self.line - return self._line + self._set_lines() + return self._lines + + @property + def _dedented_lines(self): + # Returns _original_lines, but dedented + self._set_lines() + if self._lines_dedented is None and self._lines is not None: + self._lines_dedented = textwrap.dedent(self._lines) + return self._lines_dedented @property def line(self): - if self._line is None: - if self.lineno is None: - return None - self._line = linecache.getline(self.filename, self.lineno) - return self._line.strip() + self._set_lines() + if self._lines is None: + return None + # return only the first line, stripped + return self._lines.partition("\n")[0].strip() def walk_stack(f): @@ -487,56 +508,135 @@ def format_frame_summary(self, frame_summary): filename = "" row.append(' File "{}", line {}, in {}\n'.format( filename, frame_summary.lineno, frame_summary.name)) - if frame_summary.line: - stripped_line = frame_summary.line.strip() - row.append(' {}\n'.format(stripped_line)) - - line = frame_summary._original_line - orig_line_len = len(line) - frame_line_len = len(frame_summary.line.lstrip()) - stripped_characters = orig_line_len - frame_line_len + if frame_summary._dedented_lines and frame_summary._dedented_lines.strip(): if ( - frame_summary.colno is not None - and frame_summary.end_colno is not None + frame_summary.colno is None or + frame_summary.end_colno is None ): - start_offset = _byte_offset_to_character_offset( - line, frame_summary.colno) - end_offset = _byte_offset_to_character_offset( - line, frame_summary.end_colno) - code_segment = line[start_offset:end_offset] + # only output first line if column information is missing + row.append(textwrap.indent(frame_summary.line, ' ') + "\n") + else: + # get first and last line + all_lines_original = frame_summary._original_lines.splitlines() + first_line = all_lines_original[0] + # assume all_lines_original has enough lines (since we constructed it) + last_line = all_lines_original[frame_summary.end_lineno - frame_summary.lineno] + + # character index of the start/end of the instruction + start_offset = _byte_offset_to_character_offset(first_line, frame_summary.colno) + end_offset = _byte_offset_to_character_offset(last_line, frame_summary.end_colno) + + all_lines = frame_summary._dedented_lines.splitlines()[ + :frame_summary.end_lineno - frame_summary.lineno + 1 + ] + # adjust start/end offset based on dedent + dedent_characters = len(first_line) - len(all_lines[0]) + start_offset = max(0, start_offset - dedent_characters) + end_offset = max(0, end_offset - dedent_characters) + + # When showing this on a terminal, some of the non-ASCII characters + # might be rendered as double-width characters, so we need to take + # that into account when calculating the length of the line. + dp_start_offset = _display_width(all_lines[0], offset=start_offset) + dp_end_offset = _display_width(all_lines[-1], offset=end_offset) + + # get exact code segment corresponding to the instruction + segment = "\n".join(all_lines) + segment = segment[start_offset:len(segment) - (len(all_lines[-1]) - end_offset)] + + # attempt to parse for anchors anchors = None - if frame_summary.lineno == frame_summary.end_lineno: - with suppress(Exception): - anchors = _extract_caret_anchors_from_line_segment(code_segment) - else: - # Don't count the newline since the anchors only need to - # go up until the last character of the line. - end_offset = len(line.rstrip()) - - # show indicators if primary char doesn't span the frame line - if end_offset - start_offset < len(stripped_line) or ( - anchors and anchors.right_start_offset - anchors.left_end_offset > 0): - # When showing this on a terminal, some of the non-ASCII characters - # might be rendered as double-width characters, so we need to take - # that into account when calculating the length of the line. - dp_start_offset = _display_width(line, start_offset) + 1 - dp_end_offset = _display_width(line, end_offset) + 1 - - row.append(' ') - row.append(' ' * (dp_start_offset - stripped_characters)) - - if anchors: - dp_left_end_offset = _display_width(code_segment, anchors.left_end_offset) - dp_right_start_offset = _display_width(code_segment, anchors.right_start_offset) - row.append(anchors.primary_char * dp_left_end_offset) - row.append(anchors.secondary_char * (dp_right_start_offset - dp_left_end_offset)) - row.append(anchors.primary_char * (dp_end_offset - dp_start_offset - dp_right_start_offset)) - else: - row.append('^' * (dp_end_offset - dp_start_offset)) + with suppress(Exception): + anchors = _extract_caret_anchors_from_line_segment(segment) + + # only use carets if there are anchors or the carets do not span all lines + show_carets = False + if anchors or all_lines[0][:start_offset].lstrip() or all_lines[-1][end_offset:].rstrip(): + show_carets = True + + result = [] + + # only display first line, last line, and lines around anchor start/end + significant_lines = {0, len(all_lines) - 1} + + anchors_left_end_offset = 0 + anchors_right_start_offset = 0 + primary_char = "^" + secondary_char = "^" + if anchors: + anchors_left_end_offset = anchors.left_end_offset + anchors_right_start_offset = anchors.right_start_offset + # computed anchor positions do not take start_offset into account, + # so account for it here + if anchors.left_end_lineno == 0: + anchors_left_end_offset += start_offset + if anchors.right_start_lineno == 0: + anchors_right_start_offset += start_offset + + # account for display width + anchors_left_end_offset = _display_width( + all_lines[anchors.left_end_lineno], offset=anchors_left_end_offset + ) + anchors_right_start_offset = _display_width( + all_lines[anchors.right_start_lineno], offset=anchors_right_start_offset + ) - row.append('\n') + primary_char = anchors.primary_char + secondary_char = anchors.secondary_char + significant_lines.update( + range(anchors.left_end_lineno - 1, anchors.left_end_lineno + 2) + ) + significant_lines.update( + range(anchors.right_start_lineno - 1, anchors.right_start_lineno + 2) + ) + # remove bad line numbers + significant_lines.discard(-1) + significant_lines.discard(len(all_lines)) + + def output_line(lineno): + """output all_lines[lineno] along with carets""" + result.append(all_lines[lineno] + "\n") + if not show_carets: + return + num_spaces = len(all_lines[lineno]) - len(all_lines[lineno].lstrip()) + carets = [] + num_carets = dp_end_offset if lineno == len(all_lines) - 1 else _display_width(all_lines[lineno]) + # compute caret character for each position + for col in range(num_carets): + if col < num_spaces or (lineno == 0 and col < dp_start_offset): + # before first non-ws char of the line, or before start of instruction + carets.append(' ') + elif anchors and ( + lineno > anchors.left_end_lineno or + (lineno == anchors.left_end_lineno and col >= anchors_left_end_offset) + ) and ( + lineno < anchors.right_start_lineno or + (lineno == anchors.right_start_lineno and col < anchors_right_start_offset) + ): + # within anchors + carets.append(secondary_char) + else: + carets.append(primary_char) + result.append("".join(carets) + "\n") + + # display significant lines + sig_lines_list = sorted(significant_lines) + for i, lineno in enumerate(sig_lines_list): + if i: + linediff = lineno - sig_lines_list[i - 1] + if linediff == 2: + # 1 line in between - just output it + output_line(lineno - 1) + elif linediff > 2: + # > 1 line in between - abbreviate + result.append(f"...<{linediff - 1} lines>...\n") + output_line(lineno) + + row.append( + textwrap.indent(textwrap.dedent("".join(result)), ' ', lambda line: True) + ) if frame_summary.locals: for name, value in sorted(frame_summary.locals.items()): row.append(' {name} = {value}\n'.format(name=name, value=value)) @@ -599,7 +699,9 @@ def _byte_offset_to_character_offset(str, offset): _Anchors = collections.namedtuple( "_Anchors", [ + "left_end_lineno", "left_end_offset", + "right_start_lineno", "right_start_offset", "primary_char", "secondary_char", @@ -608,59 +710,161 @@ def _byte_offset_to_character_offset(str, offset): ) def _extract_caret_anchors_from_line_segment(segment): + """ + Given source code `segment` corresponding to a FrameSummary, determine: + - for binary ops, the location of the binary op + - for indexing and function calls, the location of the brackets. + `segment` is expected to be a valid Python expression. + """ import ast try: - tree = ast.parse(segment) + # Without parentheses, `segment` is parsed as a statement. + # Binary ops, subscripts, and calls are expressions, so + # we can wrap them with parentheses to parse them as + # (possibly multi-line) expressions. + # e.g. if we try to highlight the addition in + # x = ( + # a + + # b + # ) + # then we would ast.parse + # a + + # b + # which is not a valid statement because of the newline. + # Adding brackets makes it a valid expression. + # ( + # a + + # b + # ) + # Line locations will be different than the original, + # which is taken into account later on. + tree = ast.parse(f"(\n{segment}\n)") except SyntaxError: return None if len(tree.body) != 1: return None - normalize = lambda offset: _byte_offset_to_character_offset(segment, offset) + lines = segment.splitlines() + + def normalize(lineno, offset): + """Get character index given byte offset""" + return _byte_offset_to_character_offset(lines[lineno], offset) + + def next_valid_char(lineno, col): + """Gets the next valid character index in `lines`, if + the current location is not valid. Handles empty lines. + """ + while lineno < len(lines) and col >= len(lines[lineno]): + col = 0 + lineno += 1 + assert lineno < len(lines) and col < len(lines[lineno]) + return lineno, col + + def increment(lineno, col): + """Get the next valid character index in `lines`.""" + col += 1 + lineno, col = next_valid_char(lineno, col) + return lineno, col + + def nextline(lineno, col): + """Get the next valid character at least on the next line""" + col = 0 + lineno += 1 + lineno, col = next_valid_char(lineno, col) + return lineno, col + + def increment_until(lineno, col, stop): + """Get the next valid non-"\\#" character that satisfies the `stop` predicate""" + while True: + ch = lines[lineno][col] + if ch in "\\#": + lineno, col = nextline(lineno, col) + elif not stop(ch): + lineno, col = increment(lineno, col) + else: + break + return lineno, col + + def setup_positions(expr, force_valid=True): + """Get the lineno/col position of the end of `expr`. If `force_valid` is True, + forces the position to be a valid character (e.g. if the position is beyond the + end of the line, move to the next line) + """ + # -2 since end_lineno is 1-indexed and because we added an extra + # bracket + newline to `segment` when calling ast.parse + lineno = expr.end_lineno - 2 + col = normalize(lineno, expr.end_col_offset) + return next_valid_char(lineno, col) if force_valid else (lineno, col) + statement = tree.body[0] match statement: case ast.Expr(expr): match expr: case ast.BinOp(): - operator_start = normalize(expr.left.end_col_offset) - operator_end = normalize(expr.right.col_offset) - operator_str = segment[operator_start:operator_end] - operator_offset = len(operator_str) - len(operator_str.lstrip()) + # ast gives these locations for BinOp subexpressions + # ( left_expr ) + ( right_expr ) + # left^^^^^ right^^^^^ + lineno, col = setup_positions(expr.left) - left_anchor = expr.left.end_col_offset + operator_offset - right_anchor = left_anchor + 1 + # First operator character is the first non-space/')' character + lineno, col = increment_until(lineno, col, lambda x: not x.isspace() and x != ')') + + # binary op is 1 or 2 characters long, on the same line, + # before the right subexpression + right_col = col + 1 if ( - operator_offset + 1 < len(operator_str) - and not operator_str[operator_offset + 1].isspace() + right_col < len(lines[lineno]) + and ( + # operator char should not be in the right subexpression + expr.right.lineno - 2 > lineno or + right_col < normalize(expr.right.lineno - 2, expr.right.col_offset) + ) + and not (ch := lines[lineno][right_col]).isspace() + and ch not in "\\#" ): - right_anchor += 1 + right_col += 1 - while left_anchor < len(segment) and ((ch := segment[left_anchor]).isspace() or ch in ")#"): - left_anchor += 1 - right_anchor += 1 - return _Anchors(normalize(left_anchor), normalize(right_anchor)) + # right_col can be invalid since it is exclusive + return _Anchors(lineno, col, lineno, right_col) case ast.Subscript(): - left_anchor = normalize(expr.value.end_col_offset) - right_anchor = normalize(expr.slice.end_col_offset + 1) - while left_anchor < len(segment) and ((ch := segment[left_anchor]).isspace() or ch != "["): - left_anchor += 1 - while right_anchor < len(segment) and ((ch := segment[right_anchor]).isspace() or ch != "]"): - right_anchor += 1 - if right_anchor < len(segment): - right_anchor += 1 - return _Anchors(left_anchor, right_anchor) + # ast gives these locations for value and slice subexpressions + # ( value_expr ) [ slice_expr ] + # value^^^^^ slice^^^^^ + # subscript^^^^^^^^^^^^^^^^^^^^ + + # find left bracket + left_lineno, left_col = setup_positions(expr.value) + left_lineno, left_col = increment_until(left_lineno, left_col, lambda x: x == '[') + # find right bracket (final character of expression) + right_lineno, right_col = setup_positions(expr, force_valid=False) + return _Anchors(left_lineno, left_col, right_lineno, right_col) + case ast.Call(): + # ast gives these locations for function call expressions + # ( func_expr ) (args, kwargs) + # func^^^^^ + # call^^^^^^^^^^^^^^^^^^^^^^^^ + + # find left bracket + left_lineno, left_col = setup_positions(expr.func) + left_lineno, left_col = increment_until(left_lineno, left_col, lambda x: x == '(') + # find right bracket (final character of expression) + right_lineno, right_col = setup_positions(expr, force_valid=False) + return _Anchors(left_lineno, left_col, right_lineno, right_col) return None _WIDE_CHAR_SPECIFIERS = "WF" -def _display_width(line, offset): +def _display_width(line, offset=None): """Calculate the extra amount of width space the given source code segment might take if it were to be displayed on a fixed width output device. Supports wide unicode characters and emojis.""" + if offset is None: + offset = len(line) + # Fast track for ASCII-only strings if line.isascii(): return offset diff --git a/Misc/NEWS.d/next/Library/2023-11-15-01-36-04.gh-issue-106922.qslOVH.rst b/Misc/NEWS.d/next/Library/2023-11-15-01-36-04.gh-issue-106922.qslOVH.rst new file mode 100644 index 00000000000000..b68e75ab87cd0b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-15-01-36-04.gh-issue-106922.qslOVH.rst @@ -0,0 +1 @@ +Display multiple lines with ``traceback`` when errors span multiple lines. From a74daba7ca8b68f47284d82d4604721b8748bbde Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sat, 2 Dec 2023 16:13:44 +0300 Subject: [PATCH 135/228] gh-112316: Improve docs of `inspect.signature` and `Signature.from_callable` (#112317) Co-authored-by: Alex Waygood --- Doc/library/inspect.rst | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index b463c0b6d0e402..94c5d1c8979afd 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -620,7 +620,7 @@ function. .. function:: signature(callable, *, follow_wrapped=True, globals=None, locals=None, eval_str=False) - Return a :class:`Signature` object for the given ``callable``:: + Return a :class:`Signature` object for the given *callable*:: >>> from inspect import signature >>> def foo(a, *, b:int, **kwargs): @@ -646,29 +646,30 @@ function. For objects defined in modules using stringized annotations (``from __future__ import annotations``), :func:`signature` will attempt to automatically un-stringize the annotations using - :func:`inspect.get_annotations()`. The - ``global``, ``locals``, and ``eval_str`` parameters are passed - into :func:`inspect.get_annotations()` when resolving the - annotations; see the documentation for :func:`inspect.get_annotations()` + :func:`get_annotations`. The + *global*, *locals*, and *eval_str* parameters are passed + into :func:`get_annotations` when resolving the + annotations; see the documentation for :func:`get_annotations` for instructions on how to use these parameters. Raises :exc:`ValueError` if no signature can be provided, and :exc:`TypeError` if that type of object is not supported. Also, - if the annotations are stringized, and ``eval_str`` is not false, - the ``eval()`` call(s) to un-stringize the annotations could - potentially raise any kind of exception. + if the annotations are stringized, and *eval_str* is not false, + the ``eval()`` call(s) to un-stringize the annotations in :func:`get_annotations` + could potentially raise any kind of exception. A slash(/) in the signature of a function denotes that the parameters prior to it are positional-only. For more info, see :ref:`the FAQ entry on positional-only parameters `. - .. versionadded:: 3.5 - ``follow_wrapped`` parameter. Pass ``False`` to get a signature of - ``callable`` specifically (``callable.__wrapped__`` will not be used to + .. versionchanged:: 3.5 + The *follow_wrapped* parameter was added. + Pass ``False`` to get a signature of + *callable* specifically (``callable.__wrapped__`` will not be used to unwrap decorated callables.) - .. versionadded:: 3.10 - ``globals``, ``locals``, and ``eval_str`` parameters. + .. versionchanged:: 3.10 + The *globals*, *locals*, and *eval_str* parameters were added. .. note:: @@ -752,12 +753,10 @@ function. Signature objects are also supported by generic function :func:`copy.replace`. - .. classmethod:: Signature.from_callable(obj, *, follow_wrapped=True, globalns=None, localns=None) + .. classmethod:: Signature.from_callable(obj, *, follow_wrapped=True, globals=None, locals=None, eval_str=False) Return a :class:`Signature` (or its subclass) object for a given callable - ``obj``. Pass ``follow_wrapped=False`` to get a signature of ``obj`` - without unwrapping its ``__wrapped__`` chain. ``globalns`` and - ``localns`` will be used as the namespaces when resolving annotations. + *obj*. This method simplifies subclassing of :class:`Signature`:: @@ -770,8 +769,8 @@ function. .. versionadded:: 3.5 - .. versionadded:: 3.10 - ``globalns`` and ``localns`` parameters. + .. versionchanged:: 3.10 + The *globals*, *locals*, and *eval_str* parameters were added. .. class:: Parameter(name, kind, *, default=Parameter.empty, annotation=Parameter.empty) From a35a30509820f956d6feeaa0dbf42e9ca82c12bb Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sat, 2 Dec 2023 19:10:19 +0300 Subject: [PATCH 136/228] gh-112618: Make `Annotated` cache typed (#112619) Co-authored-by: Alex Waygood --- Lib/test/test_typing.py | 34 +++++++++++++++++++ Lib/typing.py | 11 +++--- ...-12-02-12-55-17.gh-issue-112618.7_FT8-.rst | 2 ++ 3 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-12-02-12-55-17.gh-issue-112618.7_FT8-.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 4fbb410f26ab8d..3572df7737f652 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -8675,6 +8675,40 @@ class X(Annotated[int, (1, 10)]): ... self.assertEqual(X.__mro__, (X, int, object), "Annotated should be transparent.") + def test_annotated_cached_with_types(self): + class A(str): ... + class B(str): ... + + field_a1 = Annotated[str, A("X")] + field_a2 = Annotated[str, B("X")] + a1_metadata = field_a1.__metadata__[0] + a2_metadata = field_a2.__metadata__[0] + + self.assertIs(type(a1_metadata), A) + self.assertEqual(a1_metadata, A("X")) + self.assertIs(type(a2_metadata), B) + self.assertEqual(a2_metadata, B("X")) + self.assertIsNot(type(a1_metadata), type(a2_metadata)) + + field_b1 = Annotated[str, A("Y")] + field_b2 = Annotated[str, B("Y")] + b1_metadata = field_b1.__metadata__[0] + b2_metadata = field_b2.__metadata__[0] + + self.assertIs(type(b1_metadata), A) + self.assertEqual(b1_metadata, A("Y")) + self.assertIs(type(b2_metadata), B) + self.assertEqual(b2_metadata, B("Y")) + self.assertIsNot(type(b1_metadata), type(b2_metadata)) + + field_c1 = Annotated[int, 1] + field_c2 = Annotated[int, 1.0] + field_c3 = Annotated[int, True] + + self.assertIs(type(field_c1.__metadata__[0]), int) + self.assertIs(type(field_c2.__metadata__[0]), float) + self.assertIs(type(field_c3.__metadata__[0]), bool) + class TypeAliasTests(BaseTestCase): def test_canonical_usage_with_variable_annotation(self): diff --git a/Lib/typing.py b/Lib/typing.py index b3af701f8d5437..4c19aadabe3b87 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -490,7 +490,7 @@ def __getitem__(self, parameters): return self._getitem(self, parameters) -class _LiteralSpecialForm(_SpecialForm, _root=True): +class _TypedCacheSpecialForm(_SpecialForm, _root=True): def __getitem__(self, parameters): if not isinstance(parameters, tuple): parameters = (parameters,) @@ -723,7 +723,7 @@ def Optional(self, parameters): arg = _type_check(parameters, f"{self} requires a single type.") return Union[arg, type(None)] -@_LiteralSpecialForm +@_TypedCacheSpecialForm @_tp_cache(typed=True) def Literal(self, *parameters): """Special typing form to define literal types (a.k.a. value types). @@ -2005,8 +2005,9 @@ def __mro_entries__(self, bases): return (self.__origin__,) -@_SpecialForm -def Annotated(self, params): +@_TypedCacheSpecialForm +@_tp_cache(typed=True) +def Annotated(self, *params): """Add context-specific metadata to a type. Example: Annotated[int, runtime_check.Unsigned] indicates to the @@ -2053,7 +2054,7 @@ def Annotated(self, params): where T1, T2 etc. are TypeVars, which would be invalid, because only one type should be passed to Annotated. """ - if not isinstance(params, tuple) or len(params) < 2: + if len(params) < 2: raise TypeError("Annotated[...] should be used " "with at least two arguments (a type and an " "annotation).") diff --git a/Misc/NEWS.d/next/Library/2023-12-02-12-55-17.gh-issue-112618.7_FT8-.rst b/Misc/NEWS.d/next/Library/2023-12-02-12-55-17.gh-issue-112618.7_FT8-.rst new file mode 100644 index 00000000000000..c732de15609c96 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-02-12-55-17.gh-issue-112618.7_FT8-.rst @@ -0,0 +1,2 @@ +Fix a caching bug relating to :data:`typing.Annotated`. +``Annotated[str, True]`` is no longer identical to ``Annotated[str, 1]``. From 0229d2a9b1d6ce6daa6a773f92e3754e7dc86d50 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 2 Dec 2023 19:41:40 +0200 Subject: [PATCH 137/228] Docs: Use sphinx-notfound-page to show a nicer 404 page (#111084) --- Doc/conf.py | 8 +++++++- Doc/requirements.txt | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Doc/conf.py b/Doc/conf.py index f1b411126c4e87..be2a86e12fa2e4 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -24,7 +24,13 @@ 'sphinx.ext.doctest', ] -# Skip if downstream redistributors haven't installed it +# Skip if downstream redistributors haven't installed them +try: + import notfound.extension +except ImportError: + pass +else: + extensions.append('notfound.extension') try: import sphinxext.opengraph except ImportError: diff --git a/Doc/requirements.txt b/Doc/requirements.txt index ce87be2d392824..04334fd5a464d4 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -13,6 +13,7 @@ blurb sphinx-autobuild sphinxext-opengraph==0.7.5 +sphinx-notfound-page==1.0.0 # The theme used by the documentation is stored separately, so we need # to install that as well. From a9574c68f04695eecd19866faaf4cdee5965bc70 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sun, 3 Dec 2023 02:39:43 +0300 Subject: [PATCH 138/228] gh-112139: Add `inspect.Signature.format` and use it in `pydoc` (#112143) Co-authored-by: Jelle Zijlstra --- Doc/library/inspect.rst | 11 +++ Lib/inspect.py | 12 +++ Lib/pydoc.py | 5 +- Lib/test/test_inspect/test_inspect.py | 96 +++++++++++++++++-- Lib/test/test_pydoc.py | 89 +++++++++++++++++ ...-11-16-10-42-15.gh-issue-112139.WpHosf.rst | 3 + 6 files changed, 205 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-16-10-42-15.gh-issue-112139.WpHosf.rst diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 94c5d1c8979afd..08522510f9ab44 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -753,6 +753,17 @@ function. Signature objects are also supported by generic function :func:`copy.replace`. + .. method:: format(*, max_width=None) + + Convert signature object to string. + + If *max_width* is passed, the method will attempt to fit + the signature into lines of at most *max_width* characters. + If the signature is longer than *max_width*, + all parameters will be on separate lines. + + .. versionadded:: 3.13 + .. classmethod:: Signature.from_callable(obj, *, follow_wrapped=True, globals=None, locals=None, eval_str=False) Return a :class:`Signature` (or its subclass) object for a given callable diff --git a/Lib/inspect.py b/Lib/inspect.py index aaa22bef896602..079385abbc7bb2 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -3316,6 +3316,16 @@ def __repr__(self): return '<{} {}>'.format(self.__class__.__name__, self) def __str__(self): + return self.format() + + def format(self, *, max_width=None): + """Convert signature object to string. + + If *max_width* integer is passed, + signature will try to fit into the *max_width*. + If signature is longer than *max_width*, + all parameters will be on separate lines. + """ result = [] render_pos_only_separator = False render_kw_only_separator = True @@ -3353,6 +3363,8 @@ def __str__(self): result.append('/') rendered = '({})'.format(', '.join(result)) + if max_width is not None and len(rendered) > max_width: + rendered = '(\n {}\n)'.format(',\n '.join(result)) if self.return_annotation is not _empty: anno = formatannotation(self.return_annotation) diff --git a/Lib/pydoc.py b/Lib/pydoc.py index be41592cc64bad..83c74a75cd1c00 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -201,7 +201,10 @@ def _getargspec(object): try: signature = inspect.signature(object) if signature: - return str(signature) + name = getattr(object, '__name__', '') + # function are always single-line and should not be formatted + max_width = (80 - len(name)) if name != '' else None + return signature.format(max_width=max_width) except (ValueError, TypeError): argspec = getattr(object, '__text_signature__', None) if argspec: diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index e75682f881ab34..09d50859970c99 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -3796,26 +3796,36 @@ def foo(a:int=1, *, b, c=None, **kwargs) -> 42: pass self.assertEqual(str(inspect.signature(foo)), '(a: int = 1, *, b, c=None, **kwargs) -> 42') + self.assertEqual(str(inspect.signature(foo)), + inspect.signature(foo).format()) def foo(a:int=1, *args, b, c=None, **kwargs) -> 42: pass self.assertEqual(str(inspect.signature(foo)), '(a: int = 1, *args, b, c=None, **kwargs) -> 42') + self.assertEqual(str(inspect.signature(foo)), + inspect.signature(foo).format()) def foo(): pass self.assertEqual(str(inspect.signature(foo)), '()') + self.assertEqual(str(inspect.signature(foo)), + inspect.signature(foo).format()) def foo(a: list[str]) -> tuple[str, float]: pass self.assertEqual(str(inspect.signature(foo)), '(a: list[str]) -> tuple[str, float]') + self.assertEqual(str(inspect.signature(foo)), + inspect.signature(foo).format()) from typing import Tuple def foo(a: list[str]) -> Tuple[str, float]: pass self.assertEqual(str(inspect.signature(foo)), '(a: list[str]) -> Tuple[str, float]') + self.assertEqual(str(inspect.signature(foo)), + inspect.signature(foo).format()) def test_signature_str_positional_only(self): P = inspect.Parameter @@ -3826,19 +3836,85 @@ def test(a_po, /, *, b, **kwargs): self.assertEqual(str(inspect.signature(test)), '(a_po, /, *, b, **kwargs)') + self.assertEqual(str(inspect.signature(test)), + inspect.signature(test).format()) + + test = S(parameters=[P('foo', P.POSITIONAL_ONLY)]) + self.assertEqual(str(test), '(foo, /)') + self.assertEqual(str(test), test.format()) - self.assertEqual(str(S(parameters=[P('foo', P.POSITIONAL_ONLY)])), - '(foo, /)') + test = S(parameters=[P('foo', P.POSITIONAL_ONLY), + P('bar', P.VAR_KEYWORD)]) + self.assertEqual(str(test), '(foo, /, **bar)') + self.assertEqual(str(test), test.format()) - self.assertEqual(str(S(parameters=[ - P('foo', P.POSITIONAL_ONLY), - P('bar', P.VAR_KEYWORD)])), - '(foo, /, **bar)') + test = S(parameters=[P('foo', P.POSITIONAL_ONLY), + P('bar', P.VAR_POSITIONAL)]) + self.assertEqual(str(test), '(foo, /, *bar)') + self.assertEqual(str(test), test.format()) - self.assertEqual(str(S(parameters=[ - P('foo', P.POSITIONAL_ONLY), - P('bar', P.VAR_POSITIONAL)])), - '(foo, /, *bar)') + def test_signature_format(self): + from typing import Annotated, Literal + + def func(x: Annotated[int, 'meta'], y: Literal['a', 'b'], z: 'LiteralString'): + pass + + expected_singleline = "(x: Annotated[int, 'meta'], y: Literal['a', 'b'], z: 'LiteralString')" + expected_multiline = """( + x: Annotated[int, 'meta'], + y: Literal['a', 'b'], + z: 'LiteralString' +)""" + self.assertEqual( + inspect.signature(func).format(), + expected_singleline, + ) + self.assertEqual( + inspect.signature(func).format(max_width=None), + expected_singleline, + ) + self.assertEqual( + inspect.signature(func).format(max_width=len(expected_singleline)), + expected_singleline, + ) + self.assertEqual( + inspect.signature(func).format(max_width=len(expected_singleline) - 1), + expected_multiline, + ) + self.assertEqual( + inspect.signature(func).format(max_width=0), + expected_multiline, + ) + self.assertEqual( + inspect.signature(func).format(max_width=-1), + expected_multiline, + ) + + def test_signature_format_all_arg_types(self): + from typing import Annotated, Literal + + def func( + x: Annotated[int, 'meta'], + /, + y: Literal['a', 'b'], + *, + z: 'LiteralString', + **kwargs: object, + ) -> None: + pass + + expected_multiline = """( + x: Annotated[int, 'meta'], + /, + y: Literal['a', 'b'], + *, + z: 'LiteralString', + **kwargs: object +) -> None""" + self.assertEqual( + inspect.signature(func).format(max_width=-1), + expected_multiline, + ) def test_signature_replace_parameters(self): def test(a, b) -> 42: diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 745717f492e07a..eb50510e12b7b6 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -870,6 +870,95 @@ class B(A) for expected_line in expected_lines: self.assertIn(expected_line, as_text) + def test_long_signatures(self): + from collections.abc import Callable + from typing import Literal, Annotated + + class A: + def __init__(self, + arg1: Callable[[int, int, int], str], + arg2: Literal['some value', 'other value'], + arg3: Annotated[int, 'some docs about this type'], + ) -> None: + ... + + doc = pydoc.render_doc(A) + # clean up the extra text formatting that pydoc performs + doc = re.sub('\b.', '', doc) + self.assertEqual(doc, '''Python Library Documentation: class A in module %s + +class A(builtins.object) + | A( + | arg1: collections.abc.Callable[[int, int, int], str], + | arg2: Literal['some value', 'other value'], + | arg3: Annotated[int, 'some docs about this type'] + | ) -> None + | + | Methods defined here: + | + | __init__( + | self, + | arg1: collections.abc.Callable[[int, int, int], str], + | arg2: Literal['some value', 'other value'], + | arg3: Annotated[int, 'some docs about this type'] + | ) -> None + | + | ---------------------------------------------------------------------- + | Data descriptors defined here: + | + | __dict__ + | dictionary for instance variables + | + | __weakref__ + | list of weak references to the object +''' % __name__) + + def func( + arg1: Callable[[Annotated[int, 'Some doc']], str], + arg2: Literal[1, 2, 3, 4, 5, 6, 7, 8], + ) -> Annotated[int, 'Some other']: + ... + + doc = pydoc.render_doc(func) + # clean up the extra text formatting that pydoc performs + doc = re.sub('\b.', '', doc) + self.assertEqual(doc, '''Python Library Documentation: function func in module %s + +func( + arg1: collections.abc.Callable[[typing.Annotated[int, 'Some doc']], str], + arg2: Literal[1, 2, 3, 4, 5, 6, 7, 8] +) -> Annotated[int, 'Some other'] +''' % __name__) + + def function_with_really_long_name_so_annotations_can_be_rather_small( + arg1: int, + arg2: str, + ): + ... + + doc = pydoc.render_doc(function_with_really_long_name_so_annotations_can_be_rather_small) + # clean up the extra text formatting that pydoc performs + doc = re.sub('\b.', '', doc) + self.assertEqual(doc, '''Python Library Documentation: function function_with_really_long_name_so_annotations_can_be_rather_small in module %s + +function_with_really_long_name_so_annotations_can_be_rather_small( + arg1: int, + arg2: str +) +''' % __name__) + + does_not_have_name = lambda \ + very_long_parameter_name_that_should_not_fit_into_a_single_line, \ + second_very_long_parameter_name: ... + + doc = pydoc.render_doc(does_not_have_name) + # clean up the extra text formatting that pydoc performs + doc = re.sub('\b.', '', doc) + self.assertEqual(doc, '''Python Library Documentation: function in module %s + + lambda very_long_parameter_name_that_should_not_fit_into_a_single_line, second_very_long_parameter_name +''' % __name__) + def test__future__imports(self): # __future__ features are excluded from module help, # except when it's the __future__ module itself diff --git a/Misc/NEWS.d/next/Library/2023-11-16-10-42-15.gh-issue-112139.WpHosf.rst b/Misc/NEWS.d/next/Library/2023-11-16-10-42-15.gh-issue-112139.WpHosf.rst new file mode 100644 index 00000000000000..090dc8847d9556 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-16-10-42-15.gh-issue-112139.WpHosf.rst @@ -0,0 +1,3 @@ +Add :meth:`Signature.format` to format signatures to string with extra options. +And use it in :mod:`pydoc` to render more readable signatures that have new +lines between parameters. From 3855b45874d5f8eb92a4957fb9de6fdce63eb760 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 3 Dec 2023 04:28:37 -0500 Subject: [PATCH 139/228] gh-66819: More IDLE htest updates(2) (#112642) Examine and update spec -- callable pairs. Revise run method. --- Lib/idlelib/browser.py | 1 + Lib/idlelib/debugobj.py | 4 +- Lib/idlelib/idle_test/htest.py | 108 ++++++++++++++++----------------- Lib/idlelib/iomenu.py | 9 +-- Lib/idlelib/multicall.py | 2 + Lib/idlelib/pathbrowser.py | 6 +- Lib/idlelib/query.py | 2 +- Lib/idlelib/sidebar.py | 6 +- Lib/idlelib/statusbar.py | 1 + 9 files changed, 70 insertions(+), 69 deletions(-) diff --git a/Lib/idlelib/browser.py b/Lib/idlelib/browser.py index 4fe64dced60aca..672e229ffbca94 100644 --- a/Lib/idlelib/browser.py +++ b/Lib/idlelib/browser.py @@ -254,5 +254,6 @@ class Nested_in_closure: pass if len(sys.argv) == 1: # If pass file on command line, unittest fails. from unittest import main main('idlelib.idle_test.test_browser', verbosity=2, exit=False) + from idlelib.idle_test.htest import run run(_module_browser) diff --git a/Lib/idlelib/debugobj.py b/Lib/idlelib/debugobj.py index 032b686f379378..0bf2cb1d5bbfe2 100644 --- a/Lib/idlelib/debugobj.py +++ b/Lib/idlelib/debugobj.py @@ -120,7 +120,7 @@ def make_objecttreeitem(labeltext, object, setfunction=None): return c(labeltext, object, setfunction) -def _object_browser(parent): # htest # +def _debug_object_browser(parent): # htest # import sys from tkinter import Toplevel top = Toplevel(parent) @@ -140,4 +140,4 @@ def _object_browser(parent): # htest # main('idlelib.idle_test.test_debugobj', verbosity=2, exit=False) from idlelib.idle_test.htest import run - run(_object_browser) + run(_debug_object_browser) diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index e21ab98d8aab89..a59b474fba47d8 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -8,14 +8,15 @@ In a tested module, let X be a global name bound to a callable (class or function) whose .__name__ attribute is also X (the usual situation). The -first parameter of X must be 'parent'. When called, the parent argument -will be the root window. X must create a child Toplevel(parent) window -(or subclass thereof). The Toplevel may be a test widget or dialog, in -which case the callable is the corresponding class. Or the Toplevel may -contain the widget to be tested or set up a context in which a test -widget is invoked. In this latter case, the callable is a wrapper -function that sets up the Toplevel and other objects. Wrapper function -names, such as _editor_window', should start with '_' and be lowercase. +first parameter of X must be 'parent' or 'master'. When called, the +first argument will be the root window. X must create a child +Toplevel(parent/master) (or subclass thereof). The Toplevel may be a +test widget or dialog, in which case the callable is the corresponding +class. Or the Toplevel may contain the widget to be tested or set up a +context in which a test widget is invoked. In this latter case, the +callable is a wrapper function that sets up the Toplevel and other +objects. Wrapper function names, such as _editor_window', should start +with '_' and be lowercase. End the module with @@ -117,12 +118,20 @@ 'file': 'query', 'kwds': {'title': 'Customize query.py Run', '_htest': True}, - 'msg': "Enter with or [Run]. Print valid entry to Shell\n" + 'msg': "Enter with or [OK]. Print valid entry to Shell\n" "Arguments are parsed into a list\n" "Mode is currently restart True or False\n" "Close dialog with valid entry, , [Cancel], [X]" } +_debug_object_browser_spec = { + 'file': 'debugobj', + 'kwds': {}, + 'msg': "Double click on items up to the lowest level.\n" + "Attributes of the objects and related information " + "will be displayed side-by-side at each level." + } + # TODO Improve message _dyn_option_menu_spec = { 'file': 'dynoption', @@ -178,7 +187,7 @@ "Any url ('www...', 'http...') is accepted.\n" "Test Browse with and without path, as cannot unittest.\n" "[Ok] or prints valid entry to shell\n" - "[Cancel] or prints None to shell" + ", [Cancel], or [X] prints None to shell" } _io_binding_spec = { @@ -199,17 +208,17 @@ 'kwds': {}, 'msg': textwrap.dedent("""\ 1. Click on the line numbers and drag down below the edge of the - window, moving the mouse a bit and then leaving it there for a while. - The text and line numbers should gradually scroll down, with the - selection updated continuously. + window, moving the mouse a bit and then leaving it there for a + while. The text and line numbers should gradually scroll down, + with the selection updated continuously. - 2. With the lines still selected, click on a line number above the - selected lines. Only the line whose number was clicked should be - selected. + 2. With the lines still selected, click on a line number above + or below the selected lines. Only the line whose number was + clicked should be selected. - 3. Repeat step #1, dragging to above the window. The text and line - numbers should gradually scroll up, with the selection updated - continuously. + 3. Repeat step #1, dragging to above the window. The text and + line numbers should gradually scroll up, with the selection + updated continuously. 4. Repeat step #2, clicking a line number below the selection."""), } @@ -217,42 +226,33 @@ _multi_call_spec = { 'file': 'multicall', 'kwds': {}, - 'msg': "The following actions should trigger a print to console or IDLE" - " Shell.\nEntering and leaving the text area, key entry, " - ",\n, , " - ", \n, and " - "focusing out of the window\nare sequences to be tested." + 'msg': "The following should trigger a print to console or IDLE Shell.\n" + "Entering and leaving the text area, key entry, ,\n" + ", , , \n" + ", and focusing elsewhere." } _module_browser_spec = { 'file': 'browser', 'kwds': {}, - 'msg': "Inspect names of module, class(with superclass if " - "applicable), methods and functions.\nToggle nested items.\n" - "Double clicking on items prints a traceback for an exception " - "that is ignored." + 'msg': textwrap.dedent(""" + "Inspect names of module, class(with superclass if applicable), + "methods and functions. Toggle nested items. Double clicking + "on items prints a traceback for an exception that is ignored.""") } _multistatus_bar_spec = { 'file': 'statusbar', 'kwds': {}, 'msg': "Ensure presence of multi-status bar below text area.\n" - "Click 'Update Status' to change the multi-status text" - } - -_object_browser_spec = { - 'file': 'debugobj', - 'kwds': {}, - 'msg': "Double click on items up to the lowest level.\n" - "Attributes of the objects and related information " - "will be displayed side-by-side at each level." + "Click 'Update Status' to change the status text" } -_path_browser_spec = { +PathBrowser_spec = { 'file': 'pathbrowser', - 'kwds': {}, + 'kwds': {'_htest': True}, 'msg': "Test for correct display of all paths in sys.path.\n" - "Toggle nested items up to the lowest level.\n" + "Toggle nested items out to the lowest level.\n" "Double clicking on an item prints a traceback\n" "for an exception that is ignored." } @@ -367,11 +367,12 @@ } def run(*tests): + "Run callables in tests." root = tk.Tk() root.title('IDLE htest') root.resizable(0, 0) - # a scrollable Label like constant width text widget. + # A scrollable Label-like constant width text widget. frameLabel = tk.Frame(root, padx=10) frameLabel.pack() text = tk.Text(frameLabel, wrap='word') @@ -381,45 +382,44 @@ def run(*tests): scrollbar.pack(side='right', fill='y', expand=False) text.pack(side='left', fill='both', expand=True) - test_list = [] # List of tuples of the form (spec, callable widget) + test_list = [] # Make list of (spec, callable) tuples. if tests: for test in tests: test_spec = globals()[test.__name__ + '_spec'] test_spec['name'] = test.__name__ test_list.append((test_spec, test)) else: - for k, d in globals().items(): - if k.endswith('_spec'): - test_name = k[:-5] - test_spec = d + for key, dic in globals().items(): + if key.endswith('_spec'): + test_name = key[:-5] + test_spec = dic test_spec['name'] = test_name mod = import_module('idlelib.' + test_spec['file']) test = getattr(mod, test_name) test_list.append((test_spec, test)) + test_list.reverse() # So can pop in proper order in next_test. test_name = tk.StringVar(root) callable_object = None test_kwds = None def next_test(): - nonlocal test_name, callable_object, test_kwds if len(test_list) == 1: next_button.pack_forget() test_spec, callable_object = test_list.pop() test_kwds = test_spec['kwds'] - test_kwds['parent'] = root test_name.set('Test ' + test_spec['name']) - text.configure(state='normal') # enable text editing - text.delete('1.0','end') - text.insert("1.0",test_spec['msg']) - text.configure(state='disabled') # preserve read-only property + text['state'] = 'normal' # Enable text replacement. + text.delete('1.0', 'end') + text.insert("1.0", test_spec['msg']) + text['state'] = 'disabled' # Restore read-only property. def run_test(_=None): - widget = callable_object(**test_kwds) + widget = callable_object(root, **test_kwds) try: - print(widget.result) + print(widget.result) # Only true for query classes(?). except AttributeError: pass diff --git a/Lib/idlelib/iomenu.py b/Lib/idlelib/iomenu.py index af8159c2b33f51..7629101635b8bb 100644 --- a/Lib/idlelib/iomenu.py +++ b/Lib/idlelib/iomenu.py @@ -396,10 +396,11 @@ def updaterecentfileslist(self,filename): def _io_binding(parent): # htest # from tkinter import Toplevel, Text - root = Toplevel(parent) - root.title("Test IOBinding") + top = Toplevel(parent) + top.title("Test IOBinding") x, y = map(int, parent.geometry().split('+')[1:]) - root.geometry("+%d+%d" % (x, y + 175)) + top.geometry("+%d+%d" % (x, y + 175)) + class MyEditWin: def __init__(self, text): self.text = text @@ -423,7 +424,7 @@ def saveas(self, event): def savecopy(self, event): self.text.event_generate("<>") - text = Text(root) + text = Text(top) text.pack() text.focus_set() editwin = MyEditWin(text) diff --git a/Lib/idlelib/multicall.py b/Lib/idlelib/multicall.py index 0200f445cc9340..2aa4a54125156f 100644 --- a/Lib/idlelib/multicall.py +++ b/Lib/idlelib/multicall.py @@ -421,6 +421,8 @@ def _multi_call(parent): # htest # top.geometry("+%d+%d" % (x, y + 175)) text = MultiCallCreator(tkinter.Text)(top) text.pack() + text.focus_set() + def bindseq(seq, n=[0]): def handler(event): print(seq) diff --git a/Lib/idlelib/pathbrowser.py b/Lib/idlelib/pathbrowser.py index 6de242d0000bed..48a77875ba5801 100644 --- a/Lib/idlelib/pathbrowser.py +++ b/Lib/idlelib/pathbrowser.py @@ -99,13 +99,9 @@ def listmodules(self, allnames): return sorted -def _path_browser(parent): # htest # - PathBrowser(parent, _htest=True) - parent.mainloop() - if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_pathbrowser', verbosity=2, exit=False) from idlelib.idle_test.htest import run - run(_path_browser) + run(PathBrowser) diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py index df02f2123ab02f..57230e2aaca66d 100644 --- a/Lib/idlelib/query.py +++ b/Lib/idlelib/query.py @@ -368,7 +368,7 @@ def create_extra(self): sticky='we') def cli_args_ok(self): - "Validity check and parsing for command line arguments." + "Return command line arg list or None if error." cli_string = self.entry.get().strip() try: cli_args = shlex.split(cli_string, posix=True) diff --git a/Lib/idlelib/sidebar.py b/Lib/idlelib/sidebar.py index 166c04342907f9..8e7eae5037c90c 100644 --- a/Lib/idlelib/sidebar.py +++ b/Lib/idlelib/sidebar.py @@ -516,13 +516,13 @@ def update_colors(self): def _linenumbers_drag_scrolling(parent): # htest # from idlelib.idle_test.test_sidebar import Dummy_editwin - toplevel = tk.Toplevel(parent) - text_frame = tk.Frame(toplevel) + top = tk.Toplevel(parent) + text_frame = tk.Frame(top) text_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) text_frame.rowconfigure(1, weight=1) text_frame.columnconfigure(1, weight=1) - font = idleConf.GetFont(toplevel, 'main', 'EditorWindow') + font = idleConf.GetFont(top, 'main', 'EditorWindow') text = tk.Text(text_frame, width=80, height=24, wrap=tk.NONE, font=font) text.grid(row=1, column=1, sticky=tk.NSEW) diff --git a/Lib/idlelib/statusbar.py b/Lib/idlelib/statusbar.py index 755fafb0ac6438..7048bd64b98753 100644 --- a/Lib/idlelib/statusbar.py +++ b/Lib/idlelib/statusbar.py @@ -26,6 +26,7 @@ def _multistatus_bar(parent): # htest # x, y = map(int, parent.geometry().split('+')[1:]) top.geometry("+%d+%d" %(x, y + 175)) top.title("Test multistatus bar") + frame = Frame(top) text = Text(frame, height=5, width=40) text.pack() From fc9e24b01fb7da4160b82cef26981d72bb678c13 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sun, 3 Dec 2023 09:37:34 +0000 Subject: [PATCH 140/228] gh-112316: improve docs for `inspect.signature` and `inspect.Signature` (#112631) --- Doc/library/inspect.rst | 91 +++++++++++++++++++++++++---------------- Lib/inspect.py | 2 +- 2 files changed, 56 insertions(+), 37 deletions(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 08522510f9ab44..71e7cb433cb1a8 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -1,6 +1,11 @@ :mod:`inspect` --- Inspect live objects ======================================= +.. testsetup:: * + + import inspect + from inspect import * + .. module:: inspect :synopsis: Extract information and source code from live objects. @@ -614,13 +619,16 @@ Introspecting callables with the Signature object .. versionadded:: 3.3 -The Signature object represents the call signature of a callable object and its -return annotation. To retrieve a Signature object, use the :func:`signature` +The :class:`Signature` object represents the call signature of a callable object +and its return annotation. To retrieve a :class:`!Signature` object, +use the :func:`!signature` function. .. function:: signature(callable, *, follow_wrapped=True, globals=None, locals=None, eval_str=False) - Return a :class:`Signature` object for the given *callable*:: + Return a :class:`Signature` object for the given *callable*: + + .. doctest:: >>> from inspect import signature >>> def foo(a, *, b:int, **kwargs): @@ -629,10 +637,10 @@ function. >>> sig = signature(foo) >>> str(sig) - '(a, *, b:int, **kwargs)' + '(a, *, b: int, **kwargs)' >>> str(sig.parameters['b']) - 'b:int' + 'b: int' >>> sig.parameters['b'].annotation @@ -647,7 +655,7 @@ function. (``from __future__ import annotations``), :func:`signature` will attempt to automatically un-stringize the annotations using :func:`get_annotations`. The - *global*, *locals*, and *eval_str* parameters are passed + *globals*, *locals*, and *eval_str* parameters are passed into :func:`get_annotations` when resolving the annotations; see the documentation for :func:`get_annotations` for instructions on how to use these parameters. @@ -680,7 +688,8 @@ function. .. class:: Signature(parameters=None, *, return_annotation=Signature.empty) - A Signature object represents the call signature of a function and its return + A :class:`!Signature` object represents the call signature of a function + and its return annotation. For each parameter accepted by the function it stores a :class:`Parameter` object in its :attr:`parameters` collection. @@ -690,14 +699,14 @@ function. positional-only first, then positional-or-keyword, and that parameters with defaults follow parameters without defaults. - The optional *return_annotation* argument, can be an arbitrary Python object, - is the "return" annotation of the callable. + The optional *return_annotation* argument can be an arbitrary Python object. + It represents the "return" annotation of the callable. - Signature objects are *immutable*. Use :meth:`Signature.replace` or + :class:`!Signature` objects are *immutable*. Use :meth:`Signature.replace` or :func:`copy.replace` to make a modified copy. .. versionchanged:: 3.5 - Signature objects are picklable and :term:`hashable`. + :class:`!Signature` objects are now picklable and :term:`hashable`. .. attribute:: Signature.empty @@ -734,13 +743,15 @@ function. .. method:: Signature.replace(*[, parameters][, return_annotation]) - Create a new Signature instance based on the instance :meth:`replace` was invoked - on. It is possible to pass different ``parameters`` and/or - ``return_annotation`` to override the corresponding properties of the base - signature. To remove return_annotation from the copied Signature, pass in + Create a new :class:`Signature` instance based on the instance + :meth:`replace` was invoked on. + It is possible to pass different *parameters* and/or + *return_annotation* to override the corresponding properties of the base + signature. To remove ``return_annotation`` from the copied + :class:`!Signature`, pass in :attr:`Signature.empty`. - :: + .. doctest:: >>> def test(a, b): ... pass @@ -750,12 +761,12 @@ function. >>> str(new_sig) "(a, b) -> 'new return anno'" - Signature objects are also supported by generic function + :class:`Signature` objects are also supported by the generic function :func:`copy.replace`. .. method:: format(*, max_width=None) - Convert signature object to string. + Create a string representation of the :class:`Signature` object. If *max_width* is passed, the method will attempt to fit the signature into lines of at most *max_width* characters. @@ -769,12 +780,14 @@ function. Return a :class:`Signature` (or its subclass) object for a given callable *obj*. - This method simplifies subclassing of :class:`Signature`:: + This method simplifies subclassing of :class:`Signature`: + + .. testcode:: - class MySignature(Signature): - pass - sig = MySignature.from_callable(min) - assert isinstance(sig, MySignature) + class MySignature(Signature): + pass + sig = MySignature.from_callable(sum) + assert isinstance(sig, MySignature) Its behavior is otherwise identical to that of :func:`signature`. @@ -786,11 +799,12 @@ function. .. class:: Parameter(name, kind, *, default=Parameter.empty, annotation=Parameter.empty) - Parameter objects are *immutable*. Instead of modifying a Parameter object, + :class:`!Parameter` objects are *immutable*. + Instead of modifying a :class:`!Parameter` object, you can use :meth:`Parameter.replace` or :func:`copy.replace` to create a modified copy. .. versionchanged:: 3.5 - Parameter objects are picklable and :term:`hashable`. + Parameter objects are now picklable and :term:`hashable`. .. attribute:: Parameter.empty @@ -809,7 +823,7 @@ function. expressions. .. versionchanged:: 3.6 - These parameter names are exposed by this module as names like + These parameter names are now exposed by this module as names like ``implicit0``. .. attribute:: Parameter.default @@ -859,7 +873,9 @@ function. | | definition. | +------------------------+----------------------------------------------+ - Example: print all keyword-only arguments without default values:: + Example: print all keyword-only arguments without default values: + + .. doctest:: >>> def foo(a, b, *, c, d=10): ... pass @@ -873,11 +889,13 @@ function. .. attribute:: Parameter.kind.description - Describes a enum value of Parameter.kind. + Describes a enum value of :attr:`Parameter.kind`. .. versionadded:: 3.8 - Example: print all descriptions of arguments:: + Example: print all descriptions of arguments: + + .. doctest:: >>> def foo(a, b, *, c, d=10): ... pass @@ -892,12 +910,12 @@ function. .. method:: Parameter.replace(*[, name][, kind][, default][, annotation]) - Create a new Parameter instance based on the instance replaced was invoked - on. To override a :class:`Parameter` attribute, pass the corresponding + Create a new :class:`Parameter` instance based on the instance replaced was invoked + on. To override a :class:`!Parameter` attribute, pass the corresponding argument. To remove a default value or/and an annotation from a - Parameter, pass :attr:`Parameter.empty`. + :class:`!Parameter`, pass :attr:`Parameter.empty`. - :: + .. doctest:: >>> from inspect import Parameter >>> param = Parameter('foo', Parameter.KEYWORD_ONLY, default=42) @@ -908,12 +926,13 @@ function. 'foo=42' >>> str(param.replace(default=Parameter.empty, annotation='spam')) - "foo:'spam'" + "foo: 'spam'" - Parameter objects are also supported by generic function :func:`copy.replace`. + :class:`Parameter` objects are also supported by the generic function + :func:`copy.replace`. .. versionchanged:: 3.4 - In Python 3.3 Parameter objects were allowed to have ``name`` set + In Python 3.3 :class:`Parameter` objects were allowed to have ``name`` set to ``None`` if their ``kind`` was set to ``POSITIONAL_ONLY``. This is no longer permitted. diff --git a/Lib/inspect.py b/Lib/inspect.py index 079385abbc7bb2..f0b72662a9a0b2 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -3319,7 +3319,7 @@ def __str__(self): return self.format() def format(self, *, max_width=None): - """Convert signature object to string. + """Create a string representation of the Signature object. If *max_width* integer is passed, signature will try to fit into the *max_width*. From 29e6c7b68acac628b084a82670708008be262379 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 3 Dec 2023 03:09:29 -0800 Subject: [PATCH 141/228] gh-112578: Fix RuntimeWarning when running zipfile (GH-112579) --- Lib/zipfile/__init__.py | 73 +++++++++++++++++- Lib/zipfile/__main__.py | 75 +------------------ ...-12-01-08-28-09.gh-issue-112578.bfNbfi.rst | 1 + 3 files changed, 72 insertions(+), 77 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-12-01-08-28-09.gh-issue-112578.bfNbfi.rst diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py index 2b28a079dbaa95..fe629ed1cf2fc5 100644 --- a/Lib/zipfile/__init__.py +++ b/Lib/zipfile/__init__.py @@ -2227,12 +2227,79 @@ def _compile(file, optimize=-1): return (fname, archivename) +def main(args=None): + import argparse + + description = 'A simple command-line interface for zipfile module.' + parser = argparse.ArgumentParser(description=description) + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-l', '--list', metavar='', + help='Show listing of a zipfile') + group.add_argument('-e', '--extract', nargs=2, + metavar=('', ''), + help='Extract zipfile into target dir') + group.add_argument('-c', '--create', nargs='+', + metavar=('', ''), + help='Create zipfile from sources') + group.add_argument('-t', '--test', metavar='', + help='Test if a zipfile is valid') + parser.add_argument('--metadata-encoding', metavar='', + help='Specify encoding of member names for -l, -e and -t') + args = parser.parse_args(args) + + encoding = args.metadata_encoding + + if args.test is not None: + src = args.test + with ZipFile(src, 'r', metadata_encoding=encoding) as zf: + badfile = zf.testzip() + if badfile: + print("The following enclosed file is corrupted: {!r}".format(badfile)) + print("Done testing") + + elif args.list is not None: + src = args.list + with ZipFile(src, 'r', metadata_encoding=encoding) as zf: + zf.printdir() + + elif args.extract is not None: + src, curdir = args.extract + with ZipFile(src, 'r', metadata_encoding=encoding) as zf: + zf.extractall(curdir) + + elif args.create is not None: + if encoding: + print("Non-conforming encodings not supported with -c.", + file=sys.stderr) + sys.exit(1) + + zip_name = args.create.pop(0) + files = args.create + + def addToZip(zf, path, zippath): + if os.path.isfile(path): + zf.write(path, zippath, ZIP_DEFLATED) + elif os.path.isdir(path): + if zippath: + zf.write(path, zippath) + for nm in sorted(os.listdir(path)): + addToZip(zf, + os.path.join(path, nm), os.path.join(zippath, nm)) + # else: ignore + + with ZipFile(zip_name, 'w') as zf: + for path in files: + zippath = os.path.basename(path) + if not zippath: + zippath = os.path.basename(os.path.dirname(path)) + if zippath in ('', os.curdir, os.pardir): + zippath = '' + addToZip(zf, path, zippath) + + from ._path import ( # noqa: E402 Path, # used privately for tests CompleteDirs, # noqa: F401 ) - -# used privately for tests -from .__main__ import main # noqa: F401, E402 diff --git a/Lib/zipfile/__main__.py b/Lib/zipfile/__main__.py index a9e5fb1b8d72c4..868d99efc3c4a3 100644 --- a/Lib/zipfile/__main__.py +++ b/Lib/zipfile/__main__.py @@ -1,77 +1,4 @@ -import sys -import os -from . import ZipFile, ZIP_DEFLATED - - -def main(args=None): - import argparse - - description = 'A simple command-line interface for zipfile module.' - parser = argparse.ArgumentParser(description=description) - group = parser.add_mutually_exclusive_group(required=True) - group.add_argument('-l', '--list', metavar='', - help='Show listing of a zipfile') - group.add_argument('-e', '--extract', nargs=2, - metavar=('', ''), - help='Extract zipfile into target dir') - group.add_argument('-c', '--create', nargs='+', - metavar=('', ''), - help='Create zipfile from sources') - group.add_argument('-t', '--test', metavar='', - help='Test if a zipfile is valid') - parser.add_argument('--metadata-encoding', metavar='', - help='Specify encoding of member names for -l, -e and -t') - args = parser.parse_args(args) - - encoding = args.metadata_encoding - - if args.test is not None: - src = args.test - with ZipFile(src, 'r', metadata_encoding=encoding) as zf: - badfile = zf.testzip() - if badfile: - print("The following enclosed file is corrupted: {!r}".format(badfile)) - print("Done testing") - - elif args.list is not None: - src = args.list - with ZipFile(src, 'r', metadata_encoding=encoding) as zf: - zf.printdir() - - elif args.extract is not None: - src, curdir = args.extract - with ZipFile(src, 'r', metadata_encoding=encoding) as zf: - zf.extractall(curdir) - - elif args.create is not None: - if encoding: - print("Non-conforming encodings not supported with -c.", - file=sys.stderr) - sys.exit(1) - - zip_name = args.create.pop(0) - files = args.create - - def addToZip(zf, path, zippath): - if os.path.isfile(path): - zf.write(path, zippath, ZIP_DEFLATED) - elif os.path.isdir(path): - if zippath: - zf.write(path, zippath) - for nm in sorted(os.listdir(path)): - addToZip(zf, - os.path.join(path, nm), os.path.join(zippath, nm)) - # else: ignore - - with ZipFile(zip_name, 'w') as zf: - for path in files: - zippath = os.path.basename(path) - if not zippath: - zippath = os.path.basename(os.path.dirname(path)) - if zippath in ('', os.curdir, os.pardir): - zippath = '' - addToZip(zf, path, zippath) - +from . import main if __name__ == "__main__": main() diff --git a/Misc/NEWS.d/next/Library/2023-12-01-08-28-09.gh-issue-112578.bfNbfi.rst b/Misc/NEWS.d/next/Library/2023-12-01-08-28-09.gh-issue-112578.bfNbfi.rst new file mode 100644 index 00000000000000..1de5b1fe26ce6d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-01-08-28-09.gh-issue-112578.bfNbfi.rst @@ -0,0 +1 @@ +Fix a spurious :exc:`RuntimeWarning` when executing the :mod:`zipfile` module. From 1f2a676785d48ed9ac01e60cc56a82e44b725474 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 3 Dec 2023 12:16:31 +0100 Subject: [PATCH 142/228] gh-106560: Fix redundant declarations in Include/ (#112611) Don't declare PyBool_Type, PyLong_Type and PySys_Audit() twice, but only once. Compiler warnings seen by building Python with gcc -Wredundant-decls. --- Include/boolobject.h | 2 +- Include/cpython/sysmodule.h | 4 ---- Include/longobject.h | 2 +- .../next/C API/2023-12-02-02-08-11.gh-issue-106560.THvuji.rst | 2 ++ 4 files changed, 4 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2023-12-02-02-08-11.gh-issue-106560.THvuji.rst diff --git a/Include/boolobject.h b/Include/boolobject.h index 976fa35201d035..19aef5b1b87c6a 100644 --- a/Include/boolobject.h +++ b/Include/boolobject.h @@ -7,7 +7,7 @@ extern "C" { #endif -PyAPI_DATA(PyTypeObject) PyBool_Type; +// PyBool_Type is declared by object.h #define PyBool_Check(x) Py_IS_TYPE((x), &PyBool_Type) diff --git a/Include/cpython/sysmodule.h b/Include/cpython/sysmodule.h index 9fd7cc0cb43931..a3ac07f538a94f 100644 --- a/Include/cpython/sysmodule.h +++ b/Include/cpython/sysmodule.h @@ -4,10 +4,6 @@ typedef int(*Py_AuditHookFunction)(const char *, PyObject *, void *); -PyAPI_FUNC(int) PySys_Audit( - const char *event, - const char *format, - ...); PyAPI_FUNC(int) PySys_AddAuditHook(Py_AuditHookFunction, void*); typedef struct { diff --git a/Include/longobject.h b/Include/longobject.h index 7393254cd24a9b..51005efff636fa 100644 --- a/Include/longobject.h +++ b/Include/longobject.h @@ -7,7 +7,7 @@ extern "C" { /* Long (arbitrary precision) integer object interface */ -PyAPI_DATA(PyTypeObject) PyLong_Type; +// PyLong_Type is declared by object.h #define PyLong_Check(op) \ PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_LONG_SUBCLASS) diff --git a/Misc/NEWS.d/next/C API/2023-12-02-02-08-11.gh-issue-106560.THvuji.rst b/Misc/NEWS.d/next/C API/2023-12-02-02-08-11.gh-issue-106560.THvuji.rst new file mode 100644 index 00000000000000..59b461ec47ad64 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-12-02-02-08-11.gh-issue-106560.THvuji.rst @@ -0,0 +1,2 @@ +Fix redundant declarations in the public C API. Declare PyBool_Type, +PyLong_Type and PySys_Audit() only once. Patch by Victor Stinner. From d9e444dbb86e173ee5b8491e3facbd447b91eaed Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 3 Dec 2023 12:18:24 +0100 Subject: [PATCH 143/228] gh-106560: Fix redundant declarations in Python/frozen.c (#112612) Avoid duplicated declarations of "extern" functions in Python/frozen.c. Compiler warnings seen by building Python with gcc -Wredundant-decls. --- Python/frozen.c | 6 ------ Tools/build/freeze_modules.py | 13 ++++++++++++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Python/frozen.c b/Python/frozen.c index 0fb38a11902f35..77f51a7f750965 100644 --- a/Python/frozen.c +++ b/Python/frozen.c @@ -80,7 +80,6 @@ extern PyObject *_Py_get__sitebuiltins_toplevel(void); extern PyObject *_Py_get_genericpath_toplevel(void); extern PyObject *_Py_get_ntpath_toplevel(void); extern PyObject *_Py_get_posixpath_toplevel(void); -extern PyObject *_Py_get_posixpath_toplevel(void); extern PyObject *_Py_get_os_toplevel(void); extern PyObject *_Py_get_site_toplevel(void); extern PyObject *_Py_get_stat_toplevel(void); @@ -88,13 +87,8 @@ extern PyObject *_Py_get_importlib_util_toplevel(void); extern PyObject *_Py_get_importlib_machinery_toplevel(void); extern PyObject *_Py_get_runpy_toplevel(void); extern PyObject *_Py_get___hello___toplevel(void); -extern PyObject *_Py_get___hello___toplevel(void); -extern PyObject *_Py_get___hello___toplevel(void); -extern PyObject *_Py_get___hello___toplevel(void); -extern PyObject *_Py_get___phello___toplevel(void); extern PyObject *_Py_get___phello___toplevel(void); extern PyObject *_Py_get___phello___ham_toplevel(void); -extern PyObject *_Py_get___phello___ham_toplevel(void); extern PyObject *_Py_get___phello___ham_eggs_toplevel(void); extern PyObject *_Py_get___phello___spam_toplevel(void); extern PyObject *_Py_get_frozen_only_toplevel(void); diff --git a/Tools/build/freeze_modules.py b/Tools/build/freeze_modules.py index c5a397129201b6..6a54f45bac3a86 100644 --- a/Tools/build/freeze_modules.py +++ b/Tools/build/freeze_modules.py @@ -468,6 +468,17 @@ def replace_block(lines, start_marker, end_marker, replacements, file): return lines[:start_pos + 1] + replacements + lines[end_pos:] +class UniqueList(list): + def __init__(self): + self._seen = set() + + def append(self, item): + if item in self._seen: + return + super().append(item) + self._seen.add(item) + + def regen_frozen(modules): headerlines = [] parentdir = os.path.dirname(FROZEN_FILE) @@ -477,7 +488,7 @@ def regen_frozen(modules): header = relpath_for_posix_display(src.frozenfile, parentdir) headerlines.append(f'#include "{header}"') - externlines = [] + externlines = UniqueList() bootstraplines = [] stdliblines = [] testlines = [] From a971574b73140b6badf9442a5b954eab766a082c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 3 Dec 2023 12:21:48 +0100 Subject: [PATCH 144/228] gh-111545: Mention PEP 456 in PyHash_GetFuncDef() doc (#112647) --- Doc/c-api/hash.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/c-api/hash.rst b/Doc/c-api/hash.rst index 4dc121d7fbaa9b..3bfaf8b9f54c14 100644 --- a/Doc/c-api/hash.rst +++ b/Doc/c-api/hash.rst @@ -45,4 +45,7 @@ See also the :c:member:`PyTypeObject.tp_hash` member. Get the hash function definition. + .. seealso:: + :pep:`456` "Secure and interchangeable hash algorithm". + .. versionadded:: 3.4 From 4ed46d224401243399b41c7ceef4532bd249da27 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sun, 3 Dec 2023 11:50:22 +0000 Subject: [PATCH 145/228] Run more `inspect.rst` code snippets in CI (#112654) --- Doc/library/inspect.rst | 66 ++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 71e7cb433cb1a8..815bd54107a987 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -392,7 +392,11 @@ attributes (see :ref:`import-mod-attrs` for module attributes): Return ``True`` if the object can be used in :keyword:`await` expression. Can also be used to distinguish generator-based coroutines from regular - generators:: + generators: + + .. testcode:: + + import types def gen(): yield @@ -409,13 +413,15 @@ attributes (see :ref:`import-mod-attrs` for module attributes): .. function:: isasyncgenfunction(object) Return ``True`` if the object is an :term:`asynchronous generator` function, - for example:: + for example: - >>> async def agen(): - ... yield 1 - ... - >>> inspect.isasyncgenfunction(agen) - True + .. doctest:: + + >>> async def agen(): + ... yield 1 + ... + >>> inspect.isasyncgenfunction(agen) + True .. versionadded:: 3.6 @@ -985,18 +991,20 @@ function. For variable-keyword arguments (``**kwargs``) the default is an empty dict. - :: + .. doctest:: - >>> def foo(a, b='ham', *args): pass - >>> ba = inspect.signature(foo).bind('spam') - >>> ba.apply_defaults() - >>> ba.arguments - {'a': 'spam', 'b': 'ham', 'args': ()} + >>> def foo(a, b='ham', *args): pass + >>> ba = inspect.signature(foo).bind('spam') + >>> ba.apply_defaults() + >>> ba.arguments + {'a': 'spam', 'b': 'ham', 'args': ()} .. versionadded:: 3.5 The :attr:`args` and :attr:`kwargs` properties can be used to invoke - functions:: + functions: + + .. testcode:: def test(a, *, b): ... @@ -1115,20 +1123,22 @@ Classes and functions ``**`` arguments, if any) to their values from *args* and *kwds*. In case of invoking *func* incorrectly, i.e. whenever ``func(*args, **kwds)`` would raise an exception because of incompatible signature, an exception of the same type - and the same or similar message is raised. For example:: - - >>> from inspect import getcallargs - >>> def f(a, b=1, *pos, **named): - ... pass - ... - >>> getcallargs(f, 1, 2, 3) == {'a': 1, 'named': {}, 'b': 2, 'pos': (3,)} - True - >>> getcallargs(f, a=2, x=4) == {'a': 2, 'named': {'x': 4}, 'b': 1, 'pos': ()} - True - >>> getcallargs(f) - Traceback (most recent call last): - ... - TypeError: f() missing 1 required positional argument: 'a' + and the same or similar message is raised. For example: + + .. doctest:: + + >>> from inspect import getcallargs + >>> def f(a, b=1, *pos, **named): + ... pass + ... + >>> getcallargs(f, 1, 2, 3) == {'a': 1, 'named': {}, 'b': 2, 'pos': (3,)} + True + >>> getcallargs(f, a=2, x=4) == {'a': 2, 'named': {'x': 4}, 'b': 1, 'pos': ()} + True + >>> getcallargs(f) + Traceback (most recent call last): + ... + TypeError: f() missing 1 required positional argument: 'a' .. versionadded:: 3.2 From 162d3d428a836850ba29c58bbf37c931843d9e37 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Sun, 3 Dec 2023 12:12:49 +0000 Subject: [PATCH 146/228] gh-112620: Fix dis error on show_cache with labels (#112621) --- Lib/dis.py | 17 +++++++++++------ Lib/test/test_dis.py | 12 ++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Lib/dis.py b/Lib/dis.py index e08e9a94057689..8d3885d2526b70 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -412,7 +412,6 @@ def _create(cls, op, arg, offset, start_offset, starts_line, line_number, co_consts=None, varname_from_oparg=None, names=None, labels_map=None, exceptions_map=None): - label_width = 4 + len(str(len(labels_map))) argval, argrepr = cls._get_argval_argrepr( op, arg, offset, co_consts, names, varname_from_oparg, labels_map) @@ -420,7 +419,7 @@ def _create(cls, op, arg, offset, start_offset, starts_line, line_number, instr = Instruction(_all_opname[op], op, arg, argval, argrepr, offset, start_offset, starts_line, line_number, label, positions) - instr.label_width = label_width + instr.label_width = 4 + len(str(len(labels_map))) instr.exc_handler = exceptions_map.get(offset, None) return instr @@ -468,12 +467,14 @@ def is_jump_target(self): """True if other code jumps to here, otherwise False""" return self.label is not None - def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=0): + def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=0, + label_width=0): """Format instruction details for inclusion in disassembly output. *lineno_width* sets the width of the line number field (0 omits it) *mark_as_current* inserts a '-->' marker arrow as part of the line *offset_width* sets the width of the instruction offset field + *label_width* sets the width of the label field """ fields = [] # Column: Source code line number @@ -488,9 +489,9 @@ def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=0): # Column: Label if self.label is not None: lbl = f"L{self.label}:" - fields.append(f"{lbl:>{self.label_width}}") + fields.append(f"{lbl:>{label_width}}") else: - fields.append(' ' * self.label_width) + fields.append(' ' * label_width) # Column: Instruction offset from start of code sequence if offset_width > 0: fields.append(f"{repr(self.offset):>{offset_width}} ") @@ -648,6 +649,7 @@ def make_labels_map(original_code, exception_entries): return labels_map labels_map = make_labels_map(original_code, exception_entries) + label_width = 4 + len(str(len(labels_map))) exceptions_map = {} for start, end, target, _, _ in exception_entries: @@ -756,6 +758,7 @@ def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None, else: offset_width = 0 + label_width = -1 for instr in _get_instructions_bytes(code, varname_from_oparg, names, co_consts, linestarts, line_offset=line_offset, @@ -774,7 +777,9 @@ def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None, # Each CACHE takes 2 bytes is_current_instr = instr.offset <= lasti \ <= instr.offset + 2 * _get_cache_size(_all_opname[_deoptop(instr.opcode)]) - print(instr._disassemble(lineno_width, is_current_instr, offset_width), + label_width = getattr(instr, 'label_width', label_width) + assert label_width >= 0 + print(instr._disassemble(lineno_width, is_current_instr, offset_width, label_width), file=file) if exception_entries: print("ExceptionTable:", file=file) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 805cd4e4c30965..349790ecd7d075 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -1896,6 +1896,18 @@ def test_oparg_alias(self): positions=None) self.assertEqual(instruction.arg, instruction.oparg) + def test_show_caches_with_label(self): + def f(x, y, z): + if x: + res = y + else: + res = z + return res + + output = io.StringIO() + dis.dis(f.__code__, file=output, show_caches=True) + self.assertIn("L1:", output.getvalue()) + def test_baseopname_and_baseopcode(self): # Standard instructions for name, code in dis.opmap.items(): From 97857ac0580057c3a4f75d34209841c81ee11a96 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Sun, 3 Dec 2023 14:02:37 +0000 Subject: [PATCH 147/228] gh-112645: remove deprecation warning for use of onerror in shutil.rmtree (#112659) --- Doc/whatsnew/3.12.rst | 7 +++---- Lib/shutil.py | 5 ----- Lib/test/test_shutil.py | 15 +++++---------- ...2023-12-03-12-41-48.gh-issue-112645.blMsKf.rst | 1 + 4 files changed, 9 insertions(+), 19 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-12-03-12-41-48.gh-issue-112645.blMsKf.rst diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 96893527cc91ed..fc17c86665335c 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -838,8 +838,7 @@ shutil * :func:`shutil.rmtree` now accepts a new argument *onexc* which is an error handler like *onerror* but which expects an exception instance - rather than a *(typ, val, tb)* triplet. *onerror* is deprecated and - will be removed in Python 3.14. + rather than a *(typ, val, tb)* triplet. *onerror* is deprecated. (Contributed by Irit Katriel in :gh:`102828`.) * :func:`shutil.which` now consults the *PATHEXT* environment variable to @@ -1261,8 +1260,8 @@ Deprecated :mod:`concurrent.futures` the fix is to use a different :mod:`multiprocessing` start method such as ``"spawn"`` or ``"forkserver"``. -* :mod:`shutil`: The *onerror* argument of :func:`shutil.rmtree` is deprecated and will be removed - in Python 3.14. Use *onexc* instead. (Contributed by Irit Katriel in :gh:`102828`.) +* :mod:`shutil`: The *onerror* argument of :func:`shutil.rmtree` is deprecated; + use *onexc* instead. (Contributed by Irit Katriel in :gh:`102828`.) * :mod:`sqlite3`: diff --git a/Lib/shutil.py b/Lib/shutil.py index 0fed0117a63234..dd93872e83c9e2 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -721,11 +721,6 @@ def rmtree(path, ignore_errors=False, onerror=None, *, onexc=None, dir_fd=None): If both onerror and onexc are set, onerror is ignored and onexc is used. """ - if onerror is not None: - import warnings - warnings.warn("onerror argument is deprecated, use onexc instead", - DeprecationWarning, stacklevel=2) - sys.audit("shutil.rmtree", path, dir_fd) if ignore_errors: def onexc(*args): diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index d231e66b7b889f..ae6c6814fcc3ec 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -208,8 +208,7 @@ def test_rmtree_fails_on_symlink_onerror(self): errors = [] def onerror(*args): errors.append(args) - with self.assertWarns(DeprecationWarning): - shutil.rmtree(link, onerror=onerror) + shutil.rmtree(link, onerror=onerror) self.assertEqual(len(errors), 1) self.assertIs(errors[0][0], os.path.islink) self.assertEqual(errors[0][1], link) @@ -270,8 +269,7 @@ def test_rmtree_fails_on_junctions_onerror(self): errors = [] def onerror(*args): errors.append(args) - with self.assertWarns(DeprecationWarning): - shutil.rmtree(link, onerror=onerror) + shutil.rmtree(link, onerror=onerror) self.assertEqual(len(errors), 1) self.assertIs(errors[0][0], os.path.islink) self.assertEqual(errors[0][1], link) @@ -340,8 +338,7 @@ def test_rmtree_errors_onerror(self): errors = [] def onerror(*args): errors.append(args) - with self.assertWarns(DeprecationWarning): - shutil.rmtree(filename, onerror=onerror) + shutil.rmtree(filename, onerror=onerror) self.assertEqual(len(errors), 2) self.assertIs(errors[0][0], os.scandir) self.assertEqual(errors[0][1], filename) @@ -410,8 +407,7 @@ def test_on_error(self): self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode) self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode) - with self.assertWarns(DeprecationWarning): - shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror) + shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror) # Test whether onerror has actually been called. self.assertEqual(self.errorState, 3, "Expected call to onerror function did not happen.") @@ -537,8 +533,7 @@ def onexc(*args): self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode) self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode) - with self.assertWarns(DeprecationWarning): - shutil.rmtree(TESTFN, onerror=onerror, onexc=onexc) + shutil.rmtree(TESTFN, onerror=onerror, onexc=onexc) self.assertTrue(onexc_called) self.assertFalse(onerror_called) diff --git a/Misc/NEWS.d/next/Library/2023-12-03-12-41-48.gh-issue-112645.blMsKf.rst b/Misc/NEWS.d/next/Library/2023-12-03-12-41-48.gh-issue-112645.blMsKf.rst new file mode 100644 index 00000000000000..4e8f6ebdb882e0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-03-12-41-48.gh-issue-112645.blMsKf.rst @@ -0,0 +1 @@ +Remove deprecation error on passing ``onerror`` to :func:`shutil.rmtree`. From c27b09c81368bc3b756e94a79a39307ce44a4a2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Aur=C3=A9lio=20A=2E=20Barbosa?= Date: Sun, 3 Dec 2023 12:14:14 -0300 Subject: [PATCH 148/228] Fix link to 'The Perils of Floating Point', on the tutorial (GH-112499) Use author link to 'The Perils of Floating Point'. --- Doc/tutorial/floatingpoint.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/tutorial/floatingpoint.rst b/Doc/tutorial/floatingpoint.rst index 30f3dfb6b238b4..0795e2fef98830 100644 --- a/Doc/tutorial/floatingpoint.rst +++ b/Doc/tutorial/floatingpoint.rst @@ -150,7 +150,7 @@ section. See `Examples of Floating Point Problems `_ for a pleasant summary of how binary floating-point works and the kinds of problems commonly encountered in practice. Also see -`The Perils of Floating Point `_ +`The Perils of Floating Point `_ for a more complete account of other common surprises. As that says near the end, "there are no easy answers." Still, don't be unduly From 45650d1c479a8b0370f126d821718dd3c502f333 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sun, 3 Dec 2023 17:32:49 +0000 Subject: [PATCH 149/228] gh-101100: Fix most Sphinx nitpicks in `inspect.rst` (#112662) --- Doc/conf.py | 3 +++ Doc/library/inspect.rst | 15 +++++++++------ Doc/reference/datamodel.rst | 2 ++ Doc/whatsnew/2.6.rst | 5 +++-- Doc/whatsnew/2.7.rst | 3 ++- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Doc/conf.py b/Doc/conf.py index be2a86e12fa2e4..323d443588ceb6 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -163,6 +163,9 @@ ('envvar', 'USER'), ('envvar', 'USERNAME'), ('envvar', 'USERPROFILE'), + # Deprecated function that was never documented: + ('py:func', 'getargspec'), + ('py:func', 'inspect.getargspec'), ] # Temporary undocumented names. diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 815bd54107a987..6fd0d32afe7415 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -273,7 +273,7 @@ attributes (see :ref:`import-mod-attrs` for module attributes): :func:`getmembers` will only return class attributes defined in the metaclass when the argument is a class and those attributes have been - listed in the metaclass' custom :meth:`__dir__`. + listed in the metaclass' custom :meth:`~object.__dir__`. .. function:: getmembers_static(object[, predicate]) @@ -487,12 +487,13 @@ attributes (see :ref:`import-mod-attrs` for module attributes): has a :meth:`~object.__get__` method but not a :meth:`~object.__set__` method, but beyond that the set of attributes varies. A :attr:`~definition.__name__` attribute is usually - sensible, and :attr:`__doc__` often is. + sensible, and :attr:`!__doc__` often is. Methods implemented via descriptors that also pass one of the other tests return ``False`` from the :func:`ismethoddescriptor` test, simply because the other tests promise more -- you can, e.g., count on having the - :attr:`__func__` attribute (etc) when an object passes :func:`ismethod`. + :ref:`__func__ ` attribute (etc) when an object passes + :func:`ismethod`. .. function:: isdatadescriptor(object) @@ -503,7 +504,7 @@ attributes (see :ref:`import-mod-attrs` for module attributes): Examples are properties (defined in Python), getsets, and members. The latter two are defined in C and there are more specific tests available for those types, which is robust across Python implementations. Typically, data - descriptors will also have :attr:`~definition.__name__` and :attr:`__doc__` attributes + descriptors will also have :attr:`~definition.__name__` and :attr:`!__doc__` attributes (properties, getsets, and members have both of these attributes), but this is not guaranteed. @@ -1440,7 +1441,8 @@ Fetching attributes statically Both :func:`getattr` and :func:`hasattr` can trigger code execution when fetching or checking for the existence of attributes. Descriptors, like -properties, will be invoked and :meth:`__getattr__` and :meth:`__getattribute__` +properties, will be invoked and :meth:`~object.__getattr__` and +:meth:`~object.__getattribute__` may be called. For cases where you want passive introspection, like documentation tools, this @@ -1450,7 +1452,8 @@ but avoids executing code when it fetches attributes. .. function:: getattr_static(obj, attr, default=None) Retrieve attributes without triggering dynamic lookup via the - descriptor protocol, :meth:`__getattr__` or :meth:`__getattribute__`. + descriptor protocol, :meth:`~object.__getattr__` + or :meth:`~object.__getattribute__`. Note: this function may not be able to retrieve all attributes that getattr can fetch (like dynamically created attributes) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index f7d3d2d0bbec23..29298b79ef06dd 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -634,6 +634,8 @@ code object; see the description of internal types below. The module. +.. _instance-methods: + Instance methods ^^^^^^^^^^^^^^^^ diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index 016de153f3dd6a..8bdbb0fa352ed1 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -1677,8 +1677,9 @@ Some smaller changes made to the core Python language are: (:issue:`1591665`) * Instance method objects have new attributes for the object and function - comprising the method; the new synonym for :attr:`im_self` is - :attr:`__self__`, and :attr:`im_func` is also available as :attr:`__func__`. + comprising the method; the new synonym for :attr:`!im_self` is + :ref:`__self__ `, and :attr:`!im_func` is also available as + :ref:`__func__ `. The old names are still supported in Python 2.6, but are gone in 3.0. * An obscure change: when you use the :func:`locals` function inside a diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index da66dd731831bc..4072e040dc9130 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -860,7 +860,8 @@ Some smaller changes made to the core Python language are: * When using ``@classmethod`` and ``@staticmethod`` to wrap methods as class or static methods, the wrapper object now - exposes the wrapped function as their :attr:`__func__` attribute. + exposes the wrapped function as their :ref:`__func__ ` + attribute. (Contributed by Amaury Forgeot d'Arc, after a suggestion by George Sakkis; :issue:`5982`.) From 489aeac3a2d3b347ff033334688e2f44eec7944a Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 3 Dec 2023 22:23:09 +0200 Subject: [PATCH 150/228] gh-101100: Fix Sphinx warning in `library/gettext.rst` (#112668) Co-authored-by: Alex Waygood --- Doc/library/gettext.rst | 14 +++++++------- Doc/tools/.nitignore | 1 - 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Doc/library/gettext.rst b/Doc/library/gettext.rst index dc6cf5533fccbe..41beac3e0c7396 100644 --- a/Doc/library/gettext.rst +++ b/Doc/library/gettext.rst @@ -257,7 +257,7 @@ are the methods of :class:`!NullTranslations`: .. method:: info() - Return the "protected" :attr:`_info` variable, a dictionary containing + Return a dictionary containing the metadata found in the message catalog file. @@ -296,9 +296,9 @@ are the methods of :class:`!NullTranslations`: The :class:`GNUTranslations` class ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The :mod:`gettext` module provides one additional class derived from +The :mod:`!gettext` module provides one additional class derived from :class:`NullTranslations`: :class:`GNUTranslations`. This class overrides -:meth:`_parse` to enable reading GNU :program:`gettext` format :file:`.mo` files +:meth:`!_parse` to enable reading GNU :program:`gettext` format :file:`.mo` files in both big-endian and little-endian format. :class:`GNUTranslations` parses optional metadata out of the translation @@ -306,7 +306,7 @@ catalog. It is convention with GNU :program:`gettext` to include metadata as the translation for the empty string. This metadata is in :rfc:`822`\ -style ``key: value`` pairs, and should contain the ``Project-Id-Version`` key. If the key ``Content-Type`` is found, then the ``charset`` property is used to -initialize the "protected" :attr:`_charset` instance variable, defaulting to +initialize the "protected" :attr:`!_charset` instance variable, defaulting to ``None`` if not found. If the charset encoding is specified, then all message ids and message strings read from the catalog are converted to Unicode using this encoding, else ASCII is assumed. @@ -315,7 +315,7 @@ Since message ids are read as Unicode strings too, all ``*gettext()`` methods will assume message ids as Unicode strings, not byte strings. The entire set of key/value pairs are placed into a dictionary and set as the -"protected" :attr:`_info` instance variable. +"protected" :attr:`!_info` instance variable. If the :file:`.mo` file's magic number is invalid, the major version number is unexpected, or if other problems occur while reading the file, instantiating a @@ -636,9 +636,9 @@ implementations, and valuable experience to the creation of this module: .. rubric:: Footnotes -.. [#] The default locale directory is system dependent; for example, on RedHat Linux +.. [#] The default locale directory is system dependent; for example, on Red Hat Linux it is :file:`/usr/share/locale`, but on Solaris it is :file:`/usr/lib/locale`. - The :mod:`gettext` module does not try to support these system dependent + The :mod:`!gettext` module does not try to support these system dependent defaults; instead its default is :file:`{sys.base_prefix}/share/locale` (see :data:`sys.base_prefix`). For this reason, it is always best to call :func:`bindtextdomain` with an explicit absolute path at the start of your diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 1e3e367460147a..8d79a848b7cc7b 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -58,7 +58,6 @@ Doc/library/fcntl.rst Doc/library/ftplib.rst Doc/library/functions.rst Doc/library/functools.rst -Doc/library/gettext.rst Doc/library/http.client.rst Doc/library/http.cookiejar.rst Doc/library/http.cookies.rst From 09505c5c26d6a4c81b54786eb4196379e9cb223c Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sun, 3 Dec 2023 20:35:10 +0000 Subject: [PATCH 151/228] GH-106747: Improve `Path.glob()` expectations in pathlib tests (#112365) Add trailing slashes to expected `Path.glob()` results wherever a pattern has a trailing slash. This matches what `glob.glob()` produces. Due to another bug (GH-65238) pathlib strips all trailing slashes, so this change is academic for now. --- Lib/test/test_pathlib.py | 66 ++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 427e082f3e16cb..ccaef070974ffd 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1968,9 +1968,9 @@ def _check(glob, expected): _check(p.glob("brokenLink"), ['brokenLink']) if not self.can_symlink: - _check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE"]) + _check(p.glob("*/"), ["dirA/", "dirB/", "dirC/", "dirE/"]) else: - _check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE", "linkB"]) + _check(p.glob("*/"), ["dirA/", "dirB/", "dirC/", "dirE/", "linkB/"]) def test_glob_empty_pattern(self): p = self.cls() @@ -2003,17 +2003,17 @@ def _check(path, glob, expected): _check(p, "*A", ["dirA", "fileA", "linkA"]) _check(p, "*B/*", ["dirB/fileB", "dirB/linkD", "linkB/fileB", "linkB/linkD"]) _check(p, "*/fileB", ["dirB/fileB", "linkB/fileB"]) - _check(p, "*/", ["dirA", "dirB", "dirC", "dirE", "linkB"]) + _check(p, "*/", ["dirA/", "dirB/", "dirC/", "dirE/", "linkB/"]) _check(p, "dir*/*/..", ["dirC/dirD/..", "dirA/linkC/.."]) - _check(p, "dir*/**/", ["dirA", "dirA/linkC", "dirA/linkC/linkD", "dirB", "dirB/linkD", - "dirC", "dirC/dirD", "dirE"]) + _check(p, "dir*/**/", ["dirA/", "dirA/linkC/", "dirA/linkC/linkD/", "dirB/", "dirB/linkD/", + "dirC/", "dirC/dirD/", "dirE/"]) _check(p, "dir*/**/..", ["dirA/..", "dirA/linkC/..", "dirB/..", "dirC/..", "dirC/dirD/..", "dirE/.."]) - _check(p, "dir*/*/**/", ["dirA/linkC", "dirA/linkC/linkD", "dirB/linkD", "dirC/dirD"]) + _check(p, "dir*/*/**/", ["dirA/linkC/", "dirA/linkC/linkD/", "dirB/linkD/", "dirC/dirD/"]) _check(p, "dir*/*/**/..", ["dirA/linkC/..", "dirC/dirD/.."]) _check(p, "dir*/**/fileC", ["dirC/fileC"]) - _check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD"]) - _check(p, "*/dirD/**/", ["dirC/dirD"]) + _check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD/"]) + _check(p, "*/dirD/**/", ["dirC/dirD/"]) def test_glob_no_follow_symlinks_common(self): if not self.can_symlink: @@ -2028,15 +2028,15 @@ def _check(path, glob, expected): _check(p, "*A", ["dirA", "fileA", "linkA"]) _check(p, "*B/*", ["dirB/fileB", "dirB/linkD"]) _check(p, "*/fileB", ["dirB/fileB"]) - _check(p, "*/", ["dirA", "dirB", "dirC", "dirE"]) + _check(p, "*/", ["dirA/", "dirB/", "dirC/", "dirE/"]) _check(p, "dir*/*/..", ["dirC/dirD/.."]) - _check(p, "dir*/**/", ["dirA", "dirB", "dirC", "dirC/dirD", "dirE"]) + _check(p, "dir*/**/", ["dirA/", "dirB/", "dirC/", "dirC/dirD/", "dirE/"]) _check(p, "dir*/**/..", ["dirA/..", "dirB/..", "dirC/..", "dirC/dirD/..", "dirE/.."]) - _check(p, "dir*/*/**/", ["dirC/dirD"]) + _check(p, "dir*/*/**/", ["dirC/dirD/"]) _check(p, "dir*/*/**/..", ["dirC/dirD/.."]) _check(p, "dir*/**/fileC", ["dirC/fileC"]) - _check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD"]) - _check(p, "*/dirD/**/", ["dirC/dirD"]) + _check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD/"]) + _check(p, "*/dirD/**/", ["dirC/dirD/"]) def test_rglob_common(self): def _check(glob, expected): @@ -2058,25 +2058,25 @@ def _check(glob, expected): "dirC/fileC", "dirC/dirD/fileD"]) if not self.can_symlink: _check(p.rglob("*/"), [ - "dirA", "dirB", "dirC", "dirC/dirD", "dirE", + "dirA/", "dirB/", "dirC/", "dirC/dirD/", "dirE/", ]) else: _check(p.rglob("*/"), [ - "dirA", "dirA/linkC", "dirB", "dirB/linkD", "dirC", - "dirC/dirD", "dirE", "linkB", + "dirA/", "dirA/linkC/", "dirB/", "dirB/linkD/", "dirC/", + "dirC/dirD/", "dirE/", "linkB/", ]) - _check(p.rglob(""), ["", "dirA", "dirB", "dirC", "dirE", "dirC/dirD"]) + _check(p.rglob(""), ["./", "dirA/", "dirB/", "dirC/", "dirE/", "dirC/dirD/"]) p = P(BASE, "dirC") _check(p.rglob("*"), ["dirC/fileC", "dirC/novel.txt", "dirC/dirD", "dirC/dirD/fileD"]) _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"]) _check(p.rglob("**/file*"), ["dirC/fileC", "dirC/dirD/fileD"]) - _check(p.rglob("dir*/**/"), ["dirC/dirD"]) + _check(p.rglob("dir*/**/"), ["dirC/dirD/"]) _check(p.rglob("*/*"), ["dirC/dirD/fileD"]) - _check(p.rglob("*/"), ["dirC/dirD"]) - _check(p.rglob(""), ["dirC", "dirC/dirD"]) - _check(p.rglob("**/"), ["dirC", "dirC/dirD"]) + _check(p.rglob("*/"), ["dirC/dirD/"]) + _check(p.rglob(""), ["dirC/", "dirC/dirD/"]) + _check(p.rglob("**/"), ["dirC/", "dirC/dirD/"]) # gh-91616, a re module regression _check(p.rglob("*.txt"), ["dirC/novel.txt"]) _check(p.rglob("*.*"), ["dirC/novel.txt"]) @@ -2095,18 +2095,18 @@ def _check(path, glob, expected): _check(p, "*/fileB", ["dirB/fileB", "dirA/linkC/fileB", "linkB/fileB"]) _check(p, "file*", ["fileA", "dirA/linkC/fileB", "dirB/fileB", "dirC/fileC", "dirC/dirD/fileD", "linkB/fileB"]) - _check(p, "*/", ["dirA", "dirA/linkC", "dirA/linkC/linkD", "dirB", "dirB/linkD", - "dirC", "dirC/dirD", "dirE", "linkB", "linkB/linkD"]) - _check(p, "", ["", "dirA", "dirA/linkC", "dirA/linkC/linkD", "dirB", "dirB/linkD", - "dirC", "dirE", "dirC/dirD", "linkB", "linkB/linkD"]) + _check(p, "*/", ["dirA/", "dirA/linkC/", "dirA/linkC/linkD/", "dirB/", "dirB/linkD/", + "dirC/", "dirC/dirD/", "dirE/", "linkB/", "linkB/linkD/"]) + _check(p, "", ["./", "dirA/", "dirA/linkC/", "dirA/linkC/linkD/", "dirB/", "dirB/linkD/", + "dirC/", "dirE/", "dirC/dirD/", "linkB/", "linkB/linkD/"]) p = P(BASE, "dirC") _check(p, "*", ["dirC/fileC", "dirC/novel.txt", "dirC/dirD", "dirC/dirD/fileD"]) _check(p, "file*", ["dirC/fileC", "dirC/dirD/fileD"]) _check(p, "*/*", ["dirC/dirD/fileD"]) - _check(p, "*/", ["dirC/dirD"]) - _check(p, "", ["dirC", "dirC/dirD"]) + _check(p, "*/", ["dirC/dirD/"]) + _check(p, "", ["dirC/", "dirC/dirD/"]) # gh-91616, a re module regression _check(p, "*.txt", ["dirC/novel.txt"]) _check(p, "*.*", ["dirC/novel.txt"]) @@ -2123,16 +2123,16 @@ def _check(path, glob, expected): _check(p, "*/fileA", []) _check(p, "*/fileB", ["dirB/fileB"]) _check(p, "file*", ["fileA", "dirB/fileB", "dirC/fileC", "dirC/dirD/fileD", ]) - _check(p, "*/", ["dirA", "dirB", "dirC", "dirC/dirD", "dirE"]) - _check(p, "", ["", "dirA", "dirB", "dirC", "dirE", "dirC/dirD"]) + _check(p, "*/", ["dirA/", "dirB/", "dirC/", "dirC/dirD/", "dirE/"]) + _check(p, "", ["./", "dirA/", "dirB/", "dirC/", "dirE/", "dirC/dirD/"]) p = P(BASE, "dirC") _check(p, "*", ["dirC/fileC", "dirC/novel.txt", "dirC/dirD", "dirC/dirD/fileD"]) _check(p, "file*", ["dirC/fileC", "dirC/dirD/fileD"]) _check(p, "*/*", ["dirC/dirD/fileD"]) - _check(p, "*/", ["dirC/dirD"]) - _check(p, "", ["dirC", "dirC/dirD"]) + _check(p, "*/", ["dirC/dirD/"]) + _check(p, "", ["dirC/", "dirC/dirD/"]) # gh-91616, a re module regression _check(p, "*.txt", ["dirC/novel.txt"]) _check(p, "*.*", ["dirC/novel.txt"]) @@ -3642,7 +3642,7 @@ def test_glob(self): P = self.cls p = P(BASE) self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") }) - self.assertEqual(set(p.glob("*a\\")), { P(BASE, "dirA") }) + self.assertEqual(set(p.glob("*a\\")), { P(BASE, "dirA/") }) self.assertEqual(set(p.glob("F*a")), { P(BASE, "fileA") }) self.assertEqual(set(map(str, p.glob("FILEa"))), {f"{p}\\fileA"}) self.assertEqual(set(map(str, p.glob("F*a"))), {f"{p}\\fileA"}) @@ -3651,7 +3651,7 @@ def test_rglob(self): P = self.cls p = P(BASE, "dirC") self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") }) - self.assertEqual(set(p.rglob("*\\")), { P(BASE, "dirC/dirD") }) + self.assertEqual(set(p.rglob("*\\")), { P(BASE, "dirC/dirD/") }) self.assertEqual(set(map(str, p.rglob("FILEd"))), {f"{p}\\dirD\\fileD"}) def test_expanduser(self): From 5a1b5316af648ae79bb91f28253b6272bbbd2886 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 3 Dec 2023 23:45:56 -0500 Subject: [PATCH 152/228] gh-66819: More IDLE htest updates(3) (#112683) Revise spec-callable pairs from percolator to end. --- Lib/idlelib/help.py | 4 ++-- Lib/idlelib/idle_test/htest.py | 16 ++++++++-------- Lib/idlelib/percolator.py | 12 ++++++------ Lib/idlelib/scrolledlist.py | 3 ++- Lib/idlelib/stackviewer.py | 4 ++-- Lib/idlelib/undo.py | 14 +++++++------- 6 files changed, 27 insertions(+), 26 deletions(-) diff --git a/Lib/idlelib/help.py b/Lib/idlelib/help.py index cc027b9cef4f5b..580a327f620a79 100644 --- a/Lib/idlelib/help.py +++ b/Lib/idlelib/help.py @@ -278,7 +278,7 @@ def copy_strip(): out.write(line.rstrip() + b'\n') print(f'{src} copied to {dst}') -def show_idlehelp(parent): +def _helpwindow(parent): "Create HelpWindow; called from Idle Help event handler." filename = join(abspath(dirname(__file__)), 'help.html') if not isfile(filename): @@ -291,4 +291,4 @@ def show_idlehelp(parent): main('idlelib.idle_test.test_help', verbosity=2, exit=False) from idlelib.idle_test.htest import run - run(show_idlehelp) + run(_helpwindow) diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index a59b474fba47d8..4042106bf44a9f 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -190,6 +190,13 @@ ", [Cancel], or [X] prints None to shell" } +_helpwindow_spec = { + 'file': 'help', + 'kwds': {}, + 'msg': "If the help text displays, this works.\n" + "Text is selectable. Window is scrollable." + } + _io_binding_spec = { 'file': 'iomenu', 'kwds': {}, @@ -312,14 +319,7 @@ "Right clicking an item will display a popup." } -show_idlehelp_spec = { - 'file': 'help', - 'kwds': {}, - 'msg': "If the help text displays, this works.\n" - "Text is selectable. Window is scrollable." - } - -_stack_viewer_spec = { +_stackbrowser_spec = { 'file': 'stackviewer', 'kwds': {}, 'msg': "A stacktrace for a NameError exception.\n" diff --git a/Lib/idlelib/percolator.py b/Lib/idlelib/percolator.py index 1fe34d29f54eb2..91ad7272f4ae56 100644 --- a/Lib/idlelib/percolator.py +++ b/Lib/idlelib/percolator.py @@ -86,11 +86,11 @@ def delete(self, *args): print(self.name, ": delete", args) self.delegate.delete(*args) - box = tk.Toplevel(parent) - box.title("Test Percolator") + top = tk.Toplevel(parent) + top.title("Test Percolator") x, y = map(int, parent.geometry().split('+')[1:]) - box.geometry("+%d+%d" % (x, y + 175)) - text = tk.Text(box) + top.geometry("+%d+%d" % (x, y + 175)) + text = tk.Text(top) p = Percolator(text) pin = p.insertfilter pout = p.removefilter @@ -104,10 +104,10 @@ def toggle2(): text.pack() var1 = tk.IntVar(parent) - cb1 = tk.Checkbutton(box, text="Tracer1", command=toggle1, variable=var1) + cb1 = tk.Checkbutton(top, text="Tracer1", command=toggle1, variable=var1) cb1.pack() var2 = tk.IntVar(parent) - cb2 = tk.Checkbutton(box, text="Tracer2", command=toggle2, variable=var2) + cb2 = tk.Checkbutton(top, text="Tracer2", command=toggle2, variable=var2) cb2.pack() if __name__ == "__main__": diff --git a/Lib/idlelib/scrolledlist.py b/Lib/idlelib/scrolledlist.py index 71fd18ab19ec8a..4f1241a576fca1 100644 --- a/Lib/idlelib/scrolledlist.py +++ b/Lib/idlelib/scrolledlist.py @@ -132,6 +132,7 @@ def _scrolled_list(parent): # htest # top = Toplevel(parent) x, y = map(int, parent.geometry().split('+')[1:]) top.geometry("+%d+%d" % (x+200, y + 175)) + class MyScrolledList(ScrolledList): def fill_menu(self): self.menu.add_command(label="right click") def on_select(self, index): print("select", self.get(index)) @@ -143,7 +144,7 @@ def on_double(self, index): print("double", self.get(index)) if __name__ == '__main__': from unittest import main - main('idlelib.idle_test.test_scrolledlist', verbosity=2,) + main('idlelib.idle_test.test_scrolledlist', verbosity=2, exit=False) from idlelib.idle_test.htest import run run(_scrolled_list) diff --git a/Lib/idlelib/stackviewer.py b/Lib/idlelib/stackviewer.py index f8e60fd9b6d818..977c56ef15f2ae 100644 --- a/Lib/idlelib/stackviewer.py +++ b/Lib/idlelib/stackviewer.py @@ -113,7 +113,7 @@ def setfunction(value, key=key, object=self.object): return sublist -def _stack_viewer(parent): # htest # +def _stackbrowser(parent): # htest # from idlelib.pyshell import PyShellFileList top = tk.Toplevel(parent) top.title("Test StackViewer") @@ -131,4 +131,4 @@ def _stack_viewer(parent): # htest # main('idlelib.idle_test.test_stackviewer', verbosity=2, exit=False) from idlelib.idle_test.htest import run - run(_stack_viewer) + run(_stackbrowser) diff --git a/Lib/idlelib/undo.py b/Lib/idlelib/undo.py index 5f10c0f05c1acb..f1d03f4c9ed5ef 100644 --- a/Lib/idlelib/undo.py +++ b/Lib/idlelib/undo.py @@ -339,23 +339,23 @@ def bump_depth(self, incr=1): def _undo_delegator(parent): # htest # from tkinter import Toplevel, Text, Button from idlelib.percolator import Percolator - undowin = Toplevel(parent) - undowin.title("Test UndoDelegator") + top = Toplevel(parent) + top.title("Test UndoDelegator") x, y = map(int, parent.geometry().split('+')[1:]) - undowin.geometry("+%d+%d" % (x, y + 175)) + top.geometry("+%d+%d" % (x, y + 175)) - text = Text(undowin, height=10) + text = Text(top, height=10) text.pack() text.focus_set() p = Percolator(text) d = UndoDelegator() p.insertfilter(d) - undo = Button(undowin, text="Undo", command=lambda:d.undo_event(None)) + undo = Button(top, text="Undo", command=lambda:d.undo_event(None)) undo.pack(side='left') - redo = Button(undowin, text="Redo", command=lambda:d.redo_event(None)) + redo = Button(top, text="Redo", command=lambda:d.redo_event(None)) redo.pack(side='left') - dump = Button(undowin, text="Dump", command=lambda:d.dump_event(None)) + dump = Button(top, text="Dump", command=lambda:d.dump_event(None)) dump.pack(side='left') if __name__ == "__main__": From e5b0db0315941b968ebcc2414bfcdd2da44fd3c2 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Mon, 4 Dec 2023 01:36:40 -0500 Subject: [PATCH 153/228] gh-66819: More IDLE htest updates(4) (#112686) Mostly double spacing before 'if __name__...'. --- Lib/idlelib/browser.py | 1 + Lib/idlelib/calltip_w.py | 1 + Lib/idlelib/config.py | 1 + Lib/idlelib/debugobj.py | 1 + Lib/idlelib/delegator.py | 1 + Lib/idlelib/dynoption.py | 2 ++ Lib/idlelib/editor.py | 1 + Lib/idlelib/filelist.py | 1 + Lib/idlelib/grep.py | 6 ++-- Lib/idlelib/help.py | 2 ++ Lib/idlelib/idle_test/htest.py | 60 +++++++++++++++++----------------- Lib/idlelib/iomenu.py | 2 ++ Lib/idlelib/multicall.py | 1 + Lib/idlelib/outwin.py | 1 + Lib/idlelib/percolator.py | 2 ++ Lib/idlelib/pyshell.py | 1 + Lib/idlelib/redirector.py | 1 + Lib/idlelib/replace.py | 1 + Lib/idlelib/scrolledlist.py | 1 + Lib/idlelib/search.py | 1 + Lib/idlelib/sidebar.py | 4 +-- Lib/idlelib/statusbar.py | 1 + Lib/idlelib/tree.py | 1 + Lib/idlelib/undo.py | 1 + Lib/idlelib/util.py | 1 + 25 files changed, 62 insertions(+), 34 deletions(-) diff --git a/Lib/idlelib/browser.py b/Lib/idlelib/browser.py index 672e229ffbca94..8b9060e57072ea 100644 --- a/Lib/idlelib/browser.py +++ b/Lib/idlelib/browser.py @@ -250,6 +250,7 @@ def closure(): class Nested_in_closure: pass ModuleBrowser(parent, file, _htest=True) + if __name__ == "__main__": if len(sys.argv) == 1: # If pass file on command line, unittest fails. from unittest import main diff --git a/Lib/idlelib/calltip_w.py b/Lib/idlelib/calltip_w.py index 278546064adde2..9386376058c791 100644 --- a/Lib/idlelib/calltip_w.py +++ b/Lib/idlelib/calltip_w.py @@ -193,6 +193,7 @@ def calltip_hide(event): text.focus_set() + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_calltip_w', verbosity=2, exit=False) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 898efeb4dd1550..92992fd9cce9cd 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -906,6 +906,7 @@ def dumpCfg(cfg): dumpCfg(idleConf.userCfg) print('\nlines = ', line, ', crc = ', crc, sep='') + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_config', verbosity=2, exit=False) diff --git a/Lib/idlelib/debugobj.py b/Lib/idlelib/debugobj.py index 0bf2cb1d5bbfe2..156377f8ed26ac 100644 --- a/Lib/idlelib/debugobj.py +++ b/Lib/idlelib/debugobj.py @@ -135,6 +135,7 @@ def _debug_object_browser(parent): # htest # node = TreeNode(sc.canvas, None, item) node.update() + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_debugobj', verbosity=2, exit=False) diff --git a/Lib/idlelib/delegator.py b/Lib/idlelib/delegator.py index 55c95da8532f47..93ae8bbd43ff44 100644 --- a/Lib/idlelib/delegator.py +++ b/Lib/idlelib/delegator.py @@ -28,6 +28,7 @@ def setdelegate(self, delegate): self.resetcache() self.delegate = delegate + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_delegator', verbosity=2) diff --git a/Lib/idlelib/dynoption.py b/Lib/idlelib/dynoption.py index d5dfc3eda13f60..b8937f7106ca75 100644 --- a/Lib/idlelib/dynoption.py +++ b/Lib/idlelib/dynoption.py @@ -29,6 +29,7 @@ def SetMenu(self,valueList,value=None): if value: self.variable.set(value) + def _dyn_option_menu(parent): # htest # from tkinter import Toplevel # + StringVar, Button @@ -49,6 +50,7 @@ def update(): button = Button(top, text="Change option set", command=update) button.pack() + if __name__ == '__main__': # Only module without unittests because of intention to replace. from idlelib.idle_test.htest import run diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 69b27d0683a104..6ad383f460c7ee 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -1748,6 +1748,7 @@ def _editor_window(parent): # htest # # Does not stop error, neither does following # edit.text.bind("<>", edit.close_event) + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_editor', verbosity=2, exit=False) diff --git a/Lib/idlelib/filelist.py b/Lib/idlelib/filelist.py index f87781d2570fe0..e27e5d32a0ff63 100644 --- a/Lib/idlelib/filelist.py +++ b/Lib/idlelib/filelist.py @@ -124,6 +124,7 @@ def _test(): # TODO check and convert to htest if flist.inversedict: root.mainloop() + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_filelist', verbosity=2) diff --git a/Lib/idlelib/grep.py b/Lib/idlelib/grep.py index 12513594b76f8f..ef14349960bfa2 100644 --- a/Lib/idlelib/grep.py +++ b/Lib/idlelib/grep.py @@ -204,15 +204,17 @@ def _grep_dialog(parent): # htest # frame.pack() text = Text(frame, height=5) text.pack() + text.insert('1.0', 'import grep') def show_grep_dialog(): - text.tag_add(SEL, "1.0", END) + text.tag_add(SEL, "1.0", '1.end') grep(text, flist=flist) - text.tag_remove(SEL, "1.0", END) + text.tag_remove(SEL, "1.0", '1.end') button = Button(frame, text="Show GrepDialog", command=show_grep_dialog) button.pack() + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_grep', verbosity=2, exit=False) diff --git a/Lib/idlelib/help.py b/Lib/idlelib/help.py index 580a327f620a79..3cc7e36e35555b 100644 --- a/Lib/idlelib/help.py +++ b/Lib/idlelib/help.py @@ -278,6 +278,7 @@ def copy_strip(): out.write(line.rstrip() + b'\n') print(f'{src} copied to {dst}') + def _helpwindow(parent): "Create HelpWindow; called from Idle Help event handler." filename = join(abspath(dirname(__file__)), 'help.html') @@ -286,6 +287,7 @@ def _helpwindow(parent): return HelpWindow(parent, filename, 'IDLE Help (%s)' % python_version()) + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_help', verbosity=2, exit=False) diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index 4042106bf44a9f..997f85ff5a78b2 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -170,8 +170,8 @@ 'msg': "Click the 'Show GrepDialog' button.\n" "Test the various 'Find-in-files' functions.\n" "The results should be displayed in a new '*Output*' window.\n" - "'Right-click'->'Go to file/line' anywhere in the search results " - "should open that file \nin a new EditorWindow." + "'Right-click'->'Go to file/line' in the search results\n " + "should open that file in a new EditorWindow." } HelpSource_spec = { @@ -210,26 +210,6 @@ "Check that changes were saved by opening the file elsewhere." } -_linenumbers_drag_scrolling_spec = { - 'file': 'sidebar', - 'kwds': {}, - 'msg': textwrap.dedent("""\ - 1. Click on the line numbers and drag down below the edge of the - window, moving the mouse a bit and then leaving it there for a - while. The text and line numbers should gradually scroll down, - with the selection updated continuously. - - 2. With the lines still selected, click on a line number above - or below the selected lines. Only the line whose number was - clicked should be selected. - - 3. Repeat step #1, dragging to above the window. The text and - line numbers should gradually scroll up, with the selection - updated continuously. - - 4. Repeat step #2, clicking a line number below the selection."""), - } - _multi_call_spec = { 'file': 'multicall', 'kwds': {}, @@ -295,6 +275,15 @@ "Click [Close] or [X] to close the 'Replace Dialog'." } +_scrolled_list_spec = { + 'file': 'scrolledlist', + 'kwds': {}, + 'msg': "You should see a scrollable list of items\n" + "Selecting (clicking) or double clicking an item " + "prints the name to the console or Idle shell.\n" + "Right clicking an item will display a popup." + } + _search_dialog_spec = { 'file': 'search', 'kwds': {}, @@ -310,21 +299,31 @@ "Its only action is to close." } -_scrolled_list_spec = { - 'file': 'scrolledlist', +_sidebar_number_scrolling_spec = { + 'file': 'sidebar', 'kwds': {}, - 'msg': "You should see a scrollable list of items\n" - "Selecting (clicking) or double clicking an item " - "prints the name to the console or Idle shell.\n" - "Right clicking an item will display a popup." + 'msg': textwrap.dedent("""\ + 1. Click on the line numbers and drag down below the edge of the + window, moving the mouse a bit and then leaving it there for a + while. The text and line numbers should gradually scroll down, + with the selection updated continuously. + + 2. With the lines still selected, click on a line number above + or below the selected lines. Only the line whose number was + clicked should be selected. + + 3. Repeat step #1, dragging to above the window. The text and + line numbers should gradually scroll up, with the selection + updated continuously. + + 4. Repeat step #2, clicking a line number below the selection."""), } _stackbrowser_spec = { 'file': 'stackviewer', 'kwds': {}, 'msg': "A stacktrace for a NameError exception.\n" - "Expand 'idlelib ...' and ''.\n" - "Check that exc_value, exc_tb, and exc_type are correct.\n" + "Should have NameError and 1 traceback line." } _tooltip_spec = { @@ -438,5 +437,6 @@ def close(_=None): next_test() root.mainloop() + if __name__ == '__main__': run() diff --git a/Lib/idlelib/iomenu.py b/Lib/idlelib/iomenu.py index 7629101635b8bb..667623ec71ac98 100644 --- a/Lib/idlelib/iomenu.py +++ b/Lib/idlelib/iomenu.py @@ -393,6 +393,7 @@ def updaterecentfileslist(self,filename): if self.editwin.flist: self.editwin.update_recent_files_list(filename) + def _io_binding(parent): # htest # from tkinter import Toplevel, Text @@ -430,6 +431,7 @@ def savecopy(self, event): editwin = MyEditWin(text) IOBinding(editwin) + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_iomenu', verbosity=2, exit=False) diff --git a/Lib/idlelib/multicall.py b/Lib/idlelib/multicall.py index 2aa4a54125156f..41f81813113062 100644 --- a/Lib/idlelib/multicall.py +++ b/Lib/idlelib/multicall.py @@ -442,6 +442,7 @@ def handler(event): bindseq("") bindseq("") + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_mainmenu', verbosity=2, exit=False) diff --git a/Lib/idlelib/outwin.py b/Lib/idlelib/outwin.py index 610031e26f1dff..5ed3f35a7af655 100644 --- a/Lib/idlelib/outwin.py +++ b/Lib/idlelib/outwin.py @@ -182,6 +182,7 @@ def setup(self): text.tag_raise('sel') self.write = self.owin.write + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_outwin', verbosity=2, exit=False) diff --git a/Lib/idlelib/percolator.py b/Lib/idlelib/percolator.py index 91ad7272f4ae56..aa73427c4915c8 100644 --- a/Lib/idlelib/percolator.py +++ b/Lib/idlelib/percolator.py @@ -103,6 +103,7 @@ def toggle2(): (pin if var2.get() else pout)(t2) text.pack() + text.focus_set() var1 = tk.IntVar(parent) cb1 = tk.Checkbutton(top, text="Tracer1", command=toggle1, variable=var1) cb1.pack() @@ -110,6 +111,7 @@ def toggle2(): cb2 = tk.Checkbutton(top, text="Tracer2", command=toggle2, variable=var2) cb2.pack() + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_percolator', verbosity=2, exit=False) diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 00b3732a7bc4eb..1524fccd5d20f8 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -1694,6 +1694,7 @@ def main(): root.destroy() capture_warnings(False) + if __name__ == "__main__": main() diff --git a/Lib/idlelib/redirector.py b/Lib/idlelib/redirector.py index 4928340e98df68..08728956abd900 100644 --- a/Lib/idlelib/redirector.py +++ b/Lib/idlelib/redirector.py @@ -164,6 +164,7 @@ def my_insert(*args): original_insert(*args) original_insert = redir.register("insert", my_insert) + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_redirector', verbosity=2, exit=False) diff --git a/Lib/idlelib/replace.py b/Lib/idlelib/replace.py index ca83173877ad1d..a29ca591427491 100644 --- a/Lib/idlelib/replace.py +++ b/Lib/idlelib/replace.py @@ -299,6 +299,7 @@ def show_replace(): button = Button(frame, text="Replace", command=show_replace) button.pack() + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_replace', verbosity=2, exit=False) diff --git a/Lib/idlelib/scrolledlist.py b/Lib/idlelib/scrolledlist.py index 4f1241a576fca1..4fb418db326255 100644 --- a/Lib/idlelib/scrolledlist.py +++ b/Lib/idlelib/scrolledlist.py @@ -142,6 +142,7 @@ def on_double(self, index): print("double", self.get(index)) for i in range(30): scrolled_list.append("Item %02d" % i) + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_scrolledlist', verbosity=2, exit=False) diff --git a/Lib/idlelib/search.py b/Lib/idlelib/search.py index b35f3b59c3d2e8..935a4832257fa4 100644 --- a/Lib/idlelib/search.py +++ b/Lib/idlelib/search.py @@ -156,6 +156,7 @@ def show_find(): button = Button(frame, text="Search (selection ignored)", command=show_find) button.pack() + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_search', verbosity=2, exit=False) diff --git a/Lib/idlelib/sidebar.py b/Lib/idlelib/sidebar.py index 8e7eae5037c90c..ff77b568a786e0 100644 --- a/Lib/idlelib/sidebar.py +++ b/Lib/idlelib/sidebar.py @@ -513,7 +513,7 @@ def update_colors(self): self.change_callback() -def _linenumbers_drag_scrolling(parent): # htest # +def _sidebar_number_scrolling(parent): # htest # from idlelib.idle_test.test_sidebar import Dummy_editwin top = tk.Toplevel(parent) @@ -540,4 +540,4 @@ def _linenumbers_drag_scrolling(parent): # htest # main('idlelib.idle_test.test_sidebar', verbosity=2, exit=False) from idlelib.idle_test.htest import run - run(_linenumbers_drag_scrolling) + run(_sidebar_number_scrolling) diff --git a/Lib/idlelib/statusbar.py b/Lib/idlelib/statusbar.py index 7048bd64b98753..8445d4cc8dfdb9 100644 --- a/Lib/idlelib/statusbar.py +++ b/Lib/idlelib/statusbar.py @@ -43,6 +43,7 @@ def change(): button.pack(side='bottom') frame.pack() + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_statusbar', verbosity=2, exit=False) diff --git a/Lib/idlelib/tree.py b/Lib/idlelib/tree.py index 5f30f0f6092bfa..9c2eb47b24aec9 100644 --- a/Lib/idlelib/tree.py +++ b/Lib/idlelib/tree.py @@ -492,6 +492,7 @@ def _tree_widget(parent): # htest # node = TreeNode(sc.canvas, None, item) node.expand() + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_tree', verbosity=2, exit=False) diff --git a/Lib/idlelib/undo.py b/Lib/idlelib/undo.py index f1d03f4c9ed5ef..f52446d5fcdcf8 100644 --- a/Lib/idlelib/undo.py +++ b/Lib/idlelib/undo.py @@ -358,6 +358,7 @@ def _undo_delegator(parent): # htest # dump = Button(top, text="Dump", command=lambda:d.dump_event(None)) dump.pack(side='left') + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_undo', verbosity=2, exit=False) diff --git a/Lib/idlelib/util.py b/Lib/idlelib/util.py index ede670a4db5536..5ac69a7b94cb56 100644 --- a/Lib/idlelib/util.py +++ b/Lib/idlelib/util.py @@ -16,6 +16,7 @@ # .pyw is for Windows; .pyi is for stub files. py_extensions = ('.py', '.pyw', '.pyi') # Order needed for open/save dialogs. + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_util', verbosity=2) From 23e001fa9f1897ba3384c02bbbe634313358a549 Mon Sep 17 00:00:00 2001 From: Christopher Chavez Date: Mon, 4 Dec 2023 02:00:27 -0600 Subject: [PATCH 154/228] gh-112678: Declare `Tkapp_CallDeallocArgs()` as `static` (GH-112679) --- Modules/_tkinter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index f9a18644945c65..64e752c305aae1 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -1202,7 +1202,7 @@ typedef struct Tkapp_CallEvent { Tcl_Condition *done; } Tkapp_CallEvent; -void +static void Tkapp_CallDeallocArgs(Tcl_Obj** objv, Tcl_Obj** objStore, int objc) { int i; From 0e732d0997cff08855d98c17af4dd5527f10e419 Mon Sep 17 00:00:00 2001 From: chilaxan Date: Mon, 4 Dec 2023 03:15:43 -0500 Subject: [PATCH 155/228] gh-112625: Protect bytearray from being freed by misbehaving iterator inside bytearray.join (GH-112626) --- Lib/test/test_builtin.py | 17 +++++++++++++++++ ...23-12-03-19-34-51.gh-issue-112625.QWTlwS.rst | 1 + Objects/bytearrayobject.c | 5 ++++- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-12-03-19-34-51.gh-issue-112625.QWTlwS.rst diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index b7966f8f03875b..535856adaea4d3 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -2039,6 +2039,23 @@ def test_bytearray_extend_error(self): bad_iter = map(int, "X") self.assertRaises(ValueError, array.extend, bad_iter) + def test_bytearray_join_with_misbehaving_iterator(self): + # Issue #112625 + array = bytearray(b',') + def iterator(): + array.clear() + yield b'A' + yield b'B' + self.assertRaises(BufferError, array.join, iterator()) + + def test_bytearray_join_with_custom_iterator(self): + # Issue #112625 + array = bytearray(b',') + def iterator(): + yield b'A' + yield b'B' + self.assertEqual(bytearray(b'A,B'), array.join(iterator())) + def test_construct_singletons(self): for const in None, Ellipsis, NotImplemented: tp = type(const) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-03-19-34-51.gh-issue-112625.QWTlwS.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-03-19-34-51.gh-issue-112625.QWTlwS.rst new file mode 100644 index 00000000000000..4970e10f3f4dcb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-03-19-34-51.gh-issue-112625.QWTlwS.rst @@ -0,0 +1 @@ +Fixes a bug where a bytearray object could be cleared while iterating over an argument in the ``bytearray.join()`` method that could result in reading memory after it was freed. diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index 67073190cc889d..659de7d3dd5a99 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -2007,7 +2007,10 @@ static PyObject * bytearray_join(PyByteArrayObject *self, PyObject *iterable_of_bytes) /*[clinic end generated code: output=a8516370bf68ae08 input=aba6b1f9b30fcb8e]*/ { - return stringlib_bytes_join((PyObject*)self, iterable_of_bytes); + self->ob_exports++; // this protects `self` from being cleared/resized if `iterable_of_bytes` is a custom iterator + PyObject* ret = stringlib_bytes_join((PyObject*)self, iterable_of_bytes); + self->ob_exports--; // unexport `self` + return ret; } /*[clinic input] From dee7beeb4f9d28fec945c8c495027cc22a512328 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 4 Dec 2023 11:09:06 +0200 Subject: [PATCH 156/228] bpo-34392: Add sys. _is_interned() (GH-8755) --- Doc/library/sys.rst | 12 +++++++ Doc/whatsnew/3.13.rst | 7 ++++ Lib/test/test_sys.py | 14 ++++++++ .../2018-08-13-13-25-15.bpo-34392.9kIlMF.rst | 1 + Python/clinic/sysmodule.c.h | 36 ++++++++++++++++++- Python/sysmodule.c | 18 ++++++++++ 6 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2018-08-13-13-25-15.bpo-34392.9kIlMF.rst diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index bf9aaca2a696de..7f359819e6847e 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -1205,6 +1205,18 @@ always available. .. versionadded:: 3.12 +.. function:: _is_interned(string) + + Return :const:`True` if the given string is "interned", :const:`False` + otherwise. + + .. versionadded:: 3.13 + + .. impl-detail:: + + It is not guaranteed to exist in all implementations of Python. + + .. data:: last_type last_value last_traceback diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 372e4a45468e68..676305c6e1d06a 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -297,6 +297,13 @@ sqlite3 object is not :meth:`closed ` explicitly. (Contributed by Erlend E. Aasland in :gh:`105539`.) +sys +--- + +* Add the :func:`sys._is_interned` function to test if the string was interned. + This function is not guaranteed to exist in all implementations of Python. + (Contributed by Serhiy Storchaka in :gh:`78573`.) + tkinter ------- diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 0028281596fa4b..8c2c1a40f74bf2 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -691,11 +691,23 @@ def test_43581(self): self.assertEqual(sys.__stdout__.encoding, sys.__stderr__.encoding) def test_intern(self): + has_is_interned = (test.support.check_impl_detail(cpython=True) + or hasattr(sys, '_is_interned')) self.assertRaises(TypeError, sys.intern) + self.assertRaises(TypeError, sys.intern, b'abc') + if has_is_interned: + self.assertRaises(TypeError, sys._is_interned) + self.assertRaises(TypeError, sys._is_interned, b'abc') s = "never interned before" + str(random.randrange(0, 10**9)) self.assertTrue(sys.intern(s) is s) + if has_is_interned: + self.assertIs(sys._is_interned(s), True) s2 = s.swapcase().swapcase() + if has_is_interned: + self.assertIs(sys._is_interned(s2), False) self.assertTrue(sys.intern(s2) is s) + if has_is_interned: + self.assertIs(sys._is_interned(s2), False) # Subclasses of string can't be interned, because they # provide too much opportunity for insane things to happen. @@ -707,6 +719,8 @@ def __hash__(self): return 123 self.assertRaises(TypeError, sys.intern, S("abc")) + if has_is_interned: + self.assertIs(sys._is_interned(S("abc")), False) @requires_subinterpreters def test_subinterp_intern_dynamically_allocated(self): diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-08-13-13-25-15.bpo-34392.9kIlMF.rst b/Misc/NEWS.d/next/Core and Builtins/2018-08-13-13-25-15.bpo-34392.9kIlMF.rst new file mode 100644 index 00000000000000..bc4fd1ad1f5c7c --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-08-13-13-25-15.bpo-34392.9kIlMF.rst @@ -0,0 +1 @@ +Added :func:`sys._is_interned`. diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index 98717ecc875b8b..93b8385a5b4097 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -289,6 +289,40 @@ sys_intern(PyObject *module, PyObject *arg) return return_value; } +PyDoc_STRVAR(sys__is_interned__doc__, +"_is_interned($module, string, /)\n" +"--\n" +"\n" +"Return True if the given string is \"interned\"."); + +#define SYS__IS_INTERNED_METHODDEF \ + {"_is_interned", (PyCFunction)sys__is_interned, METH_O, sys__is_interned__doc__}, + +static int +sys__is_interned_impl(PyObject *module, PyObject *string); + +static PyObject * +sys__is_interned(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *string; + int _return_value; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("_is_interned", "argument", "str", arg); + goto exit; + } + string = arg; + _return_value = sys__is_interned_impl(module, string); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); + +exit: + return return_value; +} + PyDoc_STRVAR(sys__settraceallthreads__doc__, "_settraceallthreads($module, arg, /)\n" "--\n" @@ -1452,4 +1486,4 @@ sys__get_cpu_count_config(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=f36d45c829250775 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3dc3b2cb0ce38ebb input=a9049054013a1b77]*/ diff --git a/Python/sysmodule.c b/Python/sysmodule.c index c17de44731b703..46878c7c9687f5 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -989,6 +989,23 @@ sys_intern_impl(PyObject *module, PyObject *s) } +/*[clinic input] +sys._is_interned -> bool + + string: unicode + / + +Return True if the given string is "interned". +[clinic start generated code]*/ + +static int +sys__is_interned_impl(PyObject *module, PyObject *string) +/*[clinic end generated code: output=c3678267b4e9d7ed input=039843e17883b606]*/ +{ + return PyUnicode_CHECK_INTERNED(string); +} + + /* * Cached interned string objects used for calling the profile and * trace functions. @@ -2462,6 +2479,7 @@ static PyMethodDef sys_methods[] = { SYS_GETWINDOWSVERSION_METHODDEF SYS__ENABLELEGACYWINDOWSFSENCODING_METHODDEF SYS_INTERN_METHODDEF + SYS__IS_INTERNED_METHODDEF SYS_IS_FINALIZING_METHODDEF SYS_MDEBUG_METHODDEF SYS_SETSWITCHINTERVAL_METHODDEF From a74902a14cdc0952abf7bfabcf529c9b132c5cce Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 4 Dec 2023 11:42:58 +0100 Subject: [PATCH 157/228] gh-106550: Fix sign conversion in pycore_code.h (#112613) Fix sign conversion in pycore_code.h: use unsigned integers and cast explicitly when needed. --- Include/internal/pycore_code.h | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index eaf84a9c94fc9b..73df6c3568ffe0 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -394,27 +394,29 @@ write_varint(uint8_t *ptr, unsigned int val) val >>= 6; written++; } - *ptr = val; + *ptr = (uint8_t)val; return written; } static inline int write_signed_varint(uint8_t *ptr, int val) { + unsigned int uval; if (val < 0) { - val = ((-val)<<1) | 1; + // (unsigned int)(-val) has an undefined behavior for INT_MIN + uval = ((0 - (unsigned int)val) << 1) | 1; } else { - val = val << 1; + uval = (unsigned int)val << 1; } - return write_varint(ptr, val); + return write_varint(ptr, uval); } static inline int write_location_entry_start(uint8_t *ptr, int code, int length) { assert((code & 15) == code); - *ptr = 128 | (code << 3) | (length - 1); + *ptr = 128 | (uint8_t)(code << 3) | (uint8_t)(length - 1); return 1; } @@ -454,9 +456,9 @@ write_location_entry_start(uint8_t *ptr, int code, int length) static inline uint16_t -adaptive_counter_bits(int value, int backoff) { - return (value << ADAPTIVE_BACKOFF_BITS) | - (backoff & ((1< MAX_BACKOFF_VALUE) { backoff = MAX_BACKOFF_VALUE; } - unsigned int value = (1 << backoff) - 1; + uint16_t value = (uint16_t)(1 << backoff) - 1; return adaptive_counter_bits(value, backoff); } From cda737924fd616c4e08027888258f97e81f34447 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 4 Dec 2023 11:05:20 +0000 Subject: [PATCH 158/228] gh-101100: Fix Sphinx nitpicks in `library/functions.rst` (#112669) --- Doc/library/functions.rst | 113 ++++++++++++++++++++++---------------- Doc/tools/.nitignore | 1 - 2 files changed, 67 insertions(+), 47 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index b2dd32f925ef4d..c731b6fd333275 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -57,7 +57,8 @@ are always available. They are listed here in alphabetical order. .. function:: abs(x) Return the absolute value of a number. The argument may be an - integer, a floating point number, or an object implementing :meth:`__abs__`. + integer, a floating point number, or an object implementing + :meth:`~object.__abs__`. If the argument is a complex number, its magnitude is returned. @@ -235,7 +236,7 @@ are always available. They are listed here in alphabetical order. :const:`False` if not. If this returns ``True``, it is still possible that a call fails, but if it is ``False``, calling *object* will never succeed. Note that classes are callable (calling a class returns a new instance); - instances are callable if their class has a :meth:`__call__` method. + instances are callable if their class has a :meth:`~object.__call__` method. .. versionadded:: 3.2 This function was first removed in Python 3.0 and then brought back @@ -432,15 +433,18 @@ are always available. They are listed here in alphabetical order. Without arguments, return the list of names in the current local scope. With an argument, attempt to return a list of valid attributes for that object. - If the object has a method named :meth:`__dir__`, this method will be called and + If the object has a method named :meth:`~object.__dir__`, + this method will be called and must return the list of attributes. This allows objects that implement a custom - :func:`__getattr__` or :func:`__getattribute__` function to customize the way + :func:`~object.__getattr__` or :func:`~object.__getattribute__` function + to customize the way :func:`dir` reports their attributes. - If the object does not provide :meth:`__dir__`, the function tries its best to - gather information from the object's :attr:`~object.__dict__` attribute, if defined, and + If the object does not provide :meth:`~object.__dir__`, + the function tries its best to gather information from the object's + :attr:`~object.__dict__` attribute, if defined, and from its type object. The resulting list is not necessarily complete and may - be inaccurate when the object has a custom :func:`__getattr__`. + be inaccurate when the object has a custom :func:`~object.__getattr__`. The default :func:`dir` mechanism behaves differently with different types of objects, as it attempts to produce the most relevant, rather than complete, @@ -664,7 +668,7 @@ are always available. They are listed here in alphabetical order. sign: "+" | "-" infinity: "Infinity" | "inf" nan: "nan" - digitpart: `digit` (["_"] `digit`)* + digitpart: `!digit` (["_"] `!digit`)* number: [`digitpart`] "." `digitpart` | `digitpart` ["."] exponent: ("e" | "E") ["+" | "-"] `digitpart` floatnumber: number [`exponent`] @@ -727,8 +731,8 @@ are always available. They are listed here in alphabetical order. A call to ``format(value, format_spec)`` is translated to ``type(value).__format__(value, format_spec)`` which bypasses the instance - dictionary when searching for the value's :meth:`__format__` method. A - :exc:`TypeError` exception is raised if the method search reaches + dictionary when searching for the value's :meth:`~object.__format__` method. + A :exc:`TypeError` exception is raised if the method search reaches :mod:`object` and the *format_spec* is non-empty, or if either the *format_spec* or the return value are not strings. @@ -792,9 +796,9 @@ are always available. They are listed here in alphabetical order. .. note:: - For objects with custom :meth:`__hash__` methods, note that :func:`hash` + For objects with custom :meth:`~object.__hash__` methods, + note that :func:`hash` truncates the return value based on the bit width of the host machine. - See :meth:`__hash__ ` for details. .. function:: help() help(request) @@ -982,7 +986,8 @@ are always available. They are listed here in alphabetical order. Return an :term:`iterator` object. The first argument is interpreted very differently depending on the presence of the second argument. Without a second argument, *object* must be a collection object which supports the - :term:`iterable` protocol (the :meth:`__iter__` method), or it must support + :term:`iterable` protocol (the :meth:`~object.__iter__` method), + or it must support the sequence protocol (the :meth:`~object.__getitem__` method with integer arguments starting at ``0``). If it does not support either of those protocols, :exc:`TypeError` is raised. If the second argument, *sentinel*, is given, @@ -1500,38 +1505,44 @@ are always available. They are listed here in alphabetical order. """Get the current voltage.""" return self._voltage - The ``@property`` decorator turns the :meth:`voltage` method into a "getter" + The ``@property`` decorator turns the :meth:`!voltage` method into a "getter" for a read-only attribute with the same name, and it sets the docstring for *voltage* to "Get the current voltage." - A property object has :attr:`~property.getter`, :attr:`~property.setter`, - and :attr:`~property.deleter` methods usable as decorators that create a - copy of the property with the corresponding accessor function set to the - decorated function. This is best explained with an example:: + .. decorator:: property.getter + .. decorator:: property.setter + .. decorator:: property.deleter - class C: - def __init__(self): - self._x = None + A property object has ``getter``, ``setter``, + and ``deleter`` methods usable as decorators that create a + copy of the property with the corresponding accessor function set to the + decorated function. This is best explained with an example: - @property - def x(self): - """I'm the 'x' property.""" - return self._x + .. testcode:: - @x.setter - def x(self, value): - self._x = value + class C: + def __init__(self): + self._x = None - @x.deleter - def x(self): - del self._x + @property + def x(self): + """I'm the 'x' property.""" + return self._x - This code is exactly equivalent to the first example. Be sure to give the - additional functions the same name as the original property (``x`` in this - case.) + @x.setter + def x(self, value): + self._x = value - The returned property object also has the attributes ``fget``, ``fset``, and - ``fdel`` corresponding to the constructor arguments. + @x.deleter + def x(self): + del self._x + + This code is exactly equivalent to the first example. Be sure to give the + additional functions the same name as the original property (``x`` in this + case.) + + The returned property object also has the attributes ``fget``, ``fset``, and + ``fdel`` corresponding to the constructor arguments. .. versionchanged:: 3.5 The docstrings of property objects are now writeable. @@ -1554,7 +1565,8 @@ are always available. They are listed here in alphabetical order. representation is a string enclosed in angle brackets that contains the name of the type of the object together with additional information often including the name and address of the object. A class can control what this - function returns for its instances by defining a :meth:`__repr__` method. + function returns for its instances + by defining a :meth:`~object.__repr__` method. If :func:`sys.displayhook` is not accessible, this function will raise :exc:`RuntimeError`. @@ -1562,9 +1574,9 @@ are always available. They are listed here in alphabetical order. .. function:: reversed(seq) Return a reverse :term:`iterator`. *seq* must be an object which has - a :meth:`__reversed__` method or supports the sequence protocol (the - :meth:`__len__` method and the :meth:`~object.__getitem__` method with integer - arguments starting at ``0``). + a :meth:`~object.__reversed__` method or supports the sequence protocol (the + :meth:`~object.__len__` method and the :meth:`~object.__getitem__` method + with integer arguments starting at ``0``). .. function:: round(number, ndigits=None) @@ -1635,13 +1647,21 @@ are always available. They are listed here in alphabetical order. Return a :term:`slice` object representing the set of indices specified by ``range(start, stop, step)``. The *start* and *step* arguments default to - ``None``. Slice objects have read-only data attributes :attr:`~slice.start`, - :attr:`~slice.stop`, and :attr:`~slice.step` which merely return the argument - values (or their default). They have no other explicit functionality; - however, they are used by NumPy and other third-party packages. + ``None``. + + .. attribute:: slice.start + .. attribute:: slice.stop + .. attribute:: slice.step + + Slice objects have read-only data attributes :attr:`!start`, + :attr:`!stop`, and :attr:`!step` which merely return the argument + values (or their default). They have no other explicit functionality; + however, they are used by NumPy and other third-party packages. + Slice objects are also generated when extended indexing syntax is used. For example: ``a[start:stop:step]`` or ``a[start:stop, i]``. See - :func:`itertools.islice` for an alternate version that returns an iterator. + :func:`itertools.islice` for an alternate version that returns an + :term:`iterator`. .. versionchanged:: 3.12 Slice objects are now :term:`hashable` (provided :attr:`~slice.start`, @@ -1808,7 +1828,8 @@ are always available. They are listed here in alphabetical order. Note that :func:`super` is implemented as part of the binding process for explicit dotted attribute lookups such as ``super().__getitem__(name)``. - It does so by implementing its own :meth:`__getattribute__` method for searching + It does so by implementing its own :meth:`~object.__getattribute__` method + for searching classes in a predictable order that supports cooperative multiple inheritance. Accordingly, :func:`super` is undefined for implicit lookups using statements or operators such as ``super()[name]``. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 8d79a848b7cc7b..e79b4c3bdecb67 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -56,7 +56,6 @@ Doc/library/exceptions.rst Doc/library/faulthandler.rst Doc/library/fcntl.rst Doc/library/ftplib.rst -Doc/library/functions.rst Doc/library/functools.rst Doc/library/http.client.rst Doc/library/http.cookiejar.rst From da6760bdf5ed8ede203618d5118f4ceb2cb1652d Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 4 Dec 2023 13:14:56 +0200 Subject: [PATCH 159/228] gh-65210: Add const qualifiers in PyArg_VaParseTupleAndKeywords() (GH-105958) Change the declaration of the keywords parameter in functions PyArg_ParseTupleAndKeywords() and PyArg_VaParseTupleAndKeywords() from `char **` to `char * const *` in C and `const char * const *` in C++. It makes these functions compatible with argument of type `const char * const *`, `const char **` or `char * const *` in C++ and `char * const *` in C without explicit type cast. Co-authored-by: C.A.M. Gerlach --- Doc/c-api/arg.rst | 26 +++++++++++++++++-- Doc/extending/extending.rst | 2 +- Doc/whatsnew/3.13.rst | 10 +++++++ Include/modsupport.h | 4 +-- Include/pyport.h | 8 ++++++ ...3-06-21-11-53-09.gh-issue-65210.PhFRBJ.rst | 3 +++ Python/getargs.c | 17 ++++++------ 7 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2023-06-21-11-53-09.gh-issue-65210.PhFRBJ.rst diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 62d87d898e682c..834aae9372fe3b 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -413,7 +413,7 @@ API Functions than a variable number of arguments. -.. c:function:: int PyArg_ParseTupleAndKeywords(PyObject *args, PyObject *kw, const char *format, char *keywords[], ...) +.. c:function:: int PyArg_ParseTupleAndKeywords(PyObject *args, PyObject *kw, const char *format, char * const *keywords, ...) Parse the parameters of a function that takes both positional and keyword parameters into local variables. @@ -424,15 +424,24 @@ API Functions Returns true on success; on failure, it returns false and raises the appropriate exception. + .. note:: + + The *keywords* parameter declaration is :c:expr:`char * const *` in C and + :c:expr:`const char * const *` in C++. + This can be overridden with the :c:macro:`PY_CXX_CONST` macro. + .. versionchanged:: 3.6 Added support for :ref:`positional-only parameters `. .. versionchanged:: 3.13 + The *keywords* parameter has now type :c:expr:`char * const *` in C and + :c:expr:`const char * const *` in C++, instead of :c:expr:`char **`. Added support for non-ASCII keyword parameter names. -.. c:function:: int PyArg_VaParseTupleAndKeywords(PyObject *args, PyObject *kw, const char *format, char *keywords[], va_list vargs) + +.. c:function:: int PyArg_VaParseTupleAndKeywords(PyObject *args, PyObject *kw, const char *format, char * const *keywords, va_list vargs) Identical to :c:func:`PyArg_ParseTupleAndKeywords`, except that it accepts a va_list rather than a variable number of arguments. @@ -505,6 +514,19 @@ API Functions PyArg_ParseTuple(args, "O|O:ref", &object, &callback) +.. c:macro:: PY_CXX_CONST + + The value to be inserted, if any, before :c:expr:`char * const *` + in the *keywords* parameter declaration of + :c:func:`PyArg_ParseTupleAndKeywords` and + :c:func:`PyArg_VaParseTupleAndKeywords`. + Default empty for C and ``const`` for C++ + (:c:expr:`const char * const *`). + To override, define it to the desired value before including + :file:`Python.h`. + + .. versionadded:: 3.13 + --------------- Building values diff --git a/Doc/extending/extending.rst b/Doc/extending/extending.rst index 1ee7f28b2ba220..745fc10a22d161 100644 --- a/Doc/extending/extending.rst +++ b/Doc/extending/extending.rst @@ -735,7 +735,7 @@ Keyword Parameters for Extension Functions The :c:func:`PyArg_ParseTupleAndKeywords` function is declared as follows:: int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict, - const char *format, char *kwlist[], ...); + const char *format, char * const *kwlist, ...); The *arg* and *format* parameters are identical to those of the :c:func:`PyArg_ParseTuple` function. The *kwdict* parameter is the dictionary of diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 676305c6e1d06a..be890ff314dfa4 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1111,6 +1111,16 @@ New Features APIs accepting the format codes always use ``Py_ssize_t`` for ``#`` formats. (Contributed by Inada Naoki in :gh:`104922`.) +* The *keywords* parameter of :c:func:`PyArg_ParseTupleAndKeywords` and + :c:func:`PyArg_VaParseTupleAndKeywords` has now type :c:expr:`char * const *` + in C and :c:expr:`const char * const *` in C++, instead of :c:expr:`char **`. + It makes these functions compatible with arguments of type + :c:expr:`const char * const *`, :c:expr:`const char **` or + :c:expr:`char * const *` in C++ and :c:expr:`char * const *` in C + without an explicit type cast. + This can be overridden with the :c:macro:`PY_CXX_CONST` macro. + (Contributed by Serhiy Storchaka in :gh:`65210`.) + * Add :c:func:`PyImport_AddModuleRef`: similar to :c:func:`PyImport_AddModule`, but return a :term:`strong reference` instead of a :term:`borrowed reference`. diff --git a/Include/modsupport.h b/Include/modsupport.h index 450cc99c404c17..ea4c0fce9f4562 100644 --- a/Include/modsupport.h +++ b/Include/modsupport.h @@ -9,10 +9,10 @@ extern "C" { PyAPI_FUNC(int) PyArg_Parse(PyObject *, const char *, ...); PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...); PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *, - const char *, char **, ...); + const char *, PY_CXX_CONST char * const *, ...); PyAPI_FUNC(int) PyArg_VaParse(PyObject *, const char *, va_list); PyAPI_FUNC(int) PyArg_VaParseTupleAndKeywords(PyObject *, PyObject *, - const char *, char **, va_list); + const char *, PY_CXX_CONST char * const *, va_list); PyAPI_FUNC(int) PyArg_ValidateKeywordArguments(PyObject *); PyAPI_FUNC(int) PyArg_UnpackTuple(PyObject *, const char *, Py_ssize_t, Py_ssize_t, ...); diff --git a/Include/pyport.h b/Include/pyport.h index abb526d503fddd..328471085f959d 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -586,6 +586,14 @@ extern "C" { # define ALIGNOF_MAX_ALIGN_T _Alignof(long double) #endif +#ifndef PY_CXX_CONST +# ifdef __cplusplus +# define PY_CXX_CONST const +# else +# define PY_CXX_CONST +# endif +#endif + #if defined(__sgi) && !defined(_SGI_MP_SOURCE) # define _SGI_MP_SOURCE #endif diff --git a/Misc/NEWS.d/next/C API/2023-06-21-11-53-09.gh-issue-65210.PhFRBJ.rst b/Misc/NEWS.d/next/C API/2023-06-21-11-53-09.gh-issue-65210.PhFRBJ.rst new file mode 100644 index 00000000000000..a15646f4dad127 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-06-21-11-53-09.gh-issue-65210.PhFRBJ.rst @@ -0,0 +1,3 @@ +Change the declaration of the *keywords* parameter of +:c:func:`PyArg_ParseTupleAndKeywords` and +:c:func:`PyArg_VaParseTupleAndKeywords` for better compatibility with C++. diff --git a/Python/getargs.c b/Python/getargs.c index 5ff8f7473609f5..0c4ce282f48764 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -1,6 +1,7 @@ /* New getargs implementation */ +#define PY_CXX_CONST const #include "Python.h" #include "pycore_abstract.h" // _PyNumber_Index() #include "pycore_dict.h" // _PyDict_HasOnlyStringKeys() @@ -12,10 +13,10 @@ PyAPI_FUNC(int) _PyArg_Parse_SizeT(PyObject *, const char *, ...); PyAPI_FUNC(int) _PyArg_ParseTuple_SizeT(PyObject *, const char *, ...); PyAPI_FUNC(int) _PyArg_ParseTupleAndKeywords_SizeT(PyObject *, PyObject *, - const char *, char **, ...); + const char *, const char * const *, ...); PyAPI_FUNC(int) _PyArg_VaParse_SizeT(PyObject *, const char *, va_list); PyAPI_FUNC(int) _PyArg_VaParseTupleAndKeywords_SizeT(PyObject *, PyObject *, - const char *, char **, va_list); + const char *, const char * const *, va_list); #define FLAG_COMPAT 1 @@ -54,7 +55,7 @@ static Py_ssize_t convertbuffer(PyObject *, const void **p, const char **); static int getbuffer(PyObject *, Py_buffer *, const char**); static int vgetargskeywords(PyObject *, PyObject *, - const char *, char **, va_list *, int); + const char *, const char * const *, va_list *, int); static int vgetargskeywordsfast(PyObject *, PyObject *, struct _PyArg_Parser *, va_list *, int); static int vgetargskeywordsfast_impl(PyObject *const *args, Py_ssize_t nargs, @@ -1247,7 +1248,7 @@ int PyArg_ParseTupleAndKeywords(PyObject *args, PyObject *keywords, const char *format, - char **kwlist, ...) + const char * const *kwlist, ...) { int retval; va_list va; @@ -1271,7 +1272,7 @@ int _PyArg_ParseTupleAndKeywords_SizeT(PyObject *args, PyObject *keywords, const char *format, - char **kwlist, ...) + const char * const *kwlist, ...) { int retval; va_list va; @@ -1297,7 +1298,7 @@ int PyArg_VaParseTupleAndKeywords(PyObject *args, PyObject *keywords, const char *format, - char **kwlist, va_list va) + const char * const *kwlist, va_list va) { int retval; va_list lva; @@ -1322,7 +1323,7 @@ int _PyArg_VaParseTupleAndKeywords_SizeT(PyObject *args, PyObject *keywords, const char *format, - char **kwlist, va_list va) + const char * const *kwlist, va_list va) { int retval; va_list lva; @@ -1460,7 +1461,7 @@ PyArg_ValidateKeywordArguments(PyObject *kwargs) static int vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, - char **kwlist, va_list *p_va, int flags) + const char * const *kwlist, va_list *p_va, int flags) { char msgbuf[512]; int levels[32]; From c74e9fb18917ceb287c3ed5be5d0c2a16a646a99 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 4 Dec 2023 13:30:32 +0200 Subject: [PATCH 160/228] gh-110275: Named tuple's __replace__() now raises TypeError for invalid arguments (GH-110299) --- Doc/library/collections.rst | 4 ++++ Lib/collections/__init__.py | 2 +- Lib/test/test_collections.py | 6 +----- Lib/test/test_copy.py | 2 +- .../Library/2023-11-08-16-11-04.gh-issue-110275.Bm6GwR.rst | 2 ++ 5 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-08-16-11-04.gh-issue-110275.Bm6GwR.rst diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst index 17dd6da7479e50..233b2c6a771f4a 100644 --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -981,6 +981,10 @@ field names, the method and attribute names start with an underscore. Named tuples are also supported by generic function :func:`copy.replace`. + .. versionchanged:: 3.13 + Raise :exc:`TypeError` instead of :exc:`ValueError` for invalid + keyword arguments. + .. attribute:: somenamedtuple._fields Tuple of strings listing the field names. Useful for introspection diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index a461550ea40da7..2e527dfd810c43 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -457,7 +457,7 @@ def _make(cls, iterable): def _replace(self, /, **kwds): result = self._make(_map(kwds.pop, field_names, self)) if kwds: - raise ValueError(f'Got unexpected field names: {list(kwds)!r}') + raise TypeError(f'Got unexpected field names: {list(kwds)!r}') return result _replace.__doc__ = (f'Return a new {typename} object replacing specified ' diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index bb8b352518ef3e..7e6f811e17cfa2 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -488,12 +488,8 @@ def test_instance(self): self.assertEqual(p._replace(x=1), (1, 22)) # test _replace method self.assertEqual(p._asdict(), dict(x=11, y=22)) # test _asdict method - try: + with self.assertRaises(TypeError): p._replace(x=1, error=2) - except ValueError: - pass - else: - self._fail('Did not detect an incorrect fieldname') # verify that field string can have commas Point = namedtuple('Point', 'x, y') diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py index 60735ba89a80ee..89102373759ca0 100644 --- a/Lib/test/test_copy.py +++ b/Lib/test/test_copy.py @@ -952,7 +952,7 @@ class PointFromClass(NamedTuple): self.assertEqual(copy.replace(p, x=1), (1, 22)) self.assertEqual(copy.replace(p, y=2), (11, 2)) self.assertEqual(copy.replace(p, x=1, y=2), (1, 2)) - with self.assertRaisesRegex(ValueError, 'unexpected field name'): + with self.assertRaisesRegex(TypeError, 'unexpected field name'): copy.replace(p, x=1, error=2) def test_dataclass(self): diff --git a/Misc/NEWS.d/next/Library/2023-11-08-16-11-04.gh-issue-110275.Bm6GwR.rst b/Misc/NEWS.d/next/Library/2023-11-08-16-11-04.gh-issue-110275.Bm6GwR.rst new file mode 100644 index 00000000000000..194dd5cb623f0f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-08-16-11-04.gh-issue-110275.Bm6GwR.rst @@ -0,0 +1,2 @@ +Named tuple's methods ``_replace()`` and ``__replace__()`` now raise +TypeError instead of ValueError for invalid keyword arguments. From 6ca9d3e0173c38e2eac50367b187d4c1d43f9892 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 4 Dec 2023 13:47:55 +0200 Subject: [PATCH 161/228] gh-109786: Fix leaks and crash when re-enter itertools.pairwise.__next__() (GH-109788) --- Lib/test/test_itertools.py | 72 +++++++++++++++++++ ...-09-23-14-40-51.gh-issue-109786.UX3pKv.rst | 2 + Modules/itertoolsmodule.c | 13 +++- 3 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-09-23-14-40-51.gh-issue-109786.UX3pKv.rst diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index 512745e45350d1..705e880d98685e 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -1152,6 +1152,78 @@ def test_pairwise(self): with self.assertRaises(TypeError): pairwise(None) # non-iterable argument + def test_pairwise_reenter(self): + def check(reenter_at, expected): + class I: + count = 0 + def __iter__(self): + return self + def __next__(self): + self.count +=1 + if self.count in reenter_at: + return next(it) + return [self.count] # new object + + it = pairwise(I()) + for item in expected: + self.assertEqual(next(it), item) + + check({1}, [ + (([2], [3]), [4]), + ([4], [5]), + ]) + check({2}, [ + ([1], ([1], [3])), + (([1], [3]), [4]), + ([4], [5]), + ]) + check({3}, [ + ([1], [2]), + ([2], ([2], [4])), + (([2], [4]), [5]), + ([5], [6]), + ]) + check({1, 2}, [ + ((([3], [4]), [5]), [6]), + ([6], [7]), + ]) + check({1, 3}, [ + (([2], ([2], [4])), [5]), + ([5], [6]), + ]) + check({1, 4}, [ + (([2], [3]), (([2], [3]), [5])), + ((([2], [3]), [5]), [6]), + ([6], [7]), + ]) + check({2, 3}, [ + ([1], ([1], ([1], [4]))), + (([1], ([1], [4])), [5]), + ([5], [6]), + ]) + + def test_pairwise_reenter2(self): + def check(maxcount, expected): + class I: + count = 0 + def __iter__(self): + return self + def __next__(self): + if self.count >= maxcount: + raise StopIteration + self.count +=1 + if self.count == 1: + return next(it, None) + return [self.count] # new object + + it = pairwise(I()) + self.assertEqual(list(it), expected) + + check(1, []) + check(2, []) + check(3, []) + check(4, [(([2], [3]), [4])]) + def test_product(self): for args, result in [ ([], [()]), # zero iterables diff --git a/Misc/NEWS.d/next/Library/2023-09-23-14-40-51.gh-issue-109786.UX3pKv.rst b/Misc/NEWS.d/next/Library/2023-09-23-14-40-51.gh-issue-109786.UX3pKv.rst new file mode 100644 index 00000000000000..07222fa339d703 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-23-14-40-51.gh-issue-109786.UX3pKv.rst @@ -0,0 +1,2 @@ +Fix possible reference leaks and crash when re-enter the ``__next__()`` method of +:class:`itertools.pairwise`. diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 4ed34aa5bde827..ab99fa4d873bf5 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -330,21 +330,30 @@ pairwise_next(pairwiseobject *po) return NULL; } if (old == NULL) { - po->old = old = (*Py_TYPE(it)->tp_iternext)(it); + old = (*Py_TYPE(it)->tp_iternext)(it); + Py_XSETREF(po->old, old); if (old == NULL) { Py_CLEAR(po->it); return NULL; } + it = po->it; + if (it == NULL) { + Py_CLEAR(po->old); + return NULL; + } } + Py_INCREF(old); new = (*Py_TYPE(it)->tp_iternext)(it); if (new == NULL) { Py_CLEAR(po->it); Py_CLEAR(po->old); + Py_DECREF(old); return NULL; } /* Future optimization: Reuse the result tuple as we do in enumerate() */ result = PyTuple_Pack(2, old, new); - Py_SETREF(po->old, new); + Py_XSETREF(po->old, new); + Py_DECREF(old); return result; } From 9560e0d6d7a316341939b4016e47e03bd5bf17c3 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 4 Dec 2023 12:42:24 +0000 Subject: [PATCH 162/228] gh-101100: Fix Sphinx nitpicks in `library/abc.rst` (#112703) --- Doc/library/abc.rst | 42 +++++++++++++++++++++--------------------- Doc/tools/.nitignore | 1 - 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/Doc/library/abc.rst b/Doc/library/abc.rst index fb4f9da169c5ab..c073ea955abaa4 100644 --- a/Doc/library/abc.rst +++ b/Doc/library/abc.rst @@ -21,7 +21,7 @@ The :mod:`collections` module has some concrete classes that derive from ABCs; these can, of course, be further derived. In addition, the :mod:`collections.abc` submodule has some ABCs that can be used to test whether a class or instance provides a particular interface, for example, if it is -:term:`hashable` or if it is a mapping. +:term:`hashable` or if it is a :term:`mapping`. This module provides the metaclass :class:`ABCMeta` for defining ABCs and @@ -30,7 +30,7 @@ a helper class :class:`ABC` to alternatively define ABCs through inheritance: .. class:: ABC A helper class that has :class:`ABCMeta` as its metaclass. With this class, - an abstract base class can be created by simply deriving from :class:`ABC` + an abstract base class can be created by simply deriving from :class:`!ABC` avoiding sometimes confusing metaclass usage, for example:: from abc import ABC @@ -38,11 +38,11 @@ a helper class :class:`ABC` to alternatively define ABCs through inheritance: class MyABC(ABC): pass - Note that the type of :class:`ABC` is still :class:`ABCMeta`, therefore - inheriting from :class:`ABC` requires the usual precautions regarding + Note that the type of :class:`!ABC` is still :class:`ABCMeta`, therefore + inheriting from :class:`!ABC` requires the usual precautions regarding metaclass usage, as multiple inheritance may lead to metaclass conflicts. One may also define an abstract base class by passing the metaclass - keyword and using :class:`ABCMeta` directly, for example:: + keyword and using :class:`!ABCMeta` directly, for example:: from abc import ABCMeta @@ -65,7 +65,7 @@ a helper class :class:`ABC` to alternatively define ABCs through inheritance: implementations defined by the registering ABC be callable (not even via :func:`super`). [#]_ - Classes created with a metaclass of :class:`ABCMeta` have the following method: + Classes created with a metaclass of :class:`!ABCMeta` have the following method: .. method:: register(subclass) @@ -86,7 +86,7 @@ a helper class :class:`ABC` to alternatively define ABCs through inheritance: Returns the registered subclass, to allow usage as a class decorator. .. versionchanged:: 3.4 - To detect calls to :meth:`register`, you can use the + To detect calls to :meth:`!register`, you can use the :func:`get_cache_token` function. You can also override this method in an abstract base class: @@ -96,10 +96,10 @@ a helper class :class:`ABC` to alternatively define ABCs through inheritance: (Must be defined as a class method.) Check whether *subclass* is considered a subclass of this ABC. This means - that you can customize the behavior of ``issubclass`` further without the + that you can customize the behavior of :func:`issubclass` further without the need to call :meth:`register` on every class you want to consider a subclass of the ABC. (This class method is called from the - :meth:`__subclasscheck__` method of the ABC.) + :meth:`~class.__subclasscheck__` method of the ABC.) This method should return ``True``, ``False`` or ``NotImplemented``. If it returns ``True``, the *subclass* is considered a subclass of this ABC. @@ -142,7 +142,7 @@ a helper class :class:`ABC` to alternatively define ABCs through inheritance: The ABC ``MyIterable`` defines the standard iterable method, :meth:`~iterator.__iter__`, as an abstract method. The implementation given - here can still be called from subclasses. The :meth:`get_iterator` method + here can still be called from subclasses. The :meth:`!get_iterator` method is also part of the ``MyIterable`` abstract base class, but it does not have to be overridden in non-abstract derived classes. @@ -153,14 +153,14 @@ a helper class :class:`ABC` to alternatively define ABCs through inheritance: Finally, the last line makes ``Foo`` a virtual subclass of ``MyIterable``, even though it does not define an :meth:`~iterator.__iter__` method (it uses - the old-style iterable protocol, defined in terms of :meth:`__len__` and + the old-style iterable protocol, defined in terms of :meth:`~object.__len__` and :meth:`~object.__getitem__`). Note that this will not make ``get_iterator`` available as a method of ``Foo``, so it is provided separately. -The :mod:`abc` module also provides the following decorator: +The :mod:`!abc` module also provides the following decorator: .. decorator:: abstractmethod @@ -168,19 +168,19 @@ The :mod:`abc` module also provides the following decorator: Using this decorator requires that the class's metaclass is :class:`ABCMeta` or is derived from it. A class that has a metaclass derived from - :class:`ABCMeta` cannot be instantiated unless all of its abstract methods + :class:`!ABCMeta` cannot be instantiated unless all of its abstract methods and properties are overridden. The abstract methods can be called using any - of the normal 'super' call mechanisms. :func:`abstractmethod` may be used + of the normal 'super' call mechanisms. :func:`!abstractmethod` may be used to declare abstract methods for properties and descriptors. Dynamically adding abstract methods to a class, or attempting to modify the abstraction status of a method or class once it is created, are only supported using the :func:`update_abstractmethods` function. The - :func:`abstractmethod` only affects subclasses derived using regular - inheritance; "virtual subclasses" registered with the ABC's :meth:`register` - method are not affected. + :func:`!abstractmethod` only affects subclasses derived using regular + inheritance; "virtual subclasses" registered with the ABC's + :meth:`~ABCMeta.register` method are not affected. - When :func:`abstractmethod` is applied in combination with other method + When :func:`!abstractmethod` is applied in combination with other method descriptors, it should be applied as the innermost decorator, as shown in the following usage examples:: @@ -216,7 +216,7 @@ The :mod:`abc` module also provides the following decorator: In order to correctly interoperate with the abstract base class machinery, the descriptor must identify itself as abstract using - :attr:`__isabstractmethod__`. In general, this attribute should be ``True`` + :attr:`!__isabstractmethod__`. In general, this attribute should be ``True`` if any of the methods used to compose the descriptor are abstract. For example, Python's built-in :class:`property` does the equivalent of:: @@ -236,7 +236,7 @@ The :mod:`abc` module also provides the following decorator: super-call in a framework that uses cooperative multiple-inheritance. -The :mod:`abc` module also supports the following legacy decorators: +The :mod:`!abc` module also supports the following legacy decorators: .. decorator:: abstractclassmethod @@ -323,7 +323,7 @@ The :mod:`abc` module also supports the following legacy decorators: ... -The :mod:`abc` module also provides the following functions: +The :mod:`!abc` module also provides the following functions: .. function:: get_cache_token() diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index e79b4c3bdecb67..50f04d72c0dee0 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -26,7 +26,6 @@ Doc/howto/enum.rst Doc/howto/isolating-extensions.rst Doc/howto/logging.rst Doc/howto/urllib2.rst -Doc/library/abc.rst Doc/library/ast.rst Doc/library/asyncio-extending.rst Doc/library/asyncio-policy.rst From c718ab92a584fa658b6a626a744f5a71a048b47c Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 4 Dec 2023 15:41:41 +0000 Subject: [PATCH 163/228] gh-74690: Optimise `isinstance()` and `issubclass()` calls against runtime-checkable protocols by avoiding costly `super()` calls (#112708) --- Lib/typing.py | 14 +++++++++++--- .../2023-12-04-14-05-24.gh-issue-74690.eODKRm.rst | 5 +++++ 2 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-12-04-14-05-24.gh-issue-74690.eODKRm.rst diff --git a/Lib/typing.py b/Lib/typing.py index 4c19aadabe3b87..aa64ed93f76fbf 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1782,6 +1782,14 @@ def _pickle_pskwargs(pskwargs): del _pickle_psargs, _pickle_pskwargs +# Preload these once, as globals, as a micro-optimisation. +# This makes a significant difference to the time it takes +# to do `isinstance()`/`issubclass()` checks +# against runtime-checkable protocols with only one callable member. +_abc_instancecheck = ABCMeta.__instancecheck__ +_abc_subclasscheck = ABCMeta.__subclasscheck__ + + class _ProtocolMeta(ABCMeta): # This metaclass is somewhat unfortunate, # but is necessary for several reasons... @@ -1841,7 +1849,7 @@ def __subclasscheck__(cls, other): "Instance and class checks can only be used with " "@runtime_checkable protocols" ) - return super().__subclasscheck__(other) + return _abc_subclasscheck(cls, other) def __instancecheck__(cls, instance): # We need this method for situations where attributes are @@ -1850,7 +1858,7 @@ def __instancecheck__(cls, instance): return type.__instancecheck__(cls, instance) if not getattr(cls, "_is_protocol", False): # i.e., it's a concrete subclass of a protocol - return super().__instancecheck__(instance) + return _abc_instancecheck(cls, instance) if ( not getattr(cls, '_is_runtime_protocol', False) and @@ -1859,7 +1867,7 @@ def __instancecheck__(cls, instance): raise TypeError("Instance and class checks can only be used with" " @runtime_checkable protocols") - if super().__instancecheck__(instance): + if _abc_instancecheck(cls, instance): return True getattr_static = _lazy_load_getattr_static() diff --git a/Misc/NEWS.d/next/Library/2023-12-04-14-05-24.gh-issue-74690.eODKRm.rst b/Misc/NEWS.d/next/Library/2023-12-04-14-05-24.gh-issue-74690.eODKRm.rst new file mode 100644 index 00000000000000..36d793f787302e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-04-14-05-24.gh-issue-74690.eODKRm.rst @@ -0,0 +1,5 @@ +Speedup :func:`isinstance` checks by roughly 20% for +:func:`runtime-checkable protocols ` +that only have one callable member. +Speedup :func:`issubclass` checks for these protocols by roughly 10%. +Patch by Alex Waygood. From e08b70fab1fbc45fa498020aac522ae1d5da6136 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 4 Dec 2023 17:43:27 +0200 Subject: [PATCH 164/228] gh-108927: Fix removing testing modules from sys.modules (GH-108952) It breaks import machinery if the test module has submodules used in other tests. --- Lib/test/libregrtest/main.py | 18 +++++++++++++----- Lib/test/libregrtest/single.py | 4 ---- .../import_from_tests/test_regrtest_a.py | 11 +++++++++++ .../test_regrtest_b/__init__.py | 9 +++++++++ .../import_from_tests/test_regrtest_b/util.py | 0 .../import_from_tests/test_regrtest_c.py | 11 +++++++++++ Lib/test/test_regrtest.py | 19 +++++++++++++++++++ ...-09-05-20-46-35.gh-issue-108927.TpwWav.rst | 4 ++++ 8 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 Lib/test/regrtestdata/import_from_tests/test_regrtest_a.py create mode 100644 Lib/test/regrtestdata/import_from_tests/test_regrtest_b/__init__.py create mode 100644 Lib/test/regrtestdata/import_from_tests/test_regrtest_b/util.py create mode 100644 Lib/test/regrtestdata/import_from_tests/test_regrtest_c.py create mode 100644 Misc/NEWS.d/next/Tests/2023-09-05-20-46-35.gh-issue-108927.TpwWav.rst diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 16f6974ae32465..7ca1b1cb65ae40 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -311,7 +311,7 @@ def run_tests_sequentially(self, runtests) -> None: else: tracer = None - save_modules = sys.modules.keys() + save_modules = set(sys.modules) jobs = runtests.get_jobs() if jobs is not None: @@ -335,10 +335,18 @@ def run_tests_sequentially(self, runtests) -> None: result = self.run_test(test_name, runtests, tracer) - # Unload the newly imported modules (best effort finalization) - for module in sys.modules.keys(): - if module not in save_modules and module.startswith("test."): - support.unload(module) + # Unload the newly imported test modules (best effort finalization) + new_modules = [module for module in sys.modules + if module not in save_modules and + module.startswith(("test.", "test_"))] + for module in new_modules: + sys.modules.pop(module, None) + # Remove the attribute of the parent module. + parent, _, name = module.rpartition('.') + try: + delattr(sys.modules[parent], name) + except (KeyError, AttributeError): + pass if result.must_stop(self.fail_fast, self.fail_env_changed): break diff --git a/Lib/test/libregrtest/single.py b/Lib/test/libregrtest/single.py index eafeb5fe26f3f3..235029d8620ff5 100644 --- a/Lib/test/libregrtest/single.py +++ b/Lib/test/libregrtest/single.py @@ -122,10 +122,6 @@ def _load_run_test(result: TestResult, runtests: RunTests) -> None: # Load the test module and run the tests. test_name = result.test_name module_name = abs_module_name(test_name, runtests.test_dir) - - # Remove the module from sys.module to reload it if it was already imported - sys.modules.pop(module_name, None) - test_mod = importlib.import_module(module_name) if hasattr(test_mod, "test_main"): diff --git a/Lib/test/regrtestdata/import_from_tests/test_regrtest_a.py b/Lib/test/regrtestdata/import_from_tests/test_regrtest_a.py new file mode 100644 index 00000000000000..9c3d0c7cf4bfaa --- /dev/null +++ b/Lib/test/regrtestdata/import_from_tests/test_regrtest_a.py @@ -0,0 +1,11 @@ +import sys +import unittest +import test_regrtest_b.util + +class Test(unittest.TestCase): + def test(self): + test_regrtest_b.util # does not fail + self.assertIn('test_regrtest_a', sys.modules) + self.assertIs(sys.modules['test_regrtest_b'], test_regrtest_b) + self.assertIs(sys.modules['test_regrtest_b.util'], test_regrtest_b.util) + self.assertNotIn('test_regrtest_c', sys.modules) diff --git a/Lib/test/regrtestdata/import_from_tests/test_regrtest_b/__init__.py b/Lib/test/regrtestdata/import_from_tests/test_regrtest_b/__init__.py new file mode 100644 index 00000000000000..3dfba253455ad2 --- /dev/null +++ b/Lib/test/regrtestdata/import_from_tests/test_regrtest_b/__init__.py @@ -0,0 +1,9 @@ +import sys +import unittest + +class Test(unittest.TestCase): + def test(self): + self.assertNotIn('test_regrtest_a', sys.modules) + self.assertIn('test_regrtest_b', sys.modules) + self.assertNotIn('test_regrtest_b.util', sys.modules) + self.assertNotIn('test_regrtest_c', sys.modules) diff --git a/Lib/test/regrtestdata/import_from_tests/test_regrtest_b/util.py b/Lib/test/regrtestdata/import_from_tests/test_regrtest_b/util.py new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/Lib/test/regrtestdata/import_from_tests/test_regrtest_c.py b/Lib/test/regrtestdata/import_from_tests/test_regrtest_c.py new file mode 100644 index 00000000000000..de80769118d709 --- /dev/null +++ b/Lib/test/regrtestdata/import_from_tests/test_regrtest_c.py @@ -0,0 +1,11 @@ +import sys +import unittest +import test_regrtest_b.util + +class Test(unittest.TestCase): + def test(self): + test_regrtest_b.util # does not fail + self.assertNotIn('test_regrtest_a', sys.modules) + self.assertIs(sys.modules['test_regrtest_b'], test_regrtest_b) + self.assertIs(sys.modules['test_regrtest_b.util'], test_regrtest_b.util) + self.assertIn('test_regrtest_c', sys.modules) diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index d7b9f801092498..e828941f6c779d 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -2031,6 +2031,25 @@ def test_dev_mode(self): self.check_executed_tests(output, tests, stats=len(tests), parallel=True) + def test_unload_tests(self): + # Test that unloading test modules does not break tests + # that import from other tests. + # The test execution order matters for this test. + # Both test_regrtest_a and test_regrtest_c which are executed before + # and after test_regrtest_b import a submodule from the test_regrtest_b + # package and use it in testing. test_regrtest_b itself does not import + # that submodule. + # Previously test_regrtest_c failed because test_regrtest_b.util in + # sys.modules was left after test_regrtest_a (making the import + # statement no-op), but new test_regrtest_b without the util attribute + # was imported for test_regrtest_b. + testdir = os.path.join(os.path.dirname(__file__), + 'regrtestdata', 'import_from_tests') + tests = [f'test_regrtest_{name}' for name in ('a', 'b', 'c')] + args = ['-Wd', '-E', '-bb', '-m', 'test', '--testdir=%s' % testdir, *tests] + output = self.run_python(args) + self.check_executed_tests(output, tests, stats=3) + def check_add_python_opts(self, option): # --fast-ci and --slow-ci add "-u -W default -bb -E" options to Python code = textwrap.dedent(r""" diff --git a/Misc/NEWS.d/next/Tests/2023-09-05-20-46-35.gh-issue-108927.TpwWav.rst b/Misc/NEWS.d/next/Tests/2023-09-05-20-46-35.gh-issue-108927.TpwWav.rst new file mode 100644 index 00000000000000..b1a78370afedb2 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-05-20-46-35.gh-issue-108927.TpwWav.rst @@ -0,0 +1,4 @@ +Fixed order dependence in running tests in the same process +when a test that has submodules (e.g. test_importlib) follows a test that +imports its submodule (e.g. test_importlib.util) and precedes a test +(e.g. test_unittest or test_compileall) that uses that submodule. From 1e4680ce52ab6c065f5e0bb27e0b156b897aff67 Mon Sep 17 00:00:00 2001 From: Thomas Bininda Date: Mon, 4 Dec 2023 18:27:57 +0100 Subject: [PATCH 165/228] gh-112516: Update bundled pip version to 23.3.1 (gh-112517) --- Lib/ensurepip/__init__.py | 2 +- ...ne-any.whl => pip-23.3.1-py3-none-any.whl} | Bin 2086091 -> 2107242 bytes ...-11-29-10-51-41.gh-issue-112516.rFKUKN.rst | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) rename Lib/ensurepip/_bundled/{pip-23.2.1-py3-none-any.whl => pip-23.3.1-py3-none-any.whl} (80%) create mode 100644 Misc/NEWS.d/next/Library/2023-11-29-10-51-41.gh-issue-112516.rFKUKN.rst diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index 1fb1d505cfd0c5..21e4ad99a39628 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -10,7 +10,7 @@ __all__ = ["version", "bootstrap"] _PACKAGE_NAMES = ('pip',) -_PIP_VERSION = "23.2.1" +_PIP_VERSION = "23.3.1" _PROJECTS = [ ("pip", _PIP_VERSION, "py3"), ] diff --git a/Lib/ensurepip/_bundled/pip-23.2.1-py3-none-any.whl b/Lib/ensurepip/_bundled/pip-23.3.1-py3-none-any.whl similarity index 80% rename from Lib/ensurepip/_bundled/pip-23.2.1-py3-none-any.whl rename to Lib/ensurepip/_bundled/pip-23.3.1-py3-none-any.whl index ba28ef02e265f032560e28e40b2be0bd6e2e964f..a4faf716b663e61faf22bb58fa57d3b2b0e00299 100644 GIT binary patch delta 397715 zcmZ6xQ*fYN)GQp^=ETXwwr$%^Cbsbe6FZsMwr$(CZ6}jV@W0>r{+n~|y7sEt7kkxS z)xEkW%YloT08lg~S#Ssp5D*X;kVP$ZO@+(On*=l(umaiy0YUJWy2gZnT8RHCG_ryo zgZ?if0RIO2KPZU^>IC?oB1v;P2^S~`2sjuB2=0G(Ia)a~8d}-AnmgMY+cFyd2d!KU z4H+E0+?4xehJ_HiZZ&_O7DH|uKs*Qj;OBojkUy%gmEY`h_mEa81{`$!BB5U!2ASk2 zlX4y*Ixu9w=hRqe38bFTa-vieNO)Fj6GeuJsHq#A{560`=M^H;A6cCsGDWmMrYb9p zGsO3Q&xPXDvo2Xmz*w#0%pvUR2rz_o>Mjh^S zOZnzq;$F=^2P>wzBlanq@#(6u>5hEZ)J1%~`-8sX_-s_}cH6gO-|B-q-h%csI_Sf` zoSyY)O*j)sItnMIS5*cjq+i3pLa`yOv0-@`OVgRGUr+FE4U}wz(U&x2&J#t{*r9+S zjjj-Xk!OvMeH?G~vYt>8F}33)XDMXkT=i)EB5?fo-5EAo)b+>q>ef!clRmMxfazlD zJZy`mVw>v!ZWF?fa8q4`{~r8jV)HovmTUzo^lnBu?TGBAPPy8Q(YTeP>DwvWR-RpsxDf@8;`JyI zjV95*`?q&zloh6d$H4U8#Sbp*-7`0K?AoT`*Uf?j%GC9tq&OKIywAitomyYA<@0k*%>;TbXL+913pbE9xq#+M;{-KY+LVshhRr^N$kkZ@AuD zEpK9pjOMB3H4`lUnx!78KIKXcK3;b@SoC=|izNK)(ic9=u?6d3KH_qOanAJk)G`%1 ze2<9M3h!Z4a!f*2kobp9E8B#{;j%tYI{7+pGrB{&ZM{>{8$#jB&5PS8b3@Q z+&7G!D6=t3pL^<*;VMul+m$6deGOOc=qll~Wy*#b3eSCcuos72Ik0J3-OACdR&^dH za2IK}?*#%(JiN_JlWUEOeob7KHfndN=Bjk7w?yWJj{`hOD{!&n8i}RxCXn+-cnlMd z$l}Twy4f`Fxy5(#-NS4W93>d#RYgWN37c!MNjfOsKCQuWJt(*J&1@y4)oOpIRh?XF zHisj_y4{k=6V%r&eyD)xR=sgjz9KZTT!KO63}5$X^FqF0W|4EMOG*TonwmOiY3YvP zHLGq*@BlnFEp-1j&p!eTJ+%)`*6~R^&G{dT;vpqbSQXa8y3RTde-r?fV{!EnY@+}D zPVERkJKA~``bMId8Fd<@jt#e~ri{W_%Y$Gv==9(KRNZ7+&v-NzY>ZEw3i=#J8GUcr zf&G&Mkf@B0$BK2__os5>S#G-pgXc^~YBP`&Mn<8)!d@QSn~QduA@w zLI8EiHlC(bqWu)|eS_1#$2O?i1?;XuFg0}?AZs$6AmH?0+JDbDX3 zwra2%5SH%&auslDTosKY+Fe`&Y+gP*p(u=4?d9Q7WaYlazudG>*O&$G;ZHFbOqrWc z_L@z&r+z5sY4R*CD=XQ>f`fHd&|DoHm;mq*7wXZ&*u(Yl*guJ|H#T(EqXhZiCzJBS z^yL*RdGL>1nZ~FsTfFn3J3qOOH)FSDsVE>x$DBMH==Bat;y?H%e@F3X=;Axy2^c>c zST-P>X*bu%F2J*}CmM^V<4RCE8}g?Q{z)#3dv6lW58PENNyNn|gPRhc1I~VQZUB*T zB+sn+>`}w;E=sLU)t1heB`G|83UkOZ^xz?ZO7ou=x85M^1`q~w}MwHy{ z-#hB8(_H0yUBBlpU;5r{6`L5O)amLpSGn+uy^p7=)U@( z4C`&4`9WRjV539@$eCxdPE~(i2@1OP4j|9bk_ve*1)Rj!)Cq;sB(=p3Yc^u@Poej;lgvy^9=;YUn zuxIs_z*+Q55VOeVyv|7FEr)&<>q10}JL(q0n(u@>60?c>N4(OY5JlQhvnug|Dx3eL z)JDOT_bbR?^?*4LdPw#sf&#!8K#zSvk!Ra-t@#%GO08mjFrd0ofmxob%Swu%Y@6)h z)=a#s_&AVF%U^Sc_WBpWIkPrqqL}+vm6^x=y z1Zt0wco5!H)@*y5=c zS{{RytL2(@to_c>^{i=(IICo&>U;rT$E(}WI2?~@R25oL-axvKdIFhHo1ySatheW? z>ko@j_9c-GX4Ir7e$?NAFwO8})kpMqu7`$^(laSdGB@Z{X-h;H8)D2CUsM2flv!Rx z%u!aB#^B11E_)wG9B?%!ltPRY|D-@n;!A00vDqnC@eUD2c8l$)A((?Phq{0x?*JG7YRhZjfxt|3iaRG}BC4-CKIkDR=(eazF7--pd_;?LR=;*}Q^|UK3+Ma3V#|*( zPjTk#SO8#coj5&9ADlNbJ%emIzO$OoZ<@b-subJW> z3##WP@*k%O{QJ&1*=&2~KAwT1&PPA{0ZWpe3wuhT5AeHkZMv6w^ZY3-*z4f9pxB80 z4*0TNF>aj%(;^M9FV$iYb1HSIQK^+3_EOf&E&Th#xyPQP7~*XGA8Sr@a|sB~5L zy1@%$^S>G)?9Is?dJo^X?WoDs@i6Sv)Y?Q##oX?;D}2YybWD^1=JiM3q$PdiR#`9` zqcTj@?us*vZ{BXy^>xnhh~YGt zbiJoPZxX8~+HR$@So(E=?4F*Y8!uAF-~iE?UvWAQdj=Bw;Hg`vc9EbMzjKG8Ls1)! z9F(^jf_XcW>WRUK&nk->rzYApUqBtzFfE^f4k&eN_WFBz&XgRB)iZh0jfNTfh;>ig!rSkJvPyS;3mE(U*v4Tom5pw~a@iz2(IJL)#YM2!Bbs4D zT6kd{k_r?zFgaB#o*Lk6$hn_GlDfC^@JAE-d>2Ba533d`yE%UI&#S#N|7I2w2(ja*`E5cn&6~I45LT6w z&Lg|t`Rff2iYH^~G9Hz1;8+W0XNfiDKU#IQ>N?=ok z41B+2w!-_vu9fS5YT0;}0E1a^z*WcPwLPei+x;=NA-|Vge*j-g@#cgoUUlwVF3Q}r z+$^`1;~2f)jVax`+%6CNJdqRv;!^;Frw=F7^nI7{mb2VTU<_f#P_NwXPa{cb0G&eP zS>w(wlouLQdu8VGuiGoPhf%>5T0Rk5>xRtH1+fu43fCdt(>a@$zr2Sfjko)FAH<=o zrKvWl#frYx%emS}2-A1{l9kO9plvt`P(4kI%QS&`zPMEQaO)`2nFIY?{`_OwX#h8D6N$Npj@0r+I2X< zuuk~C2BQD{AnvD^klIvnR$@Z?5P9k?zQdUjm7xLeQ?M_exLx0Yg2piIK+Te}#awQb zN@dnYrnb(Q?!!w#?A0UdtW2xS47}{BG)m3bTO>)GbyPf*phq#U#(p|e&^Ukf|kRU+7WTv`^fIk&DvSKU6vUIxF3P^i`xA% z8yY;O&xEN3JB>QP<*$bP3e`2*nx>;ntm&ssvBghqf_qTgMREY`p7kh|KOXkEer*xR_VeT2#d@6V?@HE#t0eSscAHR8CKfaTb6 zPvO1b_YJ1v{l4t6@`PdxmPOtgmb@nKfttdd2=8Vs0e`=7TBz8f^_^XXC`LBu8kTMc z7T<2tx7g!W+;;;?B=#g)Qf<}#n$h!{_6c5))rw>~+BQA921RV_-uNbXbl5M@C@Vbd zr?Q1!dTe+EaF8ZoX*3w(x3A600bS*Jjydj?4oI3*=mRa{P`v6ROON>|J7^W%_<7u4 zzzzela{Z)?M6Bp9;ZqK{%0aPL#HrDCNQv0>+1uM=8*hW0`&G}L?pz};ak{;^-Hew) zUoQy=$Vgy%8g1aWj{uL?C-$T|hB)Ijw}{Qd$pb&=;Wa2A5bhSPrGsKULkZvbZK;E8 z#8luhb;~EnE6B_5S6|9tiK{_*yHwQh-KBkdJ}A)p(I%o~pNn^py$c!%Q`jz;`+ zLO5La#xR4pnDks3aX@jdr$CsqVv%$ZtHH-mKY6TxBrV$K^OdciI~M)CCnZ-QZwe!- zUg|HAO0+^iE&zR18!H$;H9!y!M(pG1-IvO`l}NCSzMe}f)6*aEp9apELtm94(Fq4$ zA90B>w2H24rovb^fFnpo4J90mCvz3B7z8AMqweIH#KSMBuVc+iI9*94WP980} z(H0U0C$zH*D2OYr5^OGUT@_U-0hc2wp2nyPUrji050Angqobem#jj)Hf5{<#9#aHK z!dysI*3rg`^)P(nvLck`19*`XgIEED>XTcqhHLY_x(c;CB-pR=!!9+Ih5dCyS_m5T z5U4%0VDQ{z&=PRU-+AMKD|m9;qEY+wVoi9{;SwAdRAUV9utTak_oV4rwsjRum4fVO z7}UUX{UANgP#HS{|6iwA@O+N`-$S?9+az^0qh7Sd5nVV$B?=a0?>vyG{)>yFLP3tg z#zKDyyMqTHvOh7%#lS*5oPw=Ai;~TnxF~m;@4>_5eR0}UiF=bsU9q_^0y!_I4wjYn zo{5MTgpyC9$^@lOes`I~hrM)JD4?^0Yxe`G!sj5V+4Zhx!~C}(J515Y481=lJQu<7 z5R-7{%t7<>>BrCa67$C%!vJzhSpi|axC)ZYKr9FEv~nbiK|;y`0Yoq3F@6#UnM;ci z6(mQ>nZTB6#(VGMEk`n;+r*#VVQ(M!o|mNaSx=(8x=n-XKHl(HAzGFe6;{)7j{h2iWt;>Px}jeo@i=rmCZ)K(9qg74}=8_B;H zm~!)=a}Dqa`Sm{LW3M-l0YRqA(Sh%H-`k~*>Cze3pq;&*IQU%VvIN7LA)Y$1xDQ2_T?U(_ePfjz5 zv3h}w2CT`n1`2%Oi`Vu=uAF*bi@rn2`YJv;s~^kTJm^ek+S`?gRUdTk;ae*W5KfoZ zg4I~wr{H*G@W06P(G~yNu%#$!LRJdm*iZ>BSP|pU-EIu_!&ypQq;l1bd<0oR$WVs! zayO@L#0A1N&-+Wx@veb#e#*Qv6YbAB8HcgdHxg*2uLih>`<0efhnW6)LUrf?_?42|ld1wN7T4O^w_jprB*H6h zG3OsK+NfC1F*rye7nJ*oz%b|}YM46LAXL+uplfs;A!EJS_BS8f!)P+>=g+mTt06)( zrCsoe!7l7I_AEr=(*f(h&~LKMDEEQM1q`rDX|>uUmU@V0>Rw;4c!NGxn85QR zd;qnsv`Z;s(T*!z*3#0kTW;~6W}(L&wpIIF!B&Zr<kabZ7n_`|$Ko%8XweY+`r< zNK1Hdsj)fT{=j{0#6`R(yusSUgmdCvFP$$gz7%7U4~<(f5Z!6Vjzf&dQkBb30BP!~wltYXX&4=T2K=Mzv_9mT`6`;w@=RFp@q0OU0Yk40Sw^XT< z?~Ez*7$8I#kfh$|2p^!HM1dwWh^bnGRd7Jg>^nA*FeO&MlO07!J~o+nl$@-iGO-C{ ze`}1?D1<*z=oRmhL)u&fO(B@X*&e0F#L1m*TA8^F)Ai#wmyuUadBjnm%Sav7@EL}VO9-SHPgvcsR2YMA{!A$<-IxBjAKTekiM3Mj zVZcsG*hx9(kZ#W@M}2DIXK9G9yI9&ui$*XxIZ(%K)5iflVvlYDMv12(%m7-+VjszE zuO8u=_i!*bDr{Gzt0SBsb|=ZpyVU0d`n1_=VBkAt(Bt{`DE@LWEonO(nE3OWd{G`J zAfai~25R>4-*ecM;zJ0N{(8BBiH1SP>{@$Um%{;+PZp~B0*d{^e8;^zqp zT&U$xyYxPg{~xZ6W0r;RzurlkWA6W<)yhKEmQXMtAf$My`3MjgNqt@rNq$b~fNats z8%ocqCX5SC=NOKAT%b}jq-x#pfZR31FiCO)du(F38y(p=>v(UeF0yVzwlTIL+U5Mk zSI0lq)+BN-Dx@ybcPVzOVb-L`IfW>htc?sS2D}0MVk`YdLa(m)23s%L(DjBXZ0STg ztdAwQ@`ztnKo-oZoHdEH_?DboQCGdo`@+M?9@P$8DuoyPp+E#YUOO$1hkN3if(%BI_?M=n6Fz7>I0A8 zVb?Jj3kEqiV>AokM)e6QK~V}WyP3E8y*5}iF;DdK$mXH(mwnUjILvMmk}|hS$f1mD zgGj528y(oak3r*>Q~=`m;AYstbS}|tRPGG`czV3vGmym0pQzTN`BSG=vu@`_&R`pL32?^OVzku%Tw6Z(xzOS(4f9D=1r_8$;h z+a^i-sw~}yiAeL)$mWbLJNGMJs^WMTUFJ}yA;=kTN(&XAmjEt2O;M#1<)St98QeOP zMtv?11YXIY$k3d%3gqjU1!s6(<{wisPrdc9O0677lmT%cYI*Ablb{P)_z3-cq9oJn zqL`vXXk2JLag7`kLf|DtQ`JEeC8oO3%5=i=wfs*^Fq9YZfm(t(sM)<2GqX9!fkTA; zN19G=I`7b>0RpNsb^++ z`y|^xF-WG zf&$@22J;eZ1bytG3M<4tZAhpf;?Crs!p~r(P-Uw3dKCF@ zB(1v+Ay8qvIG7w%`s z5FCMe`J$CHny+F5H3JrU)^v^YaKNaL#O3^SPOKb)dw$~HewvMo5AV^+^NK>96Z;F_ z)TxyDQKcJTNO?^3z)qw6&KS zmz0US{M;iNYXINFDxV$`yK_g4r%?ls?-X-YB9eI-5-$xwxgdELYrntw zz3EiTPbA0PSt+PZwSLlBt^AkZgj_peAB~8GrA-Z3d`#riR^2wVDNl!PniZJ-7u?D! z(f+Ko_AV2^X;*5A{IT@k5<+6s4=!nVd}07SFntgW2~J-E=_A4`Q{EZzPQN|q9=8ZX zBGTM1`kx6KrezDiB{yMI*cWSEDyx-1J+h6|F^*P)4E%UHXa$|5ObUe>YG7W&2Po&a zR*Ed}fxNlT2)eWn8S}d}USDG20E1--6r@|~CmAfW*Kg2(ex?(oEr5~)abx=?G|RSu!D;t8i@wXsSj--}KzM1)HSqj_DLx-4QyQoB_K z4v4C|JCu#&hZtZPfq$Y&*s3-d>(AGjURR!HO>bG z3_cqe{@Lu;w+H7Xk^bU*T01}C+Y;HQ_Dr5~_BrY3uj^p)`<%4B+TeU-9(?jb%EWZ< zKEVo;{+>2{ENQpJKG_sn!ha!x^#=_TakJ*9eo09NLoPLsFtcvQC|mmKS5Hx`cXCOq zA|^dAUsW`Ag0{nxLFR!ytH~)lqGX!@oX<`Osl!Bi#6qoZOmVRy;_BScqQ@eZ6VzJ_ z_17JcDkJWp{>+N@^`CF*>H2&K863i?qK*<_893Ck1}{_IY!k{3ZJ{_1ygzc~qs5%a zb2jNpw83iACk(R~YWeX}(mJTyim8REM)alA2nKMWG~kd4`kHhOAkvytoG>5mm5sLwKo%)W_(r2p0n0e8|8OwHj&v8nMpNFp zo(@xswQmK?V3QB?86x?3bt{?~M{fIN-plSduaC_i4Q1`_e{BM1$UhapRGJ1vMnI2~ zz-3TbvqCKyoId=VzBhjp(lN`${QAweKU%po>$UvO)gYB#`#f^Ev+W-Y94h%XAFHkH zLTeIag)asO7dcyKSyfGQ!=E#(&|ga1JHU|ucSbCUyG8}=wx)kxL=VXRLQbTCQ=p+r zh+=V&c7Gr2v51#_#vcmkAWcJVBTw&T6*0-1au-?*KU1N6JBgakrt@3qJR1f6`n#}l zH}k9Jj(G5%nc9G>(gWO)PWThMEs^%=sd>8Ip^MzeF7F5c{lCe?MEo%G790o&A!+J8 zCD{Kk2P`1&um#iqwU)R^YL$Tx?474~>!WUYQ|(B;$vsT@Msk-o}|^W71}YN|j?i`VjLaZKz84RA}SRNEkip+t8Z zYZ_l~kt)=?9MS-f*tu5gxaExRWJ{*`jenc9gI z7VuwAS`4sc?8R_YC(}$9gDx`3s5P_k3~h`zko~?3abXTqbbd?GgqNM&{X0*zw8=tv zxCamzXM`N-I_wo@rZ=Y2VZp52XxNJ<9KGL#hynU!bC=GFJ+aE0-(Lrv^miEql8 zjCXo8g*#h?3AHlVFC9wKzLjz|7ZmEJA5irD3evW1>JefRrw>Y&qXrU&o|F&#tD~-5 z82)zcE)^lG5K6 z6k8gOEq`g&H@#t60#NKFWB zyzC207*o4J2_(3Et{2vH_yce~8&^ zl=OL0lu%df3;QORU$vQT$=6_+kFTn_1uw1CVdey$$3GO4)u>bwO^42D_(XCz`lOvf zwU|MXTVY+HE-#Z0gNkbgx!LH-(s)F8 z`E%Aau04Etx_m;Y=fGx6X!j(tLY54*sGNS|TREEUtY43k~>RdG05j)!w0e2J7zIY3fhh9%ZT!^4`bMxn*N3BO7X?zP_|sHBzv!V zKts`JPep~hVO~0S$#wSt5Or|77MDl}7X^hEBF8=TP}Z7o2Nbw^X^7BO>cYk-yA?`dvQn#FN_@fka8PE|Vh zy10X0E7HE01Td1LC4^=+?{>KIoux0S`?W=pfp1WZIp{??9|!FFzTgQAih+zv=-Ni& z4Pyi#fC@#!!7q~2yrJ%2SFY0OsU=P_k274LKAEsMm{C5cQ}WRx;jb>ac3jzym#;H+ zEO^U@ga)J8VI_B2=gC5eB1G)Fx|dloGp$L4AN^EF0Ve+hVLnz5fg;sEyldRyXbfU* zM}Lk_$6?M?{v)dWdY-z~&|H1>X#E)6UVcA5zwydb0-KgpCE9n|wz?l9Cd zEA>T|1B4W?)L@hp7GJp=)1#L(NUn#l)^~$JbZsZl@X_RyQ%+cjd#!WwtU)AZOkJja(GH(%|(27-^3na>Z#$LShA9`T#DN=E;l zu562aoniP~yKa8|27}u(v8U+LLK~5f0(7vjY{@EW@drvsV|e+5#KA^uutVRA{Ux9| z-7*6;V1##hHL@N-xn_OQ(i*XL3aIOmR9;9mE7N2HK{tq9`r z@sO8H?Zo08&UG|p*e$S6t54_h1C-mUYHtNYjJZmeb2D#(6~Im(w|~D&ej1bV;U%nF z4R_dplta;1^u(gm>&Jbr=L*y;EWWiDde0y!EFad+?}y5iSbE&>cu%NSH{4io#~dv& zdPpELmdEPH-b4@V<|FHccajb0vQ@dXzmcOV1Ut}mP|qz0wxdSng1tkb0ZP-fN3(-E z$pj|t)b5LWH67z&xL*aa6+x#HR=6FKlv19T+{E#?0g>k< zcO_vTsnBr}*nZnZ96%c_{q+#bg!t81TnaT+N_#uAy}_tA!b2e;$n_`7BI*18;lozw zI?<#2_HWZmG)0|2fqEvr1d}{S&l0>H9Z6Xj2k?2pLSPdb{>&_s(*XLQIWJ4f&upi(g-Cs<(9pTmGl*Y1NtKgJCql3 zQx|8$m(J!iHEz(n2U1(E8=w@qX^XN-t;puUex|r&L|t{bg)Y8_)Ic+rtwn zH@AS7TgHc@J2TjfU1CQD#U)(JN+$zKrE|?AkHMd18ap(jg%TPU@Eb{Q?*U}N1zy^d z_v~}m9)7`pQk#_3jvAMCjlbzh{c6tkx%n?IWYxk`t-6=ot5Y?x9d^6qpbZEOdw9CH zl6=B&K}(X7!^qGVQt&J8Juc+6&!iRm1-I7z{sT*z{I9x_bR0$jzr$LEyyaN1GZ=-O zgc(kbHba`#G!o#i@ax+ETtc#~-a#TsHk_00=BedHL*f&j>ci~zAM3p)Y}sQ8nH`7fU@K{<;%_C3)|TzG}s$>ED`>G-!^PwC&W`|$NmHR7$=3Q z2!yVG0us!*KaKk!d~K@5s-ImRw1f9}Wg6X}(8&PbzkH{n%F4^scO(Cz2g*%e|^q5-{g zzesL7u!n%xXH48aE;Si47&pVGU2bkcY>)ElHxP+eBmL)b{qFj?Wukd}6 zT=Chx5qbSp=>mgYlt6QKTWTiUyrV!PofntZv%oNoW`Ytl97>F|0;5-Y`ic-M7TamC zN6_=vpy28|(u2+EL~Nuy$1l*#0&@gG*`dS&vO#Wc1M=YGlK~u|@v#w(IZP2E>V=o1 zGmKYlbu5gzHpNPW)PV^c$l$nQ-1XPYNDPI51etHsr2Y>Ol}2e75HB1Zh?%wi2>0Fg z(r6CeHlKTpDneBhn0u%tDp6QBqywe#Cf`4wVR6Kt4CuWOchg9E5juZCm*fL#(VBc9 zqtgT8DEBKNTme+6^f%-1ObwoH{8e3}!(}Z94ML-HGEsjxD>_23E)ZmMcs7#db3i}a zpo`o8^L_PRanvE!Fy||8hN5CGJ&I0gt%=(;+;2_d<*#ZV1S>)=F2wAF{CpG611*5b*yt0l2?#-A7+uPvZ~g+coqFT5+2ar6 zW%h@I!hRRG7X9OupJ7{UqphnP^qgzzyN8k$+$`B0%#Ir$X5W0~+z9m%JVM2UlXD`j zW4*Kwgs!G{+AMwzT_Az4GnhG8Rd|} z5*ke^xoFE-Y@*`w+&nCDi4oH+L4cCCeCWN26Ho{R<_s2THr561>3!5uEo_^LWMOZ~HYSfe zQ!nUFB7PNLGFDYDj_SF7=u@9TW?LbYaTM6kH{C6}7~2*IsLVj_oMkhv!d+iG4d%qX zZ>e_7Er|Fn4Q9Vk`u2N!pSnKJRy$cZ;NNdf5YI~bmk1tlp^CCv{`EmmA;g<)8O5)U zQ=6exh*?6SlW#9kL{m@aIAr7N0W%|Swe%Wf_V{v_$*_tlR~Ub?Ziz zVGH)*wM!9Llw#F9GMx92le@a`mL||a=k4%%d|Z=qfEkFN!I?n27xjwQ3ZReyEU8w^ z!dKvFr>yzm=TJFJUFjZJ?8uJ10up2qwDY^QW^ULRp##dcLk1l4P_I>}4Zuo-x0hg} ziUK{(P{oAEJB+=)(&uN9WqIx;T1lXJ{d>#<>|LTq`3BM?!}a*mQwgGPyRVgi{JL`K zj=ebO59VQ_@&r18hMI0miH8P2;%-kT@k&k%qAoAq@UdYrHe_(;j(jlhM-aQ9ZtM~< zzDhXWmp6`u%?CeWP;Kz&Qm`vmGQWff_@GROHy!*9mtcQ7gx1+7~E1TILUgJ%7@qqQ3yVc^dg{#A3L;IrD(K! zK)^hZ=jR&?TFLYRmw|fy^N+XuM3_;yA*$OYO{XN?j+Fp23B`pm_8ly_dNeI6oGV(c zZN&#MY#R5xWWWf>7{3nSu$ulVMk;C1$07RVd*2blDTfel!^78yB+${LC~Pi@yae4Q zcb_)Pos~E9v)4$H6bTVpLNkqv3@#YmXe_AesiTUOY4NWaBVKOfmZc@ z#$CyY2akXtzRFxSvS*(_nGpAWCg1d!4;zRW?drKq7z8Tywwe`SqWt`eTmAsM<3?$4 zzZbIO1}!ocg9x)gA1!s788Gx zXd!8jVoG+D)|5ft=wlS_4K6|A2uYYy0h&`e-1=Q4FD==0cV?W94C}nTVra3sz?KKi z0cM(R5e6INXT;T78eGRWjY=)4+O(KpcQX!}hGl{L=4S|GLVh|sNAO@I$)s&KMs6yto&oISp5&_lt{*kA@hQ zTfKj*OdRk)YZQ1xFhBBF(rXkpo0BImGk98BE_be^LgzG!^4B{50L)s9Yc4tX+kP{q zTri8Ro!W|ICV_q)$qTn9+Z8alm4g3hUIQJgepk&f!s$Om0 zxy1oGI6^%JiuU^JFkixk<@=+V_l4g)j}m?&*x{-FdOV$@Yq=ty3aDtLo|d>WQ{com2#_`SA8_y*; zbq1$_xPHm0z8N=~k$GRTS!IjMi;ZJFkQ58B4{7IVtC;?06zm*?{y4kXB3#>y{Iera z=C}+UG`nkdlS~RJ+PtO`QH6H?*5VCne>EEOo@ud|4kJ|uP#EPO-%Z!m2UMz)`n44S&&PBA^0nyZLz0!_A<+{EHTK@KCy zo2m*!hS?{5GDBq7A_N@g7xNV)O?d-AA;u#7p!r|EI=0xSUB|R(;&lX7?>R$_dlVBjFi^5acEho}EZw~egA<;>9b z>n>lRSn+H~0M$h%a8OOKs9*u0s*6l2FuG21aaSC{jve%TG#>zc_d2uliV@TF-oVxTjpTv(?gek86N{t~B z$MlTw)7IE~^4t}JmNFQ~nCYgV;cGuoc$54nwcpz+t=IIoa%9H}v{-74S@t*(%BGe4 zN)6=PK?=OXh1#o`7?$a1tLepYnlueC+9yDeujUiHcXX`sf3UgzN!vq+V+t@4(xg1& zOwi465{2D**5rH%K^O$SD5k!=wEi1i{_==-D@vAZ8VO~>HbYStJ^yFHtT}i8oDD2j%)yeM6HpaSv7xZ|~$2OK)kTrD7%*u?$uY?Vy=S z3)?@}TO#1FN|k}J_hJwbBsC}EAL<{=;cQu6cZdRxI>>zSrgOW!$;+O}nC8b0ycseX zt7;ab_xdV|1;)|$(2Y)q?}rG3({%2lTK(=uB;p`sd>Fv3w>h_q;%MoH^MJ0uoitPY zJIBWdAq?w~CeVEor}o`S|CP1B>+ejkSD#z`ub#)W*g;!l=3eSqx{Lm&jT6C6MyS?W zJ!xZo@6Jm?d4LgXH}G)y)BI@G~rasRwe&GUCmGEd(7vhF7Ey=lG2w35u9k98ZoW!FTL@$CykXrN`QPF6$e zx(b(=@7ZBF_1&#Z4`E{=;b<;hw2`5e!3?`epbkf%EmEY(m_Mz$Os?4GB(4b|;jTVK3f=d1I~%2{N*}11{fhQDgsP?PN;~}dYd>whMuqP8 zjEtL+?k37;4X%$u#f%Y z?l6GyRj$x{L`UEzT3+>}jR?s{LLq~6p*VRI6~~}zL6h|S>>HM1;Kvgl<6m3LhSf{Y zf>EcLx08`ow^-;6DlW|$SC(HaqoVxGIIKK#5B4Y#yBWbY99n{qqq*?^PWh16@c#sl z58%+zq9gw551eN%C!nNje+RP~fxJpHhNuB}V0Lf|4KQF&9F-y<#nG>S85LD%?wNt_ z#zH8WmXcGJ{(oG(Q*fq1yS5$MwkNi2+qRud>?gL9iEZ1qolI=o=0EFwzkg$`z24~R zs=My4t_$aJUW6I9M8;(K6SrOjv~6NnoWn(u0Sju9-204^Z)=|S$JPIS)@JCl69hrW zA_FQ#?n5$VWIqOlJMiBS>zVxopE9QOeGf~Me6Ve z7{<3-!xki0b{Hj%c+WCc$V}<}qSt`}4464I!%yyH_m;gRD?PWInqMQi6!Q+_FF~bH z_%IYL{xN0)#y|x6U(fOOHrxl}I2&H68`VW)a7l~vQ~m{DXyaPL`1I`}Et|cAZ;6ku zx6)G*5qG2S%RQU$EYQ{(G{Nw?<(S(fPPdo!&P!f~wYtz-En7C02n=rpI4sV9eZv_J zpE+`qOvpb)#oV6K#ms}uT3W6eM@}z}x!UIOhml~`!@mZSeMMT%$R|&rHGT*$%@2@S z2e5TG`@OVHT-}&>__nwA zH;(G!7I=1#KUGgSINvwgwv2I1vj>hzbeb~+QCEqymCx}XQLXPZ}MgpJir$YTK(U{7}wMNTfRr(o3t%i7VoZd${skV_! z1Q7v?Zy8V$z|jtGICMRL6R>ylm^y!^h@pNy<)E?tVq+1o3weYD8=z;Rt~d4)6zJql zP++sz8ClmbeB;&Q);U)LQ44cxYrSQ*;ZHE=D>|%ZuZ+28?{?EfhWRE?fMYoHHJdm0 z(RN0Zi8zn*W6PW$7qWIyPP8+5xi@G3)PzDAbzFk)(V;)DI$bkBxCsr&Ec+84dDZ%3 zMh8T$S`+im7K~@5^~@HQUG#5R9 z@9XI_^vMNR6siZl`sfVLdc)}*({ z(><6hklRphIBVh-Z+!EN>cL2`uK5)@C(x3{2m7)@=?Wj3 z)(QKkNLn9IIp-xVHR5HI<$~%fIDK4a#Mg}1A$H4n{?mmNm~r$kbmo4Z!^b4a~)>2mn|qdLG;evY*3+TZD7F0bnDgJ|x% zQP0B)Wk@!!o{x>R^0lv$0ecgxFa1I3`3cl~i{})83f;b}rar^c4xeu8P5W`;@UmOk z)7(8xJ-kSfc}mPV2;$tug|g}Y!CH9qAh+jq;9NDM-UWMlxry$QDPup;AjDFdC?kc? zbp?@^_oWtsnJZ~#ViS>@cp9Ji8e}@u?^UG_&bKf{Eu;Vr`x(?+$N*1QJFfKz&%N zO3VppQq}0F8K=nqKqQE)=^K~UXyxvAPbKH7lniJ3($RSw| zmHckyGXe?U-tG{3ZD+VA{>#YaZ;vTD5t%eXoDNr6er>-2QoRWnghMtmM%MH$6e&x+ zdc_%-`Ojxkd1AmY_No%y#bSDP`2g4az`6yX*+&2*3?97Rsep~Zq_og0{rl(wklEBx zytt+dn!CX&lwZyAP{lRQNk@gv`br-&N+(q!0V}A8emSBhr-QB@9{cfu_}jgu;MP!> zHq<*%Z^IB}Fi*n!T`xFB2!8?sn~|;7A#`NHY+!$+ZzOCS>=HXYXi}rip6k1x)V&Ju z29gkl*OTwMs*7b&^RSYlYVV3XZ& z%@I;j-vM**k*;Lz!lqXL6eL#;x>R91y4GhiQuiAlyq;3?jQ%!gkt47(j&lBpw3Wke z$hJ3r^1gb@dRFu^%Bx*J%K>ap7~VuI-ErFx(Cz0K0F$e|`V?m1g;FBK#RdUDnJ=N5 z?>4MUl1oh^NiY6TMy7Uo>AHxu`MSdZ++0xHX7~ngq&mdqm#K@b+`b%rPx+aSlMfd{ zs>r`X4zYlj$KA_)F(y)T0l1_oi3}tYwLP9?PkAl!^ zes2pQra-6G0pEF;*YpdejpqZ12Tl3fv_S8u96B7Sl6(xUqkpzpo=C?+<;)tOUL(^W zlPLTxKu4EX3%h_?5?`}Ideee?Gml@Nc47hDvhq84fS>oru*1>CgUVcnQ&_lH9ZM3O zDV}5AD5mxxpAJNAERhRyMX9s5$*5P)o6x~#!wL%6oO2`70m~8~C@28w?73)zzKa5^?HgOMQ1s52CbUXcnxA$z-%`F_<^X4XZf~7p& zv^!qtjx|;~iQrEq-77$bJjkKt^yv^5FKNaBStLx59SWu;B9hEN%h5JTLbl0#-?tjo z*DN%5$*RX}fDg7ur=M^8YaiqWEYURhgO-CB5^(@dyQ6QzcS@%Aa#YvgGN-8)1Yw4M z$L>pNVXNvoU{n5_01VUL>#B>VbW^3Pd+y_~M)#2*v|NZ8;H7e&?`iO_#S@&`1*y=1 z?r*b@=)4!TYyq(3f+wxaGHe3EhkqNsPq$?FXfdBea5 zEXJKcz3~)3h$)6o?Al-l9uT0aOuS9DkU}!1cc=X)%<6MpWJ zG!e^((Y^qL5r%LELz5qs4dz`EV_osa+m(ULBOLRg^}OM{OJo&aP;4?5e8Ol>MzaT^ zs4zw^p~^`p1EV=-&>LISeYRsU+u)&%fa}1xVlJQ7LED*nicfdx{(91NnoERquc?{L zzovj|?q3e9jQOcmtU3nQ=+-iEZCf_w20@afn`vIG%%gsD_Q)H}0v=1by#ZMsy^41- zJBr!$Dy(6WJw_CiMVe6>HGF^UqLt)9>6C>oaGts%lLCx5@m0CAaXZY-cg2X`VQj5= zHJDi`Y@+wQX4MNVl^1(I1M{kor5Y+TKuppI=yI+isK)@YmE|i=A}pukT-5a%jCncK!gb zj?;8nKxRqiFJD69A|CvHhJ6diIJ1Zjn!aQ{K)pQFqx!IB*~mxcYX`XntnM zo90`OfT7+WT`@vPzy;DU&cuE6ZVhS74PvePu)pMoO|-h(%zi7Jb5Q1527ln9$Dn9O z(Bq63DZa!Og|h%A*Mx8d4mtrsWIEOedpq!Y>$~nx@BhBvjRCz>p#PzRW(UZFss7os z^-H8OCI3g`3zjrDfDCXxY)Sa$?*$qCsw?cqo`|K9%BJp$qNCDRYwF~#POMJ0i5ezm z!V$$wB$l+Ip#OQfum=JY4D_ncmd2kvI*RwOdc0VKS5!jFURFzukZ!OaU9;-mu~6%0 zPd}t(V4>pb8RJ&J4IPZ?FxDt|8!JY0MQ6a^;MtH@ol});sRcy!9Malw@lT{3$}OAY zb1<07IUiZmk5_c)sAgdnbeM^KDgJR)uQPG`bL758_mflxv)#aJ(-1=`=I+n__agR5 zZP+=F)JDwH(0FIbF6@B*)9-_JCnB;HGW23%A~8a~!CO;i>t2{4N~3+(Y1kumG>S43 z1C~!;YiAoo>lk1`H=WHpHr!JEV!NT?sj*VbO4g^%lkn%$|eaNNyrN|<1ZKKn${d>tF?B7mgxva>F1G=tA zXU!wV@lyPgBPxDqcDfL&w4105h*TDMlcgY4*onslZf3xWSZk>M?V}9GxobsE52^uu z#T?Y9%nZ6zVeE;WPAiSu-+OPRm-|lr}S-yjeFZ zl=#xE(h)8e3XQ_qLj9gpCgZ<-G)32&e0Zu33q9eYR_`&AVXTqeuX)FOfa;a z#19&LwQmAm^Y?tU9W+nmoPv`-z#x^TE67gE=hyLj`7!uB8DmF?^qrd?FdCnln5L>TvF1Zufr^P+Zw#KSS z0#7*%Zb_)?+PlBLY5jSr8%@`pAX!XUr?DsQpz{S%XKXHFY)TFyL48eSSw1^OA(p<_ zTYV~w(#$o3*6*jsb3g&DyUSBHc#xq|H+?0Ca7lloUka|?s}BEmr#FTm0ToDjvyLwH zK;8hlpT5qZr^Q5MxNhGMMrE#IF<@OD^UfpgUYRu4e(~v)SpB*3%pjn{hzfMj%4o&I zf;yg`lpkpDfC<6@wDKBX{1Es&BPRUQIaZAU?Gt$oh0YhOFJB_szD-B^e*I0b4%Enm z9_PDhjb>b5w_3wEJ)NH-8#03mT=ONDW`zc{fX9_)fqjsFzgPZ5`+FF>d)Kj~DU!>0 z73nf`=9RMM+f}Th3YgM4`Cw;5Lq8b4I*lcO@ZvCOKEuPu7z+Atl-mdR8yVq+*Q|bY z>;o56eX2zhkbE%C!qw9zMGH*yT#C{s3pO>f)L1LPy^wYm>sFq zabwfs3_Pnq{_JhYKpyizH~CORJJM z>lL~T$*?Tf?b0CMiM?aH^ciux?j`QaqZ%QM&QPxHG~}LL*cA)eoi%yWRpkJbU)b4e zhCqCoP1*YOHa2C3@<7!(vfzRs=%BUmf_UIe>^5P;d9CUkis*`{yE|13okC_xqXX}T z%~L8tH=dB(d0|>XYCt43X#_K=&&w-@P0j`{6w(Nv#XEQ=v%3pws5?un!xBA~21XDI z_bX{jM15hjWgycen!)X;1t0)~9I|B^9V2G4juj_X>1FpP07;T#m&_jq^ zs?0m79miXgU0g=jG!(UZy%o0m!`D}JmrlPR&M!@)$%C(aF1(;zh=o|dPv_4&ekbZ1 z{;~5my zx&{2(9%#NqFZ;NTyB?jw7W53Si7ZG%&f+QIo_;`ReAhA5LWE|T%QX24XjutV8KN_i zDJ{^1wbtcyo*Hj$71fB}u4KMnrlf12OzlZ>JGI6{Hum7V$GJ| zVqcMeE6_94?U?L^3W7FnR+M(}gUUXxyYL1-W9>5$Gi#Dx7`rW=oTKm}J=3&f{GOXU zgG*36(782@y*I5c@XQ;?1Zn|-0sa8knZDp$9O@!zOq~3uk@}wb{hgaYdsFW3@M7<= zk%SRQO|V_aQ&0eu@f_Yj8`1-jtkIOpd_(cTt_S!kwfYt^5XF#)2uy!-N$#2JC}P?~ zDGo^ozxU_Ff&dr`{s?PBuOR40PxXiRfoIT#2JxH#@GEY_@SmCT#tue<2FURFA$+s3 zR?d0BDx3=lnQEj0>(jr&c$U_et1;G|h%I!$bP*u1~Gvt_nOd}Z}+X0 z^`tyFgGwljpA^6oK?C>JNbqWl34H~}$_LO&4Uj{%^85579$M_YVPimk$Vez2$<0z% z9DK&~L%VrAaPAm_f6;)uFQN5Nk$~Z`;LlPJ(AELle-Y=R)krk-ZMvtOl7Tdet+FDY z1wzFr5e0BCiMtDuY*GE!wSy|PROAVTA*2 zU;PtdYV7#Ad**Cs^>%W1w#nDWJ|j0CS+&!DUo&8)TAaOo^g>vB-o5?ZcyK=}Z9O?j zJv#%mmO9Le#MI3Ga1<-4N6mJ0g|d~X+Ag-#F36VH>0M-GJTVz=622pR@=A)N7LEB% z^pr_4Ya*He%7-wo)adJM^I(JX>>V{URG-!K<}^(yq!pXgU9!5$=qRWoe9BfSh_F=+ zi%VB6AZdlDRf3u-fv!d;%tJTeDdEr$nJNL1)uZf>6@hw-$->iNOU$}eT0nw`jsVL^ z6Xj?Jk!UUz5WS8N(IaIS#HQf>{Coz`w$&&S7P#mcr$6yq;`c!DF0(EW1nFLd z+uRBD!fp3?)@5{l`xM&RFny`uD8zNxyl1zdmTd;yGsZ1s*h_M`W zK`W&bW{fAM+Sp%LLU<;9!^SlzFEtyf_SNorlv0|HL=_Hxs791&T@#3{x9l0=1JU5) zktmp&LjbbNO2TP7LjLf7ry(FM@VHhc6qaD5xOzSL7 zWx5(L+n!6=--{@3bH?BePn9eb*W%ki?xTNjZvTCDRAifGc>_3FEt<)=(}lE23fP4V z!U71A*uW#^8&p(KBPXT3G6!J(l~8SXwAVVH{qvlNU^DMIYzYX8l&FKtMBEhMno|gm zyv;PORA~LJ>GlP3M}lc0DMV?n*crs!4|hi^2&KluZgFW=tBPj%JnuYEXG`B`jjOF6wS;X_LWDLwncol_4PA9L^ zZI-!hQ4KW1AcBoiHzQLQT7^S2EdP%N6Bo)_58}3!^zIX68Z8RqDN}3(`B!vzFr6$& z+%bPMvJ1UGj{%4Dod8&!yxuJorW6v;W)v+X5cHsBo+kJf@*Ub<(|c7QqI1eawUvmjDzTZR201$1MYM@7mrrO$+9y^e}r+Qf@TK4(eyrBzJW z8e}*tL_YVc?9;CnS;8l-Eipn3LEjeLzSERS<8#^|%TFnkzXM?HN#;bdgZs90Vd#Ul zce!f#sB_>f)_dTTX{(H{CcUMPmg^r7UE$jOidBM!)2O>EO?v2rVCNEFtxO)=CQAy* za@OBr49tIcix53dVa%gi(l3_{Y47K)yA1}mxJ_g$kDNVbx=G**)#QN02St91m-l^I zSw6g9O;7&`$_I4)UatbFGk-2;gj&MoCA3tS5K^o`s#i{@&%q57#v7v9j^qkO3H3b+|*rj>TPU@c( zeBflCKcMYfL~b#ICvwY9zPia!p^-rSlP<(n{)OwnKLiY~YC!q~DU2C5O7#Ux=pfk1 z!^1;9I`k%7K%%~kQZrWj|B1&yFp16i$fhi0Ap@BV)jz<@Z+9`2>@p|>ukeo_`)$|< zn|zC*$*?GAuC>emm|3ZrXe+@a8WC!m7})2&Nk0I%biWf;*#FX=E^{_^_#zj1lnU+q zYoeYM?FUe3Nh65*u9%b^;_%-kD=L{qm~8a-U(Q*W$wf`L+FQ3WL=g1=5Mnd@*4b09 zYUqR(_=Q zk|isTC9e3;c4&uot&>IPAgb*g1@)bAR3!(cS}swB!~#WZkN+7GNpfohId}j)BG&Yz z`DZ-i-f1GXB9fSY-vuvWxiIi(Sl33#TK7`yF-2enPnh0}KV99pQ|h76fAb4;Xa&`P zEiL3(Qi|ob+_Bft#=((C!BBfHJ6@$5vmfKt{pFlsmYV$dm*oKU*W=bDP|$ps zG@cQPAD&_L4R+G}4UmX9KQp#Knj1D(k161B<7Sr(N8z*_CJPwfS6V;<-@@eqy4y#T zBDXk>VQJXnVvk{=AM<>NVXhnVYzx-)dhOlqFt^*KS1OIi<3!b={`D3v>k+IcgDzch zvoxZFkk1Ff#GmFpMVioF^8k}T(m?@MIlyXFyx;+ex?oi-3^-t8-!z5fXNXZ4;S+Eg zL{gEoTz;cLwCP%S(6ZX^=N_n6vJ)X|5|(a!?tCd)2ybLS+sGt_YAb88zB_MwY?)C* z!hL?uN&~aI#KPYWP0`e{l}ys5AJ{xqj3=ewT`k5OI5Ze34BHS{h-kVY#@)^wsXm!p zWsz0QH-MqKM@1S~SvuIgM3YV+@Co3m`khyHh8$RAL?7xZe+6EY=-zfhlys)`qy+1% z>Ps*Bt;>}Lp6)QDPlmR5mLBMdVDdfO@b+^5h3@^ccf3ge^+<#4B5yo+R);9~_N3jb zf3Xtp;-S{eHGA%PX0X^C%X>7$63e&uV`G=}xO@4pfPS6|ZV1g=tun;RsCbbC<80-*@c2iFp@j>YA! znfPU0B*y|#JUMqQstIXeeEVAg3t?u~_x07qg_%>hFzsKcXeeY~>9uhriD-?jZNOlsb z#G{X6FP0+ZuuX(39EELBxD7e`$lP@kQ|z2$&rIbsp`OzJxfh`_+y-Ep#D?F$7O%He zd3n!!xW(T+zTwRc`mS9~kC%f+J`acuPQUs3MRV~!lSRWSv7VJhZu6i={m`*z$W)Jg51dB+K}z0O~nRHL){{O zWzZ=3DPW3RsnAYN;U$;$!hiPk%_@fSS&7!O2>humP!v|x0jnzvR6O(uuB4L++Hgx) z3qndl4??XS+cR0d-`9AzjjfzwFZ!n$vNhfJy>iK}M1FdaXV7d1jjx~Vd-m=-Z}h|T z(c7hHySaAg;|S#w@PDe0)GL1>{!@MY@&c04{5Q|R1?c~G7ywJX;3q^(V%;|WZ-qT$ zdj{-3TcMiJVK)BR7$FhmSk;Yh zrRwI5Vc1pv5mPI=l>6sKFaswQk$lLzj@S)UZ|h{vc?0?9hFXb+{E%#A2koZJ2VXkcqiK{qoopiW)*V>{dD3`= zWU;Bmne`;RcfP0i19VU?5!C6M2o+6_GTdK?4Zv9_DJexFeoGdnZ3x;vbbvmaOrasy z3Jpuj(NmV5-EcrT-O5}W1e%yHA#PrmE-Yw>!G&JryUCd{YsFyW9C(_i{0KREzHDLy zTjj#v*}fc86ia7DC$6FDH~GuAEZ=+z)L)+~wve|N*;Y5_r0R0rXSWLlhFn-)nZIQgpd^R%7O$hP8nqjViN$=1 zvD=T%Sp7mxY}`isgmhZn<9>!fb?p^2>+1Fx6Bgssv_7!FO6LdjW7! zLT09ruJpBOs#Is+oxmMyUsT~Rju)@bZDUKE2&*r1TMxMdv}@R}n+tvRi+D%Da9%^) zU!Nm*@Gk+;`Y=dh3>;agCZg&`4)}6!4iJSw)7+y7sJmfYcFqGEO-MsVq)z=Yfn2th zNvsqv91(U#lese?IPZcM$1hK^Bu_wl!#G1KQVz2o25(f*7^$xF-=$)DCVUl#RRK=X zHTp2!V!p0f2Ao76s2q+P2P^o*T(%w5O>dr}T3Cd1)Ylb?`rGR0G?c1+whqTlSUBMT z%Wwb+*DH21QBKJ7W*H_f1M)InXI(>^bzVhz@jzm$TxFz4!q$C;LiH4uQwCsUssXu` zOx5@1VjytSGI?W8Q-5@1I_%AEw2}V8?#aB|^b>meepT`orF^==j(UCf$@l`?2u^SA zN>Ux1gP15ci8X9w!cJ~rR~xG8?geW5(xcpT*OZu)Fw}t)D#0?^2IQ-8jXqPaL9If% zEEi-YFeM-@(x`YUi;qSP%@Z(YXTT&_j3Mc!kLrUeIx{Y+WmAAux#ZC@ax=AVMEYs_ zGd~Vf!1&pn7%3wB@QMS1Gf2GWNc3P(e{`z7sHwree!Ca}y2$Q>V=GD`Aid2O;3N7A zKY+A=@Tf}yplT4Sl)uL6o61m=t-Bv>u^UKQ{?}@8vsY#jC{X0Cb`F4GZ(D2`gKiwN z2KZ$Ee%52PKqOrd_|~7ulfy#s?-8AlaQTovFB8v|)5WH6n!ilx19G|f>FMcNS_ak? zFdtPC0CgApJ)0t@Pibvj_v%Z;74-rQMEnx<<5mLp)DoY)Fn6(h z4POyM00I`gq0r{y4i)^bp*TH5%afxyR}0XB7wdxp`2zXuASr-2Egm5@$FMEVJd`DB zb=_7_Yh%ik3DAJE8cq%th<8CUE)xjLlp*g@z{f1mW>*}1hoa`Qm=2j{S1VU%LSQ7c zb4i;P&lD-AVF5*;&HJ-j_y=9XMG0Sm^>cV>%wmt*kR{21Q0rB&o!v?Fk=5B;yhb^{ zbLK~b05llifg8XSCO+SWUtMlsGJaCEY9Z8_FxlrA{nZNt;4$S_IB(e9rLC$M=AY%% zb5H{71T#Q-xw>3p+b01=t!`m_`^D4Vuj>?`dr|y+&2VZ#mlcMY?BR2~Q%5KhUcm+l zqS_6oLq<19GwYLGyS~&s{?hdbWqIqKgGE|6gsOE6{{i5iv@~um?aQk!yOZDC4lW^6W-S3=8N{ zo?g|#mR&;{jw%8qF1R+CI>!Xg*ppPiQl6BYC=d!xy8sS*DxBYO9T#*YLQf}cmYa#futiTh{Y!Z}9&w`g45sG8u+)P$kp|?YY5~fC z2$3*CzPyjcV$k!^j6uY`C!P*@HF8h<`h{YfRSiPE!vZXzN zVOZ*C1=uB9Q-HU<2#whC4eIE92&Ym(ivT;7+>^|xd@b&>2%NkZ)xr)X z@hHXqoz|zlJ{~Z1=Bqy1Y35nt3Bk`NY>Pj8%xOX+fvkupsuT{|){nw$2YnrGVWfxO zTJZao>yJB^03RP7LjCi;=2GTj^o;_IzSL)0H{{qNMti2_Wve?aT=)~vq%b4w-0v&$ zdmtMxK-$~rq8#qyOxgqP{|TSyFGE586BC`xWc)S&2LkFv`j1TEe_|pGK(WS!!v;Ij z&vozqKlk4zE$K!F=X~Sp6A<8rk-9DPe}1viqCC1!I8sMPZ-AYI#H!MbnMF*g!>ya` zQPQY|U>T%YFlKPoWZI~>q;Z$}Qh5HUjsD%Ojp3!t%}@T$9Qs;&et+0dBe)qfkjo)H_(8rrCo9QcGRA`Ay4S(8TGTHR z<&+gQh7jm=t!Y!W=9AzstMEXCpjSN{BieXqabX_%Atu^k*<07QfO3Kmp4^!lflhKb zxvvY~j`W`v$Cy2PjjtL%-J2JP&29EJojCGbN-uX!UqFzR8{5OSpYC>YPi99dV^?tQ zjxN~a9?8rA5^dSz&JAL2h%eeD7)S@P7G=NRP7A&pH9r?-hvh%5pNp{;HK)q3y-Ck}>QuJu&bOdUmRW z7t&kk2^HS;Mc{*_)IH}Ig--bBz1ly8R+N^YT4&D0iP0aAfPsJle~G>`jLPhL7(kLE z6f}Laa9%+Q?7(WaMn8|hoSi1kH}0~Oe-PHJN~al&68I)mL|&T3o(kQeA%=Dst@@dz z5Pa)kqDIIX<{4CXMW3;hCT*X&;RcS;BV%+(wNL|kv>LmUQ2AlkFVx5^0?%S?UhKV<0>oq^7v=jwC^d<$55ff$oHt~za>DsALwO28CK6UG~pht_y`zO0Y1{l4}S$}S58tYj4Z~rniMS} z*P?sxjlhpAQSKtfy%)!VfW=q5=m0nZ(KdS3Aa5{M^YhmCO+6{jatCyw{q|;C0iC6t zu29p2k)QR_s|e#+;^QL7EhXPO5*1HB@E5E68c=So-d;Cr3U^$b`RxB9N@JViik z7`#Rn0M#Svar|#IuD^TdLXg?qYU}umJ_a-kUxtjg2|{I76`X2O?Gv=y%WY%;Sj{mi zw<{;4B+^TPmTjGeb*St<`w@=AM=u%@w+!34>Ks98klFd_1v#?8*u83qYZi_jw4w~0A zo7bFU#ZXLFGn;#IlCBAkQDb!swKc+DeQP7FpRJZuf<=9KwrW_n1~Z1A6-HmDR=SNW zYc0g}@sj2``MPR1lomQ!h2~qz<8ilV678Up_IKe?{48V2)R6A&6bdct9vn74W3|82 zfJRM?C#XmqLd?{DWTc*90asw%y{d*~*Er0^ip%<#DaX(R;prOu;iMS-ZRShrcp8ID z*2lm;^TlopE`!s?GJLj-CR?F%%aJzhTa})guOt8yS`ddQP8wHRbImLpc(2O5b-`~^ zgtsdsv*Z;siag*LGZu%X#X|ON+3C<^0J7JZlcEqFvz%*e$I;Q>rVI+J2L?5HHl39X z6bg;`X@kN|m{#JJkm>R%51%&E)XDQ?TkM}`Am+R*p13Tjb++}inUjB>i$3zq;mpLu zY8FfO09@Q+F9<_kxRcu8VRKSPgXXs4ga0F`F;*Dh!uU76qVeQG0ro$ybM~iSDjf8` z5d%2?%~%akq5xhD`WFkzz}kc(eYFee=j4<(LE7!zlQ#Jd zK>|}KGj*hv5>!!0$9{TPKI19Z?1LsnXp>k3W?taT=3SJrNiHSm*xUEDpDT8~_Le2- zVAUV)Hr!~|?Ov%NnY&k_q{lgu9?PrwYHplEuI1RD@F$f4VN~4pfZExL}6PB zSAJ6TqDCo)(w473-WI{OTHR0-ZUvKPpbj$Sc!Evwn7Y_X5&q9@2a$xzu2ruiin*Tw zH1M|p+g5!|-g$oR$i(5xj^O);VtiT3ti?^r=cvJNp}^Y2nW4>QyWH*u3iZ)>0r^Hd zoNTgo?fHPM?MuNr)UI{LMH|jKylKCGq8UF=U*BZ0mOrb{k?CV-CwKcyHKDlb%!44H zk;5C1`KBAU%XlVdG5-dcnNZzEw17FmRF-PtA3zbFnc}-Bfk&?tD>mztn{vHXP8geU2ANC3`r!?W-W=CBd;& z`mvk;K5F_2H-rRvRPxCHSN3-VCJoFBawRt9LZsphQCscoFUHia>&pL790m1$*niz|THh7)AWDhJMEv7H7;B2(O37SUae4a2e7HwG|o z42ud3#SVxW(r5fQIa)*vTEaOr8G<*#gjN|j!*zo<#7&Q zo#ie#B+K^sOIe%PLjSEvs%kJv879)(vYOmDNonyhUp_=C8$qhiH`5l~e(0SF6d zP%J?po)*(G}E zjGzz9h@~3}v@r==DUM_=97KgmKzK^@n4uip_UG#A>yFiH%gfcK(eZ|jl=cSc{HPKI z7*!`SvvY9MJp z;xP|Xa*y@BuP=0ZnHgYJz4ZIL`QCjk-C9uCW*&+*;b!);3~6V^afYj{0ep_uFfR}l zM#rX^j!-_D7A_VOBvhcK_3vnXyrUQ^4@9dTtrilbVp(v1z2rEgQYiJ|{!qU5_FH?b zzdbnCsLl%foJl}{Z2F++Gr|zMXzLEytg{!CR;fZ^D-_&{;VD33AO(63gHu1pAhfJT z2u(M1ZM@$C(g>W>WasI*51}!EG1HIe#x8=;acp${cj0N<(J~SD%3Nk4w$<&t`c5^( zwl_z@-;7+*H#iYOgHKB7dMY5&6u-)!M+B6!h%Hn~=GrO^PJZW!i%ajK$3DsLo#*RD z#ks9q$PoRk0xZCnoqW$gp3rtTta&ghrk5d*fvRJqcrGvC;KPx4fo|Lg$!fZ7EjS?{Tu&t1w~4>qHHpdr#h z-I|!U1f8^ft^df_!?k=Xq;s#zRm1m!=wmrX_5vX6L~Qr^q{)voxGcDgdxmG#u0+x0 zL&A==u*>4YVVEB9$MFq?jS}<_ycJ*Zx#S|o3|>!B{kn|E9u%1sl-jK-BwWY{2@`JCeR8|{JaJMDIv1L>s)Gtc7coRLi-?R9oiz@DMjq2}z3;{D<(HL6ku zC}^^+H>+@4B0|0|d*|EpNv{mMA3BY+H=FP9St5xQRQBi5WI0O4eF~frUr&JX?sJ`S zBkttJa<(<)R%c}Af9JdB;Oc|#|FYZ_wA6fNFtpZxaTFHBf4T#$vIxMkVE`d1u!-9k zOV~NlaIwS^Ax5#_=sOA&YU3QuA)DcP?u)wwZv>_P)ok8YB1xB-Rd4v}_wl!|b4`w& z4H0)7I;vUpxv-;STWw3l*4=dxW{p+NSM@f|KvggO=1HF2Q-|ZZm3e2$bp@mNOReeN za&bxLbpHBYTq=L3>%-CS`}uMU(0N&FQ9E<8Pw2TOH7vLBRZ~_YJE#$2eX>7YLw8Qi z5Z5+dv-)c-rfS(WiFNpVopqZ{?5{ak`65!zI0QFErH`u}@#~%T#68#l2#aAC9h>!5 zSi3VU(;n$$bM3yB%L1vo-_c!RiwDR*J^D3)zb9#MsMtiN})nI}U2&xOU>zII3r8>}!T>@LP%-RQbcb&=e z63cU#dX!6$N@_J_&N)*+OwqO%66Rd%BRMMqDraB@ZD)b*!%t(&xCWuw0mxx@o5?>& z=-ec(*IEUm@)^9Uf%32J+=grr!pC53kSL_6Nhs4*EzB;Y=0&Xve)ximP3{7#G+v-d zps~NS=bzf=^hCQ>Q0*DkVg*GkCjr`~?Q5r~dqRUAzq{j6BCX$m2RS9_W{v924EkzS zJGTuqyB=Dz7W%-nqZu<_*P}7m)g+BuvM$U`6P>a@alrcJpF`6Vz7WaT22#_%8g2e^|S*3k6Q}vpyXDaU10Wim@vYHAf{$|%_(xrt;S{VJ6dWM8fM*7lhCL3 zN?y!&FQ0+}CwZXf2p)l4CMp~Va!F&H+|*WKqU$&6S!QZIf3(?<}Ggkh&mCwh}S?%l8Z|4~cz@NF%(IarRH!+$DhO8%uNzabNfVAcUpA`Ye?w z8{K5t)$idOhka^H3|y1>$BE-2jE5(An?7*2gMaBH&g;SP5_Myh#Ga0W#Gp1NE&2~F z2TT2{P*)9&XT>NQ2w~xt|CQ>3d$_j_$?xANv*w8B0N8Vp0A*@|d2~$xojc-niw&8Z zry^myIW(ZQ{U5@*G;F+eiAgkD(EdVVV06H$+!t;b$d--A3e;FCM|zqS!!vk%q_sRDzVGE+)`A%2jj#TZrtht<*SfsX;H^4*kFOXaxN^lx%5&XoQ{X!J`* z+zE2?7g$AS_X^@%d1eeB@3&!nw7-XNGkcu4gmRkwVmB><0D5SCC58JEK{^7gXG#I7 zg@EvioK)BYTXix+8qo;^zzQc~4PTF!i2n%#42Fs?KKKABSk#}oX~^}YP+Cg_aqTiW z=FeNs(s=+l)8C^XjDj;TKmFuO=DJx|?IT-w z;G%|Ll z9!ql{SzR4V0jA#c$ag$cdS%N`6(aH`EndD?K%mOPm(Q*S}k2H4N$`?NYLu3=qfo_R0XlC_Uu!`IT9Dg>vS}B3$%+j2+ z@Q&wFMK`&DRZ7KT$8@TF(10Z9Gk7jAWS9Cp4BLPpRfVfFK;};U190H0EsC%7{c(V5 z)Tev`@>vBEXF+U&f1Ksj0)jlN5I3=ShvZ|uzaAmZ6v+^*?5DHMoDy#C0a1dw8Cvo;Ik&P@{<%gCdsMj}R z3rN#3tW4H!r$l8aqV_n6bPR68bwVW@1f^1%E)XR8FYPSiEbCve6~NrOpb_yu;7h>A zbzp=={~uT96rNcSt?AgdZ9D1Mwr$%^{@AwNv2ApmbZpzUlj(z*=bX9Q7j?O7?Nz(J z_g&h77G(gcMHp1qGR$^CP(XL8h7Ze0uYEejtxXwM$f<23vP^7w?Zj8nDjf~=iZh>O zOJ^8981i~x`#bbEQ1}yw*g=b}- zUYO>yW-z3FdmDhNoXvchNTl{2fJt(nvZ=No^YEs1iumKCC7_zUjX{}2r?=rkLJ0gIcq z!?I+=Fu}e;j$`TAKZwMewubh2LR-;9T>S<*+-q&jH&(RW@?sbVYgpqNQGb83fer87 zc0kRg*tT-+K+Xb313*hD(h5Oncn1P*!iKVjxNaM~e#3_&Li;EOtVOui&H=1LkyB`Z zs_H9h{wqTXcYd?xh@jrz!G#=wyX6qJBX>#`ugcE z`NS{AuBl%Y;`J*&nkAG6cld+gU+8wl<=X613ZJ#~6jaRarDo`IYbBD}73QX?HDSfW z(y2QHvpP`yr!v)0*>!+n@Q*1rVF%qr-M!-4kfMiOfj(c{koFq%32tBMwKlje$#j0t z&&7&YviTc_3E-6-b|xK2lY@Ox1Vf%Q@K3n_2#7B1rBM}r#A4Az_E)eNN`^%IWhW`) zLt<_V%y_Mf8$BViVqKl!Pq}R-Ct@D<0Xf?8-qwpL;gL#to8*=yF*^Ni{AFcDSFVC} zZBz+zd8`5i7%sXu+XC;@4YL$u_HzDhR|n|UCGl1-H=tnL3`A(UKL|xnJi%^+WJcT^ zn*yv-VlgR<*v=cK|i@!luOW_PANbU1(-GL!Z=oVF{7i97YNIoo2UKR1t@-! z?=%KhOrckdf&@!l%&VjS6dE|mg+tNMSrd((a$3r(+#Mdo+#AkjF{{+2f!3_HL_hOT zjaV@n8~{YFgE|3SN7`2bytlEBs?`wyQ1R^7@+F9Mk0IPVgz>OXi=|4aD1l z&d;0}zI+zaV=@hlx3D`(ap!{ftNTTiw=4>C1rX`3GTVj{F{l=Y1`S&7y)cJ8xoAox z_ZUb}u=vpAa`&o=S+a0$iXmyib?N&oa5J;r>;HVdB!CG3Jio6QOzSfp8Cw!o6PEB0@e{pUq}DH~ z0)m46Rz9aes|V^6o=`f5@b6Gpmlt6cR1W;6XF|y0LfI}rqvbf4m0;LBr$+kfy&dR@ zg-<+vESKOCq2_w%?4q}8CP)uoWM7-790``o9eXXm`fCOZ(0UOZwv-kf=G)Bl_s;hz zt&B7dXAf)mMb~0^5gIo`H)|Xj2Al98o21-j4JaBppoUqC>_L?Y=pJ*}qV@v}M-7c)VK-%N-?GC~79<@ZA!Y0V; ze~<^Xz%2JJN3T{!%Kd`dILf%92|{6Ka$_~iHIJH|RHvfzOKtz5X5m?^Op(c!itEY!pU?6Ozx``e$}8cQ*^nBz5ad%;%KerS%BJk3ztGB@XC ze8kHjDPX*z=-iZrLkW-D*fXCq2~Z=czf2@Bt>&-{GH-{4N(|B97ExgkgarWe*-CL2 zZ4%MbaxEJiZALYr6gFN)z@Nhav*JBHpj(c=<4SV41zGlWI_m_FB8jJ-5&xJj^)5BQ zZp31xf&?qPXrJi1F@3k4&A0l?%UxS4l8)U6C(T958TO4nkai9TZj2$80`wSw0;9(` zmV(9+b|ZLQ`K!6&tam8IHlIX2i~cI93dB^KP%o|3ueIjL{YqtK4?~5NMe5-u5M|!< zvwsHl(fqE|RlcKnn5Ao5e%Ez)-Ly@eL4lO1^a+iAfXB*$wSNN>ImgqlpO4mu&j1xr z7{blQcb2mYy zd}rMjv9Q{XfMWNsH>10IEi5oNH8*u-S}|ellSR8`;8lm~>&Bxm0f^&9;ztQ9UTijB z3!sSbkbz6H5G3mOCaHVE`9cXfZlc`tQL-)-`i^Y-*$!V2ohA`SU)eI`r_v$oF6DW7 zcvHA{`jF$o!sZq8)-jXy2x$}O{Xd5)t^_;@G;jK4B*PK^Q0;>yhZFjv4Vyj^aPn&W=+x(0~M zhffQY1gePN8Pe;!c|gQfZ09tbc>#eW25q!Qj0EhK3BX|%%gS{afT+|A47l_#KLEpl zV}ug2pk3rPLrg=ZbVS$>oFp$*jwr{mov%;rBhJN;cHIjtm`_ zO8c_2MYuY&(ZN?N6dY3G^kXc@#lmPthm^-KNntbG8tI$K;TH+PjJ#oDBud|ugO01n z7|_Wu(Ni+vU@%;zoy(GuetV+{L1WqBVsWtqkY4OXjAn~J9X?0EOP11S(uh7At&rMZ z&UHpZfmY3t12XWYS^xc3{*?zWP!iGt(~Bqt>6^NTy4u~(O<_6riw9kw$iIlNB4tc; zG#^1lOJ4_>yl+tq9hC77Gsj;XmcD$f5GIflHl9mg4>U|)W$hrfcaFcA{VrGumCa=y zISO*w0#9fmaASBt>~6rC4m3IJ87|WK zYEL0$Pt{DgYi$_O6*o5=%ryKP(UoJx{fL558Y!0w{Z#0m+wsXSbPdcI)Dde8;UGMa znPYwU2+$sS(@^$36HEbaIB9U9s%!2l>qWx#kP`x_OnuYgPVA^60APl^33z`ruO0l| z%a%g_vUP!KUa;SK1R_glisD~F(mdDtM(oAGDO@Q4S~*WfMY9{D4ecS-$-&ukH> zxaZM5mLY|Id+oe);B`1)Xta=O0_4mV920|R7m$*-D5h(<1-~HFt5~m>JA1qf_eAwK zl5k4A@H|Wj0ZgDavK-!_&wp`TDg=UW7x_sR|D3B-3sVt+utpBqZin98ZWMU`%`rp}Mge&$oGpBiv%xAs!2ZYex=8CEvViLeC}#gn1J4)% ze0>12k0dUQs82O6e&B2IX4&y{8kvKRC;%8LDWm2mkkV2&B6?Er6h8jG;Ib7p<}HEl zShbB@kKMdHVeVo;ZcP#k9OLi68^}Y_WE@JeX>Rz3Lzsx{G8dTOwrKv{M*6S?AVHKz z)L?{0A9uroqEKKj>O>m+t|ZAfyi<(PiV7az&C3$I8dsQ`+*V44Lx!8iEAamGwwA4SJXXZa_m%@L1*eLl_qBVep=0X=5htqOe%fmip;n=<*kK)}pW z>^OV-o%3caLG;8R?u&l*r44N|Ri@rGhM?_ES5OR0) z%vF?emsnU$Xc1E`7zNt zKn@M24(rHK+D!JAf%gA_?@}3TZn+BbMr8LG$N4jL{=H$iFH_x|bWG?3#-KBHSPA%WpybBroT4)zCz|#lLf0sE`m+5#+kiya~$-zBg=Vv)fu$&p~1#-n% z(Y5RI_YBV6P`>HWr}_`a6#zNd)DQ@4W8~^6{saOH7{oT<6Ir}4Y|&oFnv=?wtoo}F zTRFOPR2Q$=>)x7>wtKC{3O(%nfSox1ngFZzbl>&v%M3{Mftj7cq&RjQ3wRu)pp%6i z?$UDuLPTB={A>0ZI<2>ZuqIFOZxC1p!tdSDl$}R#D1}sbV;0xeJb)`nqe=KZ!+U{l zcE@#ayoCZ}-W_ZIfa}5S^?`-+1dWTi^oGrOu|lNu!6EEiPkHR)hrTyDN!9UxP4^vs4XjT<-jsFuFMa)2c%atFv!Y~hT;RhvGm zaD3%$_6L#$%$=OLn_;}pY|}$|t(wwCd82VaH~8%t3Ii+rXGWm~d&W`=9TofVmo0Pq z0-q>~#Pp-wMc&-)AQhR{=Ws$@|F50EHbD{WVl!(>x8?N?*#Y&fYMTnUThIA=C!J76 z{kaAR8uZ|+L;&qgDNQ$KasTU$o4?8~^sVuIBdFi3iUCc1#bjgea3LO)_j@i$bpzOZ ztszqQJMmb`>7FHTjDXLTNH!qly)|vA^4jpYfBh`0lE10&H7>K;=|=k#(`f$AQoqj& z;2wpUO@t>+bt^N}xsl4vNOL=!Gs)}ft(2S4E#?02iZ$3-!lIgmKS2#!{f=`Y=R!fx zzuU3v-$-Ll1V--YbBX6J@WwR`eHLWk_Za-Xh#86qizIhxUw0G@gJTtF-t0>e9KA+;OFsIpC zl@*t+=75hCJO1Tu!1l@(Ca~-;GfnY)_(P@=(D!tHQe9QwH>U==V6*oh44lB5J#q72 z61Jqf#EAKuLjCKN+Iw#bw;21UWzvwD z6{KnTgoS?2T;m~kya)-IcqGAL$ho(BIR5cUJphp#xp_)9j|jiPCL8_;9ldkE{_>_@ z$c2}`cbtF{AAp~sNMDZ@bC-EoIXWeI^=EjMBq$|?EQga1XGzUs;BvWH$=JPor(hd+ zssO^fj9XIzj-Juk9Ru5gs-7o``rl5H@t~XHN`OEOKwjHORjKn2fjoolTw5_ZIzcQ) zp!4xuIxZnT6}y)*uT6H{3OMNBZv=zY(4*9GQwJKZf0J@U85Y5SRwaz3bW<{{_i)s~ zav&N72K;c3-Hb`R21~FE zL$|5jy@75epXAs>BnMew?zULe*jW%S`t&U|JfX5s1tu&4VVP!2DJ$Sn@#5wP6~M3( z-17v_55E?%bH0n0N1oHkTs-rN1ZD*Kh90(S>AOy4HxHv-r~kxBAbJSHUc5!&@Mdww zRIMc?E*ozE++`M#bG`It#pZH$3b&FuFEFf7?sG+<;sAMFGbEO#U%9<0)+{|&)d2vJVL%p3FuoR-3G zDv}3#5|`B^lwac4_(5XY2_Kb-Nt{>N4YCqNmMsF4 zO7-}i#eM2&owg$0V!KcC3m`R9G#i~>k6O1*T~B1?1`}=H@^Ltf2<|XyB3zbf*fu)O zpIt;CrI*ZNrhdOj0z~O|uYOeALy0D0tw}i?h#tt4_o6P)mP<0L!%jX; z0i>dszI9%ls_ds%9>CLFWKLun8jR=eI8m|Q#n+a&d9z-HkT{MK#c&Xu(#$XVqM74tZIB=@SXQH?TuB5pt?twqd&_6iqZ9=9 z`}$=^i*|L|=DQ^IEw&4lqISh$TE7pmdC)di12?j6SLw1Z4-iR62i62F{)B}2^>lRc zQif-^EZo%IX|Ls%f8TKkGaw*R?uaW-a`>=4q?lq+ipeR2d%IZE+t{LyrzpMQ{mE~*L2VcF_$o|Il2=8lGp=uFk zKJ)QU*JQU9SyZ-x8&!=KydUcdX=k<3;wyvu%~PfAZAwhzfR5@Y-C)-)!L^*rugd_0 z5%i^Q*L9HC+9Kk;uzdO5avJtMZ%el)Ew)4i++&_Mj3{8B-}`ee9sbG!Px98+aXqSR6j-3>}War_&> z8_k!M=*6bdqSE+^9#hZ9v_={QIFqe00WK4)q!QDX6K0&gFI*}0cp*|$bH!%QW?(1sv1H#MsWj^Rz&Zmh;x?hhn$ z)U~?KI1S}1FWImw)IqlCNt3ozNnh(#Z3URHQnVx1k4)jQ2 zctv+SYWo+Vt#gg3s6-FIhBxAElFy#`6QTyc4bz95c8d`N=4mZg%{jqdZI@jr{??ck zT*&^nV(7h;OqFRKh=2DE107o35usU=wnI2qhKX|ZQuGS!JaqiUj#3jm&~En@9{CBw z{zVB+)&UVkJ^*}_f|${J?rJ<0K4m3Oamqbp+0Bd-;Ouz36_o=}@ITk)$RcLIM6yJd z#~>BHCT8Ur+osgyJjsTga1nS4e$5H#1#2w$S0kwH&S!ncF>^TLc zshTkdWHbppRt}U()eU~9KpO67?HCO97R&c{GU1KyYkhLMd}kxj1lCRY`quKF zO-*wFh{<@_XCc0%@QK>+%E>-TH)bK?h6|b~+$7fJtyaj_a*_h=38qhs3L^$IW)hd) zh%z&_bShb7YBbNNh}SW06Y2#yMEKGelbq1l@;u2B0!9q^ zjCp#LajLmVit|~`@=5LTp4f}EgAJ+4S677xyG&;M*U7e&SRc82jJ8sZOADs<(o%X> zny74W$ljOhU*HXp37d`0+NdtGI7wyp{!uB9g-j%j@Vhb+$eoC;_EahTc{b%)i$eJT zo?~X?!wZ|`({J!dlqWE>N^lsV{^#1NU|e+~&yB37*(4$0VWYAQa+s{uGJzle zS&YIJUV8kiS={7wHZ}ArjIoU9UKR`R@p0Whs<$HndFf@-^n^2GwFXIvG6?d%+H=iMO;&QFRWiK$hdD0dq=0oAUV|s@@#A%&On7%|ZlS3Nbh0 z?Wi0vHX1~G05FlKXrrb4nRyl5KoQAi#6m*%c||O~Lw9XnS-1DB61Umo30Hm12}Tdo zapfBn8~R;`kRH2Zz;$CB;X70ikahp%GbE!U}LVoQ!wOs-c+%e_1Q5wLQ|DKvaK8A_!&L`AVsr`W`6TIMec#q=g4LDw ztDx`Wgvq>Ya_&IdyYZqZjmFu-B#8aqn=?;OExLnPnRWGTh&;&xgG5)8wqyHP0mNAD zZ83`?J3xSeezzI`ERJkJh>c|B~zHz*-qM&C;D zuqCh+WP@J1os{oWsMchQdINdJb9qcXC0{oAes&WP3r_<8EGQ$L(b9-2bkT1=3W$6+ z&$SHP(T|xlUJj0`{r&_J!*Du!?Q*;*Va285o-GVC}UXYeDP`J*6I8f2QQ zTRLcOG?-qv0NaO(Pc6Qjaeqz6OA_<`pZA1-DbnAML&v<$u>GGbO?}&2bZ=aPz}Oz? zwIQCZ8D;_yqJ3<3sH=qA+@XYVG^Q4a_)_U95D%WZ_z^;qo($=>Hwu9TmN5wlT4ZdJ zm8qC_lr2U8bD66o;Q=|4#YTz5cxP1-mVs!D&GtiiM-~N$OP5Vgj?NWzo=F3S?L{G( zzh967F#F+KS}cg2Vi?p}GCg@?ud>kkoW>;#E`x?67fCST-j zLa6_~RB(doQZgHg1iG!~g3!gYpH#05uJr4^GDA=1ruO47i>$qng%P zbXUcl>7GIPVJD<(I#1WfY`;*KT=)1E)SO@d!_9USi>L}admPKT_R2EfQj6i8Xjl*^ zzQIn7lz8L*N?eq`l0TFq~dA|;P6s{?fip~g1$!(Btv-& zsxVZIatt_K`zyNa&_LaHuNc8eU++l)+CXTGmSl{%n+E68j#oLd0`KAx0V5^PLZd^@ z=9f&wAwaBS!bo-l(k?xRB-d_i|B~N*M~Ho^|U?v<2ymar> zU^44#DU>2S6ATnuybui|31=%D4^#v32>|~*&BV;Azg?f~)CbnXr!;~4M*{n=ztLc= z7TQK7Mrxtoapm!hus5^-j^UmrbU_$XoBuQqA(%Q~(p!$mnl2x&UWI0)!)lgm?`ApP zT+j2%m_OkEGn)ybrqR|X1Dpf-U%5*I72vo3e$>?gCI$ab3TBCiGEp892uK$D|4}f| zZIy<=hX1`1!4lXP_&-;gE_HI&fdB!`K%^JKfuW@vh!G+HjE!8({_!$3s&Wn+OmICX z8c02K5GX$ZK={kopr=M#OqU#Rakg^}7+T8YURK{My%9;^m+buAft$h3LsuXl#E{QWXj4b5n(#d>PZ{1wU!_V-ZQ zC1$uDs;e^rgE5_DKLXGvHRa(6p3s(e)CK^>X;x~$?gA0rbNQ!ri#3@G!nL)rQEZ>N zPIwNpugp=J#|vTKyikJ<@YmMn=BChM5|p-Gp-I%_skrozb!2}vCOvbPGkTx^515Zq zLV|3r-wTe^KZH{`f0JRr2$nL$-;ux?Cfr9KRH72liW$X!{8*qF4-HjcDhp3aD(l965sYpCY>JK**OcuVHr08EO^vlNJb#EaFJotIG3h@HkZI(LX`Z9%fd;ZgR z#5GXBt8Fu^GL_nLzx7wT<-Mroee37zX%A2>r=*-+Ge^|E2W z+u07+g_TdRHZnZ>WI0er{(9~v9!*Ry{FOP#B2+hC{EXO>*Z^{vU$bQT5mYg>X~nOl z;w>mZo-Nhx`ElQ{`IZ0IVTCXIUqw$P_}Thg>kC;3fGQ)8mM&b@*8d|HKGh(dZaKN- z=4<4>Lvus;KX;0nMq0Xnr{_PVxHM?`t_?5~Ku%$Z2&v~qL)Lp;PTjwsAO^=|Uo7^{ zT$Yu7;mVYnXm;q^r*Ji%eG=N!VY}OFmU}@2i~a)D(IFQ0)*2KywvuWcOuMzn7=LV- zhj&6qzv|m#0?^`1KJhQWFLFMx-5p2N}6&&rh#f@DeDj}sQg#mxr*l^h8&Yc zMH%*~Us~NNjI|@`Xgb;Y#nGj!d)*=ja7i1Vl2**b1)@iR~x z#k^$+jiNFH9UAZQmL{eC1eK#Ab%mfrrp3})zh)J7miD>PhE{Wxw8pYYcd;FPrAdymk|q2dus-=3sn1(c7ixRXx9EMX zNS%^qypV{HYS#_|cprtYy%iQfaQ`b6@B)s7s^;KR!&XNl&zLW{Hr8QM#IWbLe7_%c zL$otr{0Ne_(bkud?h^h|;5E*E!zb1yU`olLN~5w>nkole*{1I92G7!m{LZ400ph)S zz>QO{sTej3sAbKsw1s@H@Jc`SntDk%nH)A&QpE%BwdB?kbuV!d(yhgW10<2Xfoem; zuoh!kOyrmw2{(qWz1^c=+1qxo5t~ViOt!|FzN08K;S{J=BSz{NGBih>h1vB(tpjt9 zvZv5OoHB4PQ~%j~pqyD7gxqNy=_`0Zzw~r6bU-@=)K0B~25%u0ci3&4Zu{G8BVx^B zl<;Sip|t)bNyO{>V;IGvrf8Rb`TfF|VNjS{XNQ+l{eW^n)C##skCuE}i;4o(Qe8FN zg-{8)ftS&*s8-ZFA=xDm+aU22lVa-0R1E)BB>G^6&uE@oE&C^1{zPZoqmr#lL<6+Zkm1V8T zz);veaaD?7p3W*3jMOtGCo^Jio?X%y zvbmXaj=g(38plp(inPJFJ{W~uqY#@DPb6^u&5iUu1sPQP%Sc(=csvkhL)2|-^0$Wq$z4N zmx*c|+zn8GPl53&3fD7yq}kHgpk!@v2eA>_Z(;+L7^PHC#g~C8^tw>~!!YrA=J{#R z@^nbs=%NhhUA#QKp?N6;(yi`MJoVindfLCUK_dZF z^W|M8LMt+>?N>{g9QPQp0zW=vb5y#H{?+$b8aY{~u0{mZ)M{ZTRJG~01?x?UXwsMZ z9Cs3hBEM01?FZyA_8QM#@EP|RfXj*(Sn9DSLcRmyOUtSVk#VmpKeoXSoX&zFd|Omg zB=1njIM61F+t_rn%|JmK}JE-XEi_R#!_9pP5mQ3CG z8KcXy^TaL+E}%e_0-GywYi?RE-*c8xvqSzObb1|-S9KvJ*#s^f2@B@aI=XR#E75%{(3-y>w{=jR>3Cu7)O0RO8&tA?_TE)N-G=fUnl>S zp6Cip515QwWJ2mbq1ASgE+HmlLV-eBM+E_s^bw?T-k*+PkF&d+X4t#1+Q?qHwv~4h znAlP~+TH-Rbe_l&f}i#ds*6iu7seDLOqTEH^b#bksmndB-T&M2Bq<%h$ zI#1a}2p{xtB>=^NAtDkQ#OP#D-kXVrA)<$1O6w<*MluJncl#5q2}N@FjPyGI1AD|o z0^l7qC)%mF{E&(cx-hL8O7s0z_L2C-sNkr#NPHjSly4b|lU!+}&Rh^#!`^E;_)NW|! zuxl^G1Yxc?$J}tLq%e0+-4WdC-}G;CG{6f_v%s6@@O~zXH5^H!s-t~5ZATE*2#502 zIb;yAgbD#x%rcy;9xgqOnR09`Ltx{b+?)32i!-}Syj_FVYNpryz@)aK=LcCszK z`Yw(R$s*Vd-+Mi_^Kkc&ksUKslnq>x5%seQ z0BMA1i*^yc=WQA;_G!U>^Fb&t z>o)e`K?IxEcqOw8U4*&4@(VNbC@Zb)HU1bklcpb1csn8&y>>=IN#2<)WSMsOJkRT6 zrQeY~H~PEQIYiLYa=5e6%uPIS)D@}30F{yaRU#9BB9vbY2@avMP{ak7&lqa{t`p-f zOCcH#$2I?rtYGv3g{-v^*SDpk=jeP(4YeWsC=%8UQF+QqWmA5&gRq6zpw3O~l#r=4VU`x+l0haz}Kl3hzy-qzE^aJeM|Bs#4I+@YJ0R>!T| zI&QA6qQU%zA*SG0U>|+;+@!x8I9D+TZc3}kab`gf{PK}XnfY5YvO^_6VUn^%&P}~Li(5bE^>`T?qDAu>`q!(t$yk^Z;8*Pp$y;p(hMs^uLb&x-4 z*=qZeL=BEEom6-oaEfepN}R)$-0AqPL=oos;7e>te?}&$qOdtJg_eOoKsE>(%g6Ar4M$j)>%}6k$sS^Zs z3`l;bv5!0u4>Xgn4b4ex#wmSChh5(-`>5VzLt0JUMRl6i#tSAkKs(l&USvg#$ItED z(azhB4RAb#H2ri@&v~_Jy%y!FnpT_RvN^xaKVVEgSQ$(@x^$d0MM!W3nA)_+_EsO7 zfi{gI1Gxwy2YLblx%=Bl`EP-uG%OhbwT0$iZ`ORj!Ei3YC}bf%DG#V+#o@{xIMgW_ zik3f??DFe&2}VxJ0LDqZww+n2Rd$^NuGKXLQDX@CQA#QP1Xx3VO16~_)QXYt&qObRzS*!T!rL=N4)E57hQ05prC!OlV4-{*;#p^#w) zSvabwF%K)WsHhZ25T=}Zvp}m5oXK0oDBL>*ZI(Neh9$P|;)X-An&R`A#=q+o@@0yfd9UZyiKa~6dTShDhmdtb>I=$8M`>uN6rPk5Ka zXmArAqD}TtDUr!+j4m3~9z5$OE+V*lKz9QsWNr)F#jEnOL#ZTU*zTo1V%j~!h((6J z8>WZ>vooLBKTyIu{}b1$!i4N^nsa2n7l9EgV1bS3! z%9nNq9U{7U02Q(O_9y3eSu;~#s6T;(E?sxuXP8uCgCzeGkGFr!*y?Aj*7WygDn=w` zbJD(zr1h7A^vvMVP794Smlm3N#_u;qgRj7N^6czlkea`V+s;@GklY6_e3v$(`)FDP z0L$7c4A}!@ld4zvh`a2ATcd&aV!U10Bx*Y z^X%=z>V}2qf_VP=+s5mV=t*c#Po{lqL5`x`r_#XU;r6x zIC+|Es|p%~gP#jVy8#j_H}MJPfHQ}D{>DR9Hpszhx>=R9r!tw^P^mCPSMOd-hiK{h z%4<$Km54>xen9x_lDz?pt24H?Rzo*UgNt4eow{GN@C#ALAGUyLFYjsoNK7lg%zTjF zcwUe(F#p{gvfh<#QUu>@uL~v@AjV9p>1m2+L=gf4>L=!+HG{-m->KQc6a=&Ul;R6k z%@W&GyZTifS+S{NXp=cgc`!ikJ$prLG+Ls1t_SxJpG&q9c5&`wyRwK8_ zyO2EydfYIM9{3vq;J!YmG&rFNRWT4h-CTQx7$Ua~MvxK7R3!+F5XruW^VF~i<^H_u z7TQyN0uyxsA2m3&CNcQlWII_(V#hC8W96h5q_^qLguajzI@Z)8^+*ojHVf6im``@mT-o%yevWoi=)x3xgR zK7SxOX=Z6Fz`)*_kPSJUp-@z-9Xei!cO30u3z7J4HZ1W;*2IDM`xNX*{rJ&lS^k^$ z^=NuVUtaI2 z9#wC*V#$IyLgPhW!2)?#4~qywn4%&IzfEjYS+EBW zW=%j~GVDeP4BPS=KMaVt2O}Gv71`S{#723*Zp;f#5Qq6DhG>mH`SY`|QAeYEMgEbY zOUwdLRAF)x;pRMn{N9i@>$!a+n!K)5lxEil&?iqucfOl3s-4@2FiXGY8d@f>^76Sx zX5#Y``#WZl60bv$A=N@+I?qxO5R?>Vla%V8@Rl!HWqlq0VezMzr>xya$B8+ZHTP>5 zEL~tut;T%$c?7rPYLJl#z-_EHL~b=C3K+LZ`<88#FbH*3hi8Ffa@O(p$f37#K!LX9HBoa@fV4P%{B7aXD##z)11v2&Np zQ@OH=(TU5;@Q$0)cqWDTBnlEhrbX03v&4-@k57kyOP?f_Z@L*cu?veUIaVntH|!|29}tre<}KP*B4`&q06rVgH*E#W@qseTxM> zfj^TR8V8V}LAM}#6SGRp{xGQBUAj=U1D(|E+25pNRL-m;Uew7;g#; zdkiFg!^g6+Z49%m&Eut~B)kR*bkXo32Ut7=%UY5ms)z@xhDeWhkm$@~s{ z3(fQXyd(EooCEls`WYb(J#mI>85WYzZG2Bi(mFJ9;4hn1d9^b{HrYXS>)dS#dsz1( z0=;NV(&E*HITH?i^lAj|9G0^JZss<{hzf`Gqe1Y4k;Uf@$7Q4;t8%$B`~B}48X|R& zM1mdJjCSl12!}{>uLQ|A(t{3ePOqx^--IY}>Z&bZi?PTVI@xZQHhOcWm2sa`Nwewa-O8byrvG zshV@mG2U^N3E@Y;oWfW$H4W?^S_InOvS@35R-rpGus}Y3zDw+qH;4wVC*?SC#kFxl z+11^uTfy)C=WfjtviN-D?+&hmIwnuHoQl5(i{zggDrmGKe`;oT)yPN2i9W~Z;=Ke| zyE9t_)eI&>=h_;Xwh(~o#3TVVe-R&?UDC~YOzq9LY*yi>@F$OJB!zt0?Er5rw}9m) z!nrxILM|U_>jn79lz1jFpZ+?XARzj+kcWulY{|DlEjd>nkTnOdLyJ{$)o7$PD4Pm7 zt#$+lTpQ4P5SL*EyPnbsU%~eZyO|+Oj{sCYpa}C3Y@0Ll01t2up|NhJw!|OybzV|N zTdqVS>c5nltF8YAMoK50@IM=sYM#-MY;>kEpZzL6e8>A+V`4*)>Bpn6Yp8eijWdvJW!Hp#5X z(v@(iv!@{++a=I*Mh8#8<{zltHLBHE5Oy#=6TN{#JNiq|Z!ouO=y5shu@{5-&50mJ zeSCai%@(RNiMad5Rr2I*f7pW(ZK5>$T>|QB$-#?Zr<`L$MYY4-Khi*$sq^B$M~Gk? ztZt^MYzIg-xK8~Mur~Lby6Rbm3AEpF=lIf%aA?^_JB$m++($FW8L#+t2O9YOP7HA5J`nr_&Ou@QF0>KZ z4hJ;cCAJ{qhP>eq_FgIVoSL;X&7cO_H_0E&2Ai_JNz12=7SE%xi0q~udGnkOt%!%g zzRk+o6L7k#X*`U3C$Q=3C8}nj+$C{S`#7iZdH|6}Fu+2>5WQ#*Z_^A!ibNisiysI(j_vi~H}yNY_3%g*&a7 zI1k8%YdIE!h6WEQ2=GS;!lBDkkdkCkaJ&wJvt!cc`Q{6BcscF3KaqT?0@Nw?YysFG zLBFE8?}S>$Smveiz{W8f7warFo^yMQ>*J)OahJ~cd)oY@vr##!29k&FIPWIxOCapqey4}{C#@D;d`%Y?0E^6naX#{dD~2)z_a>GV=<{&_Xkg<%9;I5u^_{zV92+=vui z<~=KKy|8Pz#B!Wma^x3s|D>|9M6m9tfbo!^?bQ2Q%PgbY1{Y}Bq#}FuDz<#o#%w^S z`u!kBkGW9~kKIJlf#}S78?eGU%@N|;EXo?iQ$10nHP+t2CxQ>-%a0L|7l6P&cnd=& zYb{h*xKGij&O85vd?KKeF!j{i!ItH&{VGE#W|-6Gxb}#!#2xMye*B+F%ANybxWCtS6G6+UvdX1L zWrj{qRbd>px&hC{q1DCO1{!R!xvfh)08}6`CUtbFDe51h zdWNHARkeaCph}csEJ+&cLxdQQj|(#HKU=Hu5Z)sCuN{xaK)+YXTsZt(6pb9fkH3gg zczQ1j=&(MC+ygE#B%u5@%gOz#1d#+geE0(T>}WJhIm+B=CMaGJ0092TMb2EdPfVPf zH^`bKck3iu&&?!`k3NN}T@3RNYlChobf?NY*mA%5M=|WA&ss1!f*(7J6@Pw3$=ewd zl(@1daM!-4oip{DDfs2F?w;NbDT(;}JJ?i$<=#3PWlurq9J$N{lCq-%c49tMx8r~t zv9b}5P|$G{pM#n_2H?=P0I_goZQAe4*}?)LU|!v6xZnE47*se^Rk$dQvJP!uYWs4+ zm2K7+IXDfH%krjxJaHLWkSk~(7wJQ8W)PMjdMjd2DAMfV6VIi29zs&yO7JPBDm+Gh zZ$e`~M|C7;Ov>IGzh;=Gka-1cTHKLiu6IhSdcSEK?|F(z5wMn~?c=d_yp@d)`4huB!?iz{W*mt8@2BCR9?1qx}5Om?!f?k+_eZii*%oD;})9o zc*VjQJt`IE4DikmPIX&an4v)H;+T@wh<_(i*bT2RBCNVDd~|@bQ=w;Zru@snMIj=5 z59X0b3(Yt%1)}`Tk6Y;9Py7!E;t)sqL?AQ7g$#4pTOBG=Sj$3hlP6ku!UJX|@_Smrq9g~>|2jCG38)Iu$mCJzsSfe<^1GR4W zS|_gpecOnWQo=3lt^Q4C(ca@`$3G1Z6WwiDP8rqQ)XQ6-$8EXsFEUQuCKGFeT(x0< z{b$<5mmI^#FS|aBD|K3jiBtFnaL@!Rc1Hc1DbWQ=MeMv^!0dYT&auiu*2xn-6?RpX zWlt*M^+lYK#1?cI{IonBi~KW{EW*I}W-j7|xVv$kyuw_p^UG%U zyjRX|J_Rnamo~sR(1Co*u@fcLG_;NHYb2|4GY+y4iDOc66WxcY%VW?}&W;kP_i17rO+IVyly zvYs9p2nd|_KVrca#wK7nlMW$$>CX7mDWWasBX)^j#^D=R0`ez+ab)N3TKfTiEi`}WinNFqv*S2y^ ziVmlN=J4ja@JI1l>3&=qoO-*J2G!)J%P5pV@}je7cS6vhOP&f9V+)D4P0i+gw;$pQ zdQos3qg)$XepiB4(}4^EMv>u5jS9`m(TQ&O+P;r%>1mSo zt+;g&%TvP8sf{Pi2H?7AdCkEX9kvFmcC~-!&qtZ8H?u@TY13K^MRJN)(E3a_h{X=Z zen?mJz2U_iwLqo>Cj|U*&DYZrfn9h{&+GFeppE!zFx!~6-$0SSi>veI6)DLkn6ABG zv#i~=A>RBZHS2sdiepruw+*t^1$!^^c;faYc&-N1Cz ztyzT#gB^-S0erm3$7w{K!yniY-5^ly95*RFdENgj4IvTB`L}7QIPrd6<@+nq<`6S=ec=5mpP)n=f_YKs9I{yx9TXvKf6gJ!D+3sePNMRu}riDW**|c+$lR zbb|<5X(HoRbhFJH3@Od}nP?Y@p89O#j(F3nEEckZ`HppxKohE$KbfD-^|4LSOwLCy z+o2J62EYlk)zO+<5SlDq`s`ZJtSk47Qma*pmvmG)fnwEI4ol{4hO(rLnG5RzIWwB~ zveOIi)VZ4Wc*4hi@#vnEPd<(3NE+2;j&Kd?K+Awu4rJU=CX7?d;Z^9lze2+Bmx^eu z%G*BXf)pjmoor6gRpgQfpZUe+JEGmv4*^^(2*4Qj?`#zGaJyB>+JuZ=RJXv0Mb*pL zDA+9^X+a1lnHwz{flAi!d{#uYt2meq&GwZ*@kNguX1d4#AQS&&-80%z@Xi9Un z_s@1^tYuZYmf4-90VQ!+Ly%QTNrma~Shd9`)^MMv;)Mvhfx9O2XS=JgA@vr*5#ocp|Br>FIRi63b7mU9`cjd zKct5oG0xu-y|NV2nlj6pZ&xgkIK%R6N*}QPcOHewf8LFL@8Kw#cv3sXDFduuF>a9a zmBV3Pque)s!6j|hA?UJRO2|d1&jQC_Y97H^8iNDY{&`zrGM|ClA$(Zq#p*^btBLvv zAZ+HG$mgydjUwPq@yHfbvBi#MhH*McCjx_-BXsU{Tboe*5y87>II-0Yg*V{Y_aW&K zdL9#=xeTKXpiE26`2E7`<(@Iylk92|b9m};=%em0_b-%G6awuJI#8zm-`Ug2W&2ph z-JcekGV21ej%)JN&hHUO?JW?GpCkJh{uCime#)5A{RVo2cN!u4Dl|fa57&>_>*NcX z&&g9kc$0?;J1p+Wxl-Qy`~5~%B$d7=yAl&S!hutiS3K*VNB;|j??6<(s#wusR;s(1 zAKN)ZsN*n4NcX%1uqM5o-qv*Em)YcKsZBTFb!y`APp!`T6J4WKcSn)m$M<7XwellJ zlt<{BdMp^3<(`jpY?T)5d|5LLh9dy$$}@t|@&#I$sm+UzQN@w~O7+RP#`O{TXop%C z8U?F2+>1xY?-efvetReV+znpWpi@EwWqNd%LEW?CZGf5-shh?j=lY_XYd@;vNy7oa zR5ky#u{8bfbZ)l#K~xTi&uxdI+l3AdK~sjY=^DcutL#)SQn^wb=p6$GSW+?Ck7aX? zfVVP{jF#orCj^#pifq1Fpz=1?-4lLlh%GN&p`W_|nU&dsERi2Uk=?sHP9jA{WW@WT zHsLUuidtg85qeWc`Pob8tfDO7mv1h>$3r6XG<^6%S`ZD|rAmo@&f#w1CiBv0x4&!a`X>ZKTxpD~5aNykcA6cc=QbV-g6dw!tnGN>;_K9g5S_5NIz zTcHSK_#~dwFz@!DpjjPHM*Y-dkdBVgyQeEw8|_lddYpv_6Rw!BL*`$w9X)=4(Vz{o zQu}!n@$Rz<*iZ`M{R4?SJ;g<9+@IY~X}n9nze{;OFAt~l>AFp_c{1sprBGr+CJ(|? z>@j3^cn%Bf z{pvIgA#Um~`lfCrz~PnC6#^dH1_N-QD>bjpq-0W6?NUHeB7jq1Bx z!+9ffD36AOiUVPcm)vNJQ7IKQway6UFDv6bD25CkFMam;mL;Opcpy`gp-+n!e|+b2 zFhZi(qJ%YSYW(7*9OgorX*$o=7FYkTODKe*YjI9|412N}Z3@r*{;p<-*X#BEtC^$0Hie46CA?R%9Y7iy5%0;2wW*5!mQ;uXoqw z@mtsqyKBQC0nK}$eca?@mIgkDQ6N5}k~2Q<2rW6WO4D~H1Ei`&5!DcB)S0TIe3W~N zB^3E8r}8KSZL-!KX2ut6Z^oZB?RR^Sy+op|8p!j!L7sjK3?>VTCu~kS)HX3TYLc$Ik-=^>0YF6Qn zZfQiG910Hchc z!nmlrZUnHv`|fXXDk_xTK~!_u;8!r`2O~TyR*u>{azLuoI9rhDN%IB@8M}10;>&~C zHB{hJpbW=>77+a4^>1hxFhJ*SnYNrK60qe#NEl$5j#Rwe-2mfHC86fn3l0-k)MFC; z?#CNwRp-mud3;^;hEV8~-;xz3d|GZ89HQQhti-G<&EyNc#Tv3BVxZTff4MQ?-5^~mi!bY443Kts z%%9ATL6!fCF{UW+P-YmTc<5np0@I2;RY$AMhwa%_{QOXBbkW z&;`U}7rBgLKvh6;J-boAQWcS-Jz7Oqh@AK|yH((kkPe>98$>UyTZxrGsFsU52ueu+ zuWFqV-rZhBcM6}zQ}N%3^~2E4C>~3mP;W4kFx|Lc+a)>2YM!w~hmIotDye*+eGuSS zK-kY(~E3|28teIzR5a^N8L6{fYE55oPBerU(8oV3g6WVo0$%}3{!zx6GtJwMCU0gal zd2}g>m4%jPPt>eZFinAQgyoXwUmItDmzmv1_@GrcQ)qz;;@^dIO6fup_ThX6@(&Qr zy~;aqaOIFun0Ua_u6NT5Ck%io98ke6?(0LSA!-iAGqsUpxuN51#;pG(X}wgN4uDt~ zi5Wpq!8r=!mmm9vawd3YoptT2G!0*bJk*y_ZGf?>0MQ~lWdX6sWw1lqlYwrCz=M82 zK@2;O!*((^4klWsW`jVp?HZzA^!e!-ZVJeLpUO&b%I7Nx{3a;kc1}dad8_<_jozHn zx)}bWZ^{z{p#~{fIedT*d{zX=CtlIpTky@-Hj9uyxrf9-FI3?Ra(!C37Gl(x#JJgP z*)AD?b2aIFQrIPL8P)RPTcFDFeZ(hw)^8UBwN3N|VnD#e-ATXjWYRyHW=5UfS}mFD z`QJOeMdA<`_P-Z5YnH=j2R0B8i|PNNK_~zz>&;0&`2{<(8OCEYa}Mi;nun8D@|O&bz|$s^8q6=*(pj;UuJo>{kT;%y25=!QON*n%^EWrO zP$_bmuUFDAt1nlz31*$5e{ZVC<%zG6+F1Im!M4x!g0qI@NS6AYV-};QzY6q8 zg9$oQG&56te~GITDfKcO>i>Ko+G&8rr0lhdr`7IiRiXlutkHkYyc1iYmdHIz3}*e_ zK6KChR%fJa26_jF=sO>B!<@@$fBR`?;{W1SB8?q5X@)vzgqE15rHE|)dg*V@W>cp zuKK_n#ak3oE~fY5&tJz4Gv9PXsfAiRSeQMrBj)SAul$d0FZ=#f{h){A~?ff^>k*g1PoA zTPc}|Y$Ut?Zb+WLmPI#Vt6Un#(6Jv+|CUgqc`ljNPI@_l5XT5GwTGEhGEZ2dRxPvX z+k5wS^boE8BbNK2(H_8K%NSoQh`6W--K>=h{&<;S<*qk^8>19@T-L9%$NMoT`C@Z8OZV@Fa;8@ zz5|v1Mm$U~=<4^tAgN)H3!ZCMiicRLg;Eq%6vYdrhq81?l;`n?E?CV1EruRsGGBja z%-7W(7FgKU$KRM^qL4{ttbz=Xi+b~pDEcUp4qW`T8-q=~l0%v3tZdi}o1ur)UitZc zwHz#9#{+`UUc5>R{t@D)y<=YdT6@mDKZ`!7*5vs`PC1e{OHZ(CsQ3l)i1Y!8O)2h4kuJaY@u1`V zKIO)USQm#ij7T|?cT+Ai+xprhu}}RCCJu%JG!1A1?u>^+nG7U+96HH68x!&$dUZ+e z2SXyKo+Ot#(~M1%M=y#<+u~RjFP768FG%E!RxFTq|7QFMHsdC<=WJ#7`A?g zGJeS@5rW7V=CdXGp{?~JugqT?-jY|OgZQ|XF>y6OhBP67?}qj9<5>M=niYz77-_VR z%=@!d)#mR#1C&M$HJqv5EMa|3()~PflMcXqOxW3`ZvG@C|IiN2&MD631M}-VhuTU# zuL`y4JO2RX9=OIm`hX@G;&%fXi-Y_<1C*l`=@d{(Z2h~{?{Zdb)Fdb^i&?6#gb)vC z*O;#5fY<*%-XxAkaIyTIQDmuB9EB@aIh@*V94gR>S=z8ox>r0907MJalP`mm2?K~i zBL_;BnJ@}MIzp6#BBc~9VIDiuzL~-mc_l6JSkZ)2W9F4p(i-PnlmB}u_}1G9p;xs4 z_*;ESt&kv@8wisQ<`--LJ_|2Cq)Lr3_cC%|TW#V)XIk-)kgmfYHV!%xZ-;DzS^fbMy}i zqlpZt9@Mj11h;pqGTbIM8iFaZEhMazg|;z2T@OmO2D8>XQGLg3Ki$aY--07~>_BXo z!Qt0LZ@rS_$dB;U$H;5*az6UlrsvLOnryZqRd(|znZAyh76lgUWBs^n8wF597LH9v zJW6#Gb(F{m=ZH)pTXQ#b+dNo*n^zye}EP5 z9NY|2!xEK50qct4;sK@gPf9>AcTJo^YXv^D*sAo{F>F7B${2160jw@Zx@K@Er_h7R zZfM2C0sbd0i_c!GuhtM)F{&tSDe|FWX$`FR-yziG4L=a^m4Xg9`Qp#}Cm!M>d3eIx zmg?8v*DDZ~&Pr=;dn8;0yLXDAe2Ce-84j9l`D2_s?g^&)#tVC?&Y1FlJQ?JvN%#ZO zU_{rdvlfB5b-~OzmwCwD`1Hwg$xR+5oTX?zphZVw)s5BPsWe71G)H5C3I}VCC>}8`}5Vf#>pNEP#e1aFMoJZIUo&VdZdN+HqzP3W*7w zk5%*xEQvJG#!s7XYhIuO*rYrHTpi-d&_wpWa4jVT8^>&x@Bn)7=AxIDoQR=H-bY2& zBuRZ9*a2>{O~Ug0)Ba3i*I<_RP@q9O5H{>?`kwZDy;8uxbxDfoWaEdR+ao|d54anN z`mh!5P!mIbGjGy6BXEw_uAOFu?UJ1+0U~c&O4^tPnImXSmcl}d$9VjvSGX-aFv9S6 z%6*9P8ZHW@^#RX?SNCdx@TU4lCtQ`Bm8iB%XHCNAh0M3Kkxq|B^a3Xjz1}!xJFJ7z z0T7>LT)X1)Nt%nLD=P=mnvPPZTKIAmlozsimDxQYkN=8|pEWKIANAEt$>0YI-Py4~AQA|lcq(xwP=U*Bj- z64=b@@V#eWwc*!qjz`=Y(L=u2-B~wg+)?&bAg6>!J`Nt*aFt7o)n1x9c;m$9oCQ`z5)tt2y0L7Jv1+LCOozYMVwW@KK?>p(E@)H3Fs&XF>& zq0Rb!2(J(Czuca4X5vChd(dUDTNv$I9yf(EIxcS-ux$(x7x*_UfhL+yeEi~2OB$Z5 z2W+J^NJ^HHz^$sEy^_>}t00slh`^kO5R5VGb86UuJ*N@{AI>|6%*NHmHbwAnEvp!H zcwToRFsN>>2Q$1_cER`6TIA~Cg7k`EM&Wz)ssXh}aArnZ=ei})<8{T~;T+Q=^Li;n z?KMW0raraNW|pkdI2`-<*Pgj#Wi;bGh{`b5P`Fr%IO1CNeMwP)*pb&Jm1^VC!VZHm z?~uKo3iss3#W<+K;{lOIdDvJ+Z;5RERC1^RU4|40ejJ1`xoJ6)A3fCb_1h$r-wHMQ z-2lu#OnO&E+Ol)tCxx4>7Nb1~GWJg#^zx!!0sd`ZO5OU3?w|y)FUM1!Q_mQstAIwW zYT`4FGv4%Q`bqfa7FUm?jn1UFm&> zc-J1OGhW!_RH81qf{Y^>t&dE&R0Vh9)`0qcOrFNVAY}Da4%zx?)(;psr>N^6%n`e^ zX{zB90uR12fB?Kb^}#X2b$)K6X0xuV-sei@@g zVVJF8?Byyn0+FkMr=5a{8?0#P`Nh%voDV-!+EY|cGatb#C$Z6*p5-Ok+ye5F6(H=1 zcSY@>$X@7EbLazwxKh{4Cvyd>E3uK;^kmbHr7a5G_F%s)at$FXPIz@23`z?a3cAD` zvmHkH6H1le#dk+C>r-7poHAKOla<<6{7!z9_`3DS6`@q$IAq#S*&3h)ixY5=KOZ~` zUN-RY^`a!Mp1N2J1>r=hVp+u51xQ$zbCx{Y_B_UAq+hpZDJA}`r88{!OMiLt8D&<1 zh_c@3^nIhJUE#zCbzUtp)s}Gl507jz8|rN=rpQN)D-6?yE%1QsJ@as*h~Aj3;39Rv zv@O(6-o^!^b+N_JdZwW>RqGtyIHJ}7dwZ|0`Wu?_++R3)tC~CG-acU^N5BylMJWyC zA!KDeQ3ZM6}Y-eTjMh+cGI{Mu_Lq704DCquGpn zKYRR#O}o!+Sk-VAlyYyjGJtba-I!3S392n7YjSxP_VY1NXcWgz=!XdxD%eAN*&*DM zzxM*)i~?S_4~L`t<^33{tV^FT!G5kh@JR~@g%{cI4n zgaVoq!+B(*a#y!H2f_)>zUwCp@d^zE>XK43)CyC0v9~3Qf5VR1n3z;I$fWK&7Y!8> z#g@`N4!e*2_gKmPHSi zy=r-6X+BE!L9Xp@zP|E4&vA1Odluo2%Pzc>suDKxJe1V%Lq9-37Lr7W*r?p5xEdOk z5Yxbx?U>NN%V|+b`jSh3xCSgp4GN6YYV|_)vuDSCui?_4f+ngqZQUA1>jDE)ao^T| zWXsi4XTS{+^c4pv&m8XA!lT*Z4s_zu-e7_g@n0TFw^N8fGApm);=x|);O)JZ7orw4 zaq6P?RxWgZBm+RWos_Fsh#-|^NLUv^fb2SM{|>USFh?Mx1y8=TO8i8bf5syRTFMny zyIJ5hxVUVfz*1vOMjl(#K^5r->@5gS%d$ZdFwMFHpSY@sXiTmLnw-{J==6uIeA>@o zp$@#0LJ&lsvhDCbt9(;6p|(@l7Jg$Vavk56b43A=ECIB}HE2Qc7G3EF<621te=gP9 zMQYqO3(K{NyBnK*3_FS8-SREQi6t5cI2tSb$8k0IuQK)bwFFgt$*k?T_hT6My3A>QJPiD4+`WIW2 z+TA53)}6)0YoOm#Oaq&goy7z1kvu*4bKL#^nfziT7$YdkqOE=Hu`J}{Gnz6QuvXF1LKq`5!;=@&tD`VjmX*`;G?ZaWrH(k4@0oE z`T(HX_`EwYcsk^P+TXpAR)BnCRS*%ti`rH#+rD!RWzrwe7&yYc zeC9bVP$)M&viXq;q&T$N?kY7FczG>ARyM`ec2yH({-Ak(P!>B`2HYBRZRhu32BY2g zP20SuoO|D7%k6EltYr}?5u>Lae*+3f!L7v`8F`O3wYf8rRCmadFBp0tt6Y|e(Pzx! zW*Ya@D0J40#dzT=`S{($z)}NA)vAo98s6DN*A046t3qkuQIEW&;Seb4a?cKe=-RE! zU8%dgww(JcBj_Cao{3le(CbiW+0haz<8*KMV(q%9z=a<2=?1+@#JQR39RO1NdH~Y= z16iUzC+_XAS=2bjL{UJB7hrWhZ|n;D?Q>9pnRS&P@LW^ms(VV=uq&&>P4lm$h+6_ zr>#7-#q{7j8Ey-&{qt+u`E}K;S{u!f`C6OLy4jh~b6Q3vwQ;B5@xYq*xyXrZert z*s^Y^dqa3TBKr@QYo~@Rk2A=+PRg-0^dqNZ)pPCV?+G!k^9q$Kizf!V{Qs5}wtO%; zR1ljno(v5T1{Q3WfC2ybU@jl-JL%(wLyWo_pHPKp(LYJMF$mE7J3%4Tv8!Q+|G5r$ z?fnb_>zgsRR@9P?(mDowkVmRq8?x|Wej?}>7V)3Zi?^xlMVgyAR6&rU!ZOCMl2=G# z0);Q^Uuy9QIPa<@ywtf?vPxEGvRkjCg1cXxdA^m!EtvkH(+e_?F|jL zc&=Z5;pe~6xPQ?Ryp&Gha)?@Kp#(6xufu=9W;700*si_XRQ6S!+g#aaAsmvtaO1tT zTd=^pJ5ZZrUGehk@1IZ9PBPHNM_hK zpjEdPj^MpL?Ev&)Mq_L2NnSdwrn)ZV{4+J_dsiN&IEVhNujDW;4ML$v16%r*999YF z>fGk*m4CAmM}*W-93odBg5IKeZpBwG^EG!BEpAxboeJg&tSCgjHP2B1znh+2kF zlzY;pp<-_(!Lqk6w}kVjm%m9WBUmXI7Wnx}XwEW(vjl@rljRg?UN3r$IG<$QSg$n#Qlq6C_dVWx-!X20W&MQ`g2`>Z z*_q;Rc6+cO+y2&5gAs0cjzy+i`=^13lp1-0oKF5tw*`^3nJ?Zq*Xv?Xx|icDLl}Q= zPgRMX+G+KqRBu%|$RpRA4_OhXQ;C|fDkTu83IIvpALMN`WSTz6EFLr1~(Y!N_^w;MSDkt7Bor|mDDOm1Y+n86{kez6Qb&vn-r38{QLf`_k zQ~*9emEVG!=h7J2ktC%x0t3hmcyrPDSv#iG$Oc-;RPA9@9(S zV5a$6`$W3#hSMP{5&+$+`75vER@j$Rl~@kXNHL;5xN(&4#7&3p}2t z_^4wOh_lnIlQGhqk=BW5Q(9m9M^@pqo8Y|0Kgqu@4P~tuT?B9B%~RS#1q*CqxylZK zE@R&qHz0_bR0QDuy407cQ?f5?u@SyuV${2Pbuex!taiOGXYw551fq#V)WR%Or2?u* zN@~Vb+)VI2DYy~87B->>EoQ&b5WK5M^Jd>K<=w`0^rdV4)~b5Z<$Q!qb3JmzpNgkS9`@QW_#9s)RkCsUa?dea?zuY` zGC)X)e3Q&m!@_3#<{bng-KtkNMn;TJV1}>d4Q35Gge^%%r#t$icoVw&eIt!lp6Yh8 zu!1Y_;~|Pf_`TXkNlMj9MEm^H&GzS7TY(P+joVR|)pKV&gHd1*z2=y;>O_2UICk>1 zkSBT-L2W3bNQA0fhlRuE-EkK(}5;HnB;|(j;mY)ws(SJdO&Q@%MqjUdy z4gK&fM(Y{g7R_uPSHp-7qeEA5>}@Qd`C4qCV+14yEs2^dUjW{O!Nn5TrqGt!xA0y@ zO*?Mx-4Y72kU3t?;pb0vfZe219YHGX=muP^_moidK%aBp&as4s2W-gVxQjyZF+gp5 zQP=Au7s##kiB?;+fGBn7=0qS$Yce#$vx1*c{lP4#XB0gme@9gn^&)6&vjyi^2} z27+}KM?;37^Ga)x<7lzJz07V%56~oGEde1NPs-tXC{mGq2=Vs{-$O!cZjw4EPneY? z*uEC%&V3929^C)+g_3PQNzG;^V`o7;(JF9`xb{Y#X(vJR{w9a9{_jS-DUjV@GMa&O zJx#bugjI)guR44A25^Q84iFqt!fJw$>QX8^|FKALqid$7`vEM3S)qygzuGMFNHvki{Tr*N0&zWKwW zruPM?i{ySu5?Z3Inu2~J^pE1r`gYvQJ4Qwjvfy%s);o7oT|eIVPV+z-=s$GKO*&;W#Tx>v|x|1b9;tl_;t=W~3GRt2VrE!mn=TT=@)wB8DplCP67L%9ai_`ES&ephx`uMp+EBN{9$@|66I#=t@d5 zYdBA>0wL7uJnH?gvk`HX6Ppm1H@54DUolE85$29lWs^UsKp7xsGkd9>CVJ^ch)Q7U z%?OPN&EK_`jew^{NtibicayO0Mo_b*=hG>zrrgha#$e3VQ@zjWzv}q*@2)wLpGP-R zrk<4;a4MQGbWDO5%HX@+Vx8hx4juW>iaoxgsK!+lASEQ+1l%{Cr-!XI#ySAAZ;E3# zMBvO}JfUF+83ho2V?JoMaXji=%i8d+mF*6V{|vpj+6#SO9Qz7H+RLL7{^%19%I8qQ zV_3(f8sN>Gz7BD>xN+;el=WUE&!KD3?^Y-t>BK`YT_f!Hkm!C*v8mp5D3)bGQ(F-z z7Jpfyh9pXVASo^~&`Hl4WGvhD!&CMW(wyXM>~N3;LTAlbR*sypR}t45)_d4y=vQW}?MrD? z0xHv7qSi1D-3wuf5&cWBO?Bp>LoutpBJe@%a7ejtRt7A05?$y_ zYIOK$v=QunO9@_a_{3m-*}x$(5hNuGfnmobiw!G5| zc2e4b{X9eD){HhZFCoQT=~ol%GNOm7%ZI&!y|;e($z%?+rC7y4*mHD`CvyOWDpZ8y=X z3p7YyMpDxgZ?I{Ag!21%B(|b!XMIARYRS*lrFzmvda<~%n>M|#ofMI8Az8b@B7eDE zwd}^P7Iv{}8IErfE6B5^|LW1Y_}5q3pGyYGt+GM`dO3gBzr9M?qcM5q3Ygq%Eh_Np zo(DY&#E(f4D0_nb*i)a&!lftp%>wCT=E|%Sy0bGlB?01D988k<&4dLhA`Wc!a25-p ztC2wj&sXhTu{Zqrye%1u0l6r}P5TI=e!iM3puQJnE>eITpam$=n3XtyBA1Xr@%za> zA#U9H^NJbA6R zVf9jc!+ZF%E#{Z^=^4Tq?#mAPXN>z$B25zHQX@j*i&n_d3bt`@tsj*Dn%K)dRpHVh z6!eMrQg@G)YYvTyfT7Mfp2ioL5WvgLaA=rpdf_q6N#Vl3={WUR`t>#*>(&r8My-mt<$pP@ zl?oU0U7(~&`xl=bG-&uCgH2y4*f6Qzn2MfkR+#JD5YOiN|JAA2{X#}|?J97%!e?_HT-YYNNl`umSAkZ+ig zS(MeA71A}dUTcPBs2S&A9N-XTb{*%`pz>7aFOcWD z`r=#yIUFh_9x6zp7wPY;w3bf8oZtHyZ!vhAJH!*6{A+z+15_~^((c;Wcff4!%oRfF zmr(JRiu+J(lZ?JfkInI>7%9KmAvH4O%xNCyIRct}zt6^YtmWVI|5v8wA!Jc)2m}NH z6#An(rv1NyoGb+n5k!W^H@dr8nj1!X6vFRC5YdmV%lUx&fFG=LM>AwlUx!R(<^kv(yF%0$cj-3)u-m{o54^Pu2IbY9)i-^Je zcvnm+8jVtU4A0&k-&oNXz?!?uP;yrg2Q)pMg4)F<`n)XcIOwgF03sN&O7FikrrBa; zqG>^+Z2nzTl)^3+!_NrZYZJyd+Y2}6BR9=Ox9JZziAeB}OzcidEUI-TBIdu0($ocX z{1p3{$@Tw_t8)sjENa(vvSQn|ZKGq`#){FgS8O}!m>ruPqhlu>qhs6Xob3NUb*lE6 zSEFi-nl&#*ee11fCEdOEjFG<@R7U=alihV z<6SyvJ1z1(hxs?!2PtAdllqsXEq*ymd&o`AHa+IE71=h5w_a{Q3HCxE9PV)Z`WU%P z%o06W&DGRr@G=EiDGA_pG*YDlJ1PUIJh8Dr;_Qw3rh#Y<)nf_qx5;)6rH@>gGsScd z+cb9bT9}enT)%n(fKLxxY?F~Z(~(#Ak+eB!z^cym3wE@9Z}G^ZV-@_zr#bl_wRJTw zB}iYizz~94-KkhLM*5dQW;24NqDo^MEd*6->}@#2Fzs-E>>^Vj!wSgqj#xA5zO@U) zEZ46=Vap$V6~vwypXNdlTfK7-o{o^9T;>=6vq#;Xh@;PEE-!j&J3&Mz_2!m)En4r1 z3hh|vjDS`pVY%EKRLlTPNDfK_g|^~z>pQAhkxYn@aWg_ZB~wq>@9&5(jZfKniCdnw zOc-VO*DB2X(#m0Ax~hTLJm1Nyx=96vUXRJ8rD)i-`bB%tobQt)hTAqEC=47JZ}`;5N5v4wEvRlARG*r1G#lj6M$nFW^Pje)#!C%{2f`X208{e%GOn zD$>1{I8zZY8O53i^koU%iFMX9nakr4k`zC7111ZN1X6M!hwQY7KA z?AwG1*Lxzj#%=YdTbEC6l+VU$<+b@7RI*1U*@0BmPrlbu!)mBs>^ej_Ltl~=i$+N% zFgTaF8?K9I_(E#z%xl8uRVxMR_4Xo3k(ww78%o|xpp%&Q(x5%cxK znXB0b^WP2F2{oWb#o~-?Q7h9Jh*u0}KL4EbU0NFc)v76Up-N_9LUE~BhhD-Sifl8~ zpD(X-y&thg=pvDv_e#ie#_FL>sXgc&!?rSGCau669qn!Pc|i-UGT42}TH-mqQ{)=H0+6CJ*bq0@(mL`8~ z1}5yn#l0PK-bmOch*yjSs5@gVD00k5pTN+f76j6Dolg9duY^kjA22RA_oNt5lT@!_xdEc|T_zYaie9Nn6gHCo`!)zn3aTykIug!54ks1Po zKTYcqQLFX1qb@_JDAuQfF(bA5 z$YqgDarSi}t34W&-K|{MWrlQ%To+0AAaG5lqxR}2d}|a6Xc3CY++wQ{@^#9@q6^Gz zt#=QM^Oq$Q)=@I{TkOi%xV z#DUK?#`;@;XUoi0xDAc2bF(fl7SRE@v5Kst&gL=E6y)<=tNg) z_KlmBfMQ{BH`uqw8*z`oOhf-6_r)fgEOKB_Rs3*Lkhv-WlZn5rb;8h`hLF$Pr1(BQ^=dV8LI8S>9I@&#E+)sIe^q8&_ znj+Ah!+}TlH`0F2EbYY9v*~#R#OL@)Yv@of6F&{otSgZH3b+iBSV@iyj^A3Hmrmnf zi{)%D6nzs+6Z-r4L>=9D6xHoLXsneI3@wd3G|ov^$yK1yGd&1;7_eA&DJ^@$Hu_t} zK^|5qV*+Tu-33a@2?1II!tj~LToHb?f<8oUY*c6XtQ9+0P`rr_OWNyud~@p9JSDr! z9Us2$AI80(>-jkFLEJY*V_H23yiE$gL_8x#1W^W5E+*sKHP(>VX|6yh4H8%)>7(>) zVeg2=f>a};Fv(y?Jmw4YTw`iRc~-aN!gW?lL6{CM{Zi=8RV?XFm!%7 z^#kl8GHTiwb{+9_S02_f)L;LqqHR1`^Aw%72ohQd;A z%FRID1&9}G3#Y3dcy?cg9t)k$Yu^W>LSLy`;@0YR3I9Y6T+(`KTnvXCW#(3y zkXluUc#I)vgCQMQ!l-OhZHh|rmNVJ8&pcO_hXUeXx)fqK*LuZdGyWpWId^j++ZUzp4@ZBng#e^Xi`olU4eG9qkIc7>hbhbK|gTxo6& zY9fxEM#MC~u;<=q>qOJ;K9dRT`s*n>UT@R6ZqMDp3~6#vBQNh-r4G{4oL}&!1M9$; z`J_g^UdSK_B2kYVPnHg+Ku6xWj1!^ai1Pf)`@y-^rbuN}F@J(P$S?bkA&%k!Nh9Am zB@XvIdw#Qd?zY;`L(WIxmI&Qd!XL5KcWxLFSMJxy4S#A{+Bjd*@8|%_N&_Bn?_ZnY z2?Of|Buux*)hPtRPyawA!@5~=iw^4g?d?pVB6u_aVPfac>KSZRa`<(v7tF&f(*6x2 z-U+W(dX{G{Dk)Ym`do+-G(Afiv|SC3Kuiz~hYvYQ1~y)B*iD}V!h4^(OW;`>W<(w- z5viS$-b@bfaY`pg>*_@qAjKkcLrw1{aaPp}gD}`=@ehSW96hMtCQzNjk)hrNWl8nt zNsb!w5w}`c_kyCi%Z6!J3SV9sjI4(`tLA<>x^b}jF130b^IGG=8&`6gQ&xm;0ncLr z@_jY^48C?7BbCDLWZ~ldv`zO8!?WpuVB!iFAt&@uW2#Jv3c?VCs;V{A1BIVvkOrBv z-lHS>v$tf~hb-t<>wToUQVmH4m8epaQGT|_L4 zMiZ%q(XN5MvNj)koQm6lc$s{wlI>9M{7_C*6j#ISV)s=0=9EiQG958-6%;8;OPR7V zY#%QyL%;N&DJ`jM)zi#jPzi6RZV-%dG~C-#^IPN8ceod z)+9&FPoZvUuf~!v=x-|A#cpUSrGmsS5{$vu8Qm2cZr4qHKBBB&uuOX4GaANf(al)_R;M(zuaU86oP z&l9!};Oj<=VTAa!@ngJ{O*bg?nC1Jck2e`!+b-4VFHC<6KMw$NL-f$I2}?^QvFdF zw|6U)g(Rm0bho*)?>|E5;(BF(1*}NP)MjrT!pi}S9Eq}?3JL7&I4WRjDe%=aaUQK* z#k;N=7NKKdRumx2End+qThyekhPlO|jg#CQomKa^a&pmkc+ek$jx#2(1iZD$hfGuM zx>qsH%yz2Q|LNs!|bjL(tJe^UFTaQO@*UG45Mn!v6}tO zh)clBLmFCYlZmnlbSjYRvqPHUgk;pL;+lr+oMy~)!MkjK3ihM7>z*hJ|2<2w1IKNg zh0sgA@SwVQg9V*N1-s)ErGk}p2v2~D)5;?(AU74G0;KqJ=2pMTZMJ1(>DV=N=O?oi z_1o`xh)o9e8^r4H4XtCpSMy|*<|lwqrVTU4b;@jiWlDuB&)LoD+e{PPX>};)3b^>~ z-zUP*+P@u4Pt;NV+rjsfy4~T4Jtu@+$1)c@W@%Vm%?069$^gi?7xbo#WtV=qU_Rd8Lkg9rA znsQ6&qCsm?LYK(+{;ix^LF2LFh!EnPIv~5(CAD;MY%LYnMM1IEt;s^yZztun4 zE=flGP{Wa0^O}y2uiW<9Wy}H9q;4WVu}l(6FyoK*OjOngo@89pP%*6aaaZ%Laoj7jxNLK33HVLr zN>2klO$;FWweIsV@sq+ej_1IGWV1#-&1Nmc*nus0K0MCD$70!9n!UPu_m{LYl%b(R zFX*3CPvjQCk)HoUMxAljv#S?ht!QzJFT4KWl~MeOz?M|lz!XCHHBJ~;%dB$!mwpXx zZK$felRMyECgT11q6s@ zZt=fBp1ehQT9RJ2seb!FL=&G{)gh-D{0HH&-|HP3Wj>&a*i-rDbu+DHnIP_y$>hCc zHlA6{6;Z0uR;s73uE-;S@O0>1j$uAS*0}x-|G(2|8IIHd_y5lSA5jCE!T-C=rv>c) zUm#-yE8ypUlWW@<6;lIW$U11qj0UMMAfpmD0Ip4s6EF{s_CM&fJ?@-$VMs7Az*o(k zAw!h&tKN>NNP?MR#|=OLIrj_&QqnMFw?IEayFo!C+9f&U2a6Vzst;vR>n1lcV7^@a zDrVGG+t3O$jS>`odfXsluzEz*V|c(A?dD?cbLv#w$z>D=fcDCv-GgEzhh4PR7)1)0 z<0h(UJ$9qwEq(ub9Q%Q~hU7Tu_kW_y<8@0DO^^5%=9uJUNFBX^VD(M|l@QYS-iJi= zuEl7V@?VSJx@f9O29dp0Y5)57QUOf2DCC{6MNxFWjhCd8&D051(g$H!Urw_IaJ0W( ztBvhd4(>*fHNK-mzPq7$qb2;quh}sFsp#EmN0|?kl7V@5ChpCiq{WT1VJPc5l}2-zf$tf492(J78EC~ogpeHPBs z5#NW6No1s_l~CP7R6b^x8xkUgU6w+%v;Wpbn&==eZlVzdVs^Qa5aXwcY;A;e7(iS! zH8p_yEnDRC8*P*teIn{;Vk+#|${(mbhG^ikMv9cmfzru`t6rgJ$)P|$uPjn*B#^S+;jqKpl!UScg-2+9f$nuGC5qDfRq?>vW10<{~-Y@;r+sq)&JAq)5}3Dde?^g-P(sU#`t? zrTjB?!M`l_T=@la(I?bdN?DnmG8PzFD)w*h{#oD8>?_$*WkrlZi?AGCva+rYYL+1O zq8;?I71PuwxUvVN+|@a4Ro6**X5YJAWSPF-f&OkrAYsN8V-soo$g$ldkTh~td~@{k z$e_dpcVQR^Q9+uj#5>kGE#WSN^bCo49Z&j9sHQPWvCT%o+axY3t1NUQYp~iP_IJRS zbkjtn^w^LY1E^w_k#YULRg7L!Q(xQ=oNq>uSwm6W1A0-4nWz7-1z;O8RYyqORmTCH zWt$GlibuBg!*_BRZH$ZPK`(ORc@{r^I7kh5wbo$SkuU1`D3=&-Z*BGXbCk}(=Kpm2 z%g>)TG!ygxxAnAn^8>DN{~M(o)Bz>`WyL-p`Sn{vgMm??{|~nDKcNYxjb9TG1pVKW zz4`#u{{}HI{VpuJFKd%})Qn9d024^jX^$JV`%POR_nU*Le8K9YSs#>(Ob|el=lBo^ zsi#+D&y7x(sA1sV@Y&5#LLQZzGgZOAg3e`3*u%q-tXUHWvA(8h>IPL?Q_3HNI02G5 zLx0u~sK8>ucxJr5X{eBIfZuJY)_o<{s70#sewVj#$q|>B_|@# zt+ftIF#; z3^3cpDoW%R6zj|-brG>&k3EFIP(nvS+z(ISnr>;zd?<;fdDU*cgg`rZRDHPmDy6aA zGqO1oNyt&s^ZL)%RiF(dx)T-2<=Vq=?LDC_v8VtzEo+4DA+Q`X86H*RR+8k`9vk4iCC@Gd8A~@&zg2MT zt4sjta(8!>f-d9X2*rY2Vy){^NWMZVSfXNB}4Zi~GVRj9L zG5(tv15{G=cWY6n3F37HW8s#!(2^CH*y?$z|>;lXUzjC9B%H8#j))|d{BteHd`&dDgz_rqZrVM-P6pk9G@Z(a$=VuGuk>o2F{uqBB+ zW{g)0mR{YZonaWuIavpM{&XD>nK+&5OwqK*G%p~K2)BxAnm zfp;xb1QX4iaYNu$kPvpcc(J99(2|yib&SAWJDUos9`d3Fnjz?_2;b6V$;%{%ZkxMJ^y5$3P2YDaa|^Qv zlK(9qZ6I#C!N0;OD)Rr+gfMNd<^T++{{-F)G#h}!e=4)zbmTr`vB1EDRQ_|Yt-uCQ ziuIoz<)yhs@2^)Fqx_F&31-IE4s3=K2mlMZ(3Vg5!teH+X(MiYJCI5W5@u#rYliE* z07#-!65QA8;HGlwu`wjk%dVb*w(*jEyq4Fg9TFZ)nNkLw4&M&NE^J#~A5Kr1Qj6?S zEtcC>3S9uYx}|$j}C4>ly5k8 ze9xi!cjOI;jNxPIyzbP&&iO(SR)xb%t`op(n!L*y?8_IfW*_x*4WOB1DZcsL_K*EJ z1|-gPR>GAczgtFWDF1ShcFpR@K5Yh)j=9jv*l_$u(RnygDfureC?5 z#dWYhHB=ny4yeKUI}LIYhtzP$;37!%#ZRDcNA4IIvqPFWeh1smyFRA=6~FtCj3VE&L?Nj@QH9Ge@@ip{mIdk#pEMa zn_OalLhuEEEtiv>(c1Cx-MygMVf-exu4&(WDLUySA$Fi zGRjU+xa}VQP4!SQ(NIL^n}r4;OYu`@(`+49iiO0o^r@JBKhjEmW85XVrzL9vRP>Xv zLN<013~hctiG3eJcc)*jTY5gCGYP2^nno3cSeDmz7zHTy- zstYe}D57<1zVHFutLOTCWWY@3kd&+&K|b?wTQM<=jZjLk1a2Q(iRs} zEe@ed*6_VRzA3b_&s-DqTA;^7UnJwwBh=GfLdE{q#Z0I|gP)P0u;c=FmRLghdNI#{ zV};#zHb;q*kiQPNg|~&!qXPTXh+OI28PMp8)revRt!8F%__nV(%K5ASLx-uB0Ljnz zUgQJ;mn|fKA3uNLQ8C;)eF(i8?EV{tl$gu)NsZPzJq{BB-rNRsK%v`;hmx~mZl7F7 zO_}XuJ?}0oS)bQNo9+kg%?7~?C=ZC=Ys9IIx?vbUCgk^@Ndkf6{38$W^MA3UGC5da z9rtV*FJLXCK8Bu46=@PhfQZG=ioCrfRj|xgKT~CGOssK}7STV+T(Q%gQ@FhlHT!w= zq0-!FVB&s^PqY9U%$HgTS9{uhkIOEmKRHy$`f@?iMv9BVnVoN!f#`d9{PlxNFy!p| z4%ir%Tj(&oc0kgiaZ+)N=XU_U+_X#SrSXD{^T5{QVt^wZ@d%`QUUGvAGnH!D{pHG{ zrxsd^BO)K!b?q?bgC&v5AILjt8w)KwH#95#%KC}9Pz?ntj{Q?!p5IS@wAq17hsZqZ zhB+wEb~4t`l5i0;yl>H+C(E*U;FJFch4V*#&$051ul{B+KT`181ZEZ2_T70(_&A**eo>e7S8hX;XCZe7Je(+QMd&v#WmMO`6^ zHJW9sde|IsTYuV^_LE`U^6K3CbrH80KDbgw{PTNZ>I0F!%(qLof^)A6mh$mur|Hj- zgKd=b@8@^cSvf-uF7xb1^U2<~%F4053)ivY3G0D`Ps?NZ|2y0lF%TbreCh02oQxal zuhyFXiPdc{!2tCCn=C+y24KSfS8~&&0)}w@yS%Cby!`in(01McxC8tr?0+=@fqgMy zzn=d}_JR)pLPnh`2|`A0H3TBu|6~-N>B~?uG8kI`f)FwbMHzN20CMo`jQAFS2e@Yj zR4afETqlF26_5^Yo$)56uPH0tTyZ7>Pw{NZPsGLiX`Vpau41wj=&Y@1!I<XoMl>wUDc4_Unc9?|I&aK9Q63t=z%q*$;59{WiPwkucOxIiUtDO;HH}P_9Za8j$7pTItxyfo}xBq`04;;<^~yz zW$th{oAzX3ImUv~v_1^!o`6N+u_LcUoquJG0_p0l&Pa^9-(N;f4v}SG!c4pU9zg1^ zE_jzCVz&U2vMbi_!j?9!v4X^A4~ybF*eAjqTI-LJWv| z8rN|Ny-%z~0$izl7eI>W88EM2qJ9^dT+-Q|GD5n;gr4b;^-BqvM#mR9lBsdV`2_s$ zn{y~ww7~`cWjG1h4X}a#&kfMe>bH(11Ot0>_>X6I#(!==ff@-sXj90}Y5jNl_6Nh^ zI4rrTC2;+%ua>1*a*dJ)JegA_*CU8rh0eyJp-?9M+p22J7yLcJdq%}W&8fM_)prWrrO#Jg(TfU3GZNs zyTlJq$|j{9UTmX121_o?MgulSyZYVtS>c-Q!?#l0KU!HjK`tMKUC^*lwWWwT?yJp; zEzD=9+8y@e-S_1Kx|S$A@FW=?r#!(@a&sXhv`NI>5 zw1dsu$cyl<8FaOt*Bd|;;i8Uw5CcRy8-V^B`@Y{o=(zrGkcX*HN4lxUxXOZ}X(Oo; zNFieDGj@P2@)%Eayl!=G>3C~&HKp*}sWCtD*T7)H)J6(we8cu+TDSIgy~_M+_kJ7QJ0S*+HY6fNNwMnVt0yWek2%rVLJxy`Wa;al zYaA0H020={v_Oi8ESdL>?-X~e3|iE_MP-Yb|L(_5 zc)qq!POCB%I6{=)ShvV8oN#)2+b&T$711XYG?~qDQ5k1(*r(jRk?K5f%Xsp zXfumCEite91-Ce+LGqF@fjGh@45;lve9nTYzx{={lqb@r##D7A-(agBcle+*K<3>i zH=nQyyDnpn6$?a(jp(l69HsE?SK>FzIR92K8J;R#;e@0C~^);U!h;uMQK zRPs!|g?Dga4U8HQ+oH1Y3r-4%jO6UhUE>T3v%@0C7QBxr4R6+P^&8n$Q=|V z7H94Pu+IjRz->PnuRiCpTp~n$UgNfQ=(~#JGrHiskLRna3wzeLr@#Zy;-WPE1t`3b zVgn5<;a@DV!#@@wX;Q!7Q%-Rt#_$o>XajmDNCHfrNYoQpGtGPQ#cB1~-XFuECnDfa zRgT&PsPPm2tATRsjJ2;JzzWwJvzXJ7XYP{gG?iQf?R9tqly`|Bm}>g*&WwXY>xhs$ z#2KF#tR@-}!_IPfMbn8q(M>SCbP)QUYX;-oKhE7vZ!nwMBpAP{WxSx{bV|FQICyyW zBU10CGZ>8v9X}7QRru_YnRYx@JR|?OZF(dAnOr#H*r(#ldOnBJxjD+eM$je0hH^e} z&Y_Tc@C$`kBYk4%5W@;mKLq$?_bL9PNklI^4pY&&_lr)5nR5sv`vo-*{(`x}6~>8O zMKOE(^Jq_JGGnD1T~y~8{TZ~sxt@W;a6Q7^Uf4K?_}lp8Mf405tci7AJpNa_B_f~( z1+ax2%EfTPqJo<|?BdOmB}kS$>XG!uW*Z=mvyU8u$xjOkPis`ZQMjsc?k!e_MV=>^ zO_&y7J5xWeZ}C%Q)i(jS_T3ebyUr?)OcmCPF*&-KS4Fd{!gyDi^xd;{! z4GOv;WQt_Cw#uK8j}m$KMIT z+y>ndITDhOt{b*^93G~-1UaCz)4svGkvPanbmZW{iA7~eNTmF(2kt^=t_^h_V-?+% zBjzV>Xv6#Kj#Tq~dVntHXwIlQ_b?z*^6vdL-IS(mZz5aKH5w@C3(f>p4=K4S-U691059Zp0^xfj?f4nY$~BT!^2Q~RdSHRg~E`W@;S z{D`Zqx=QdE`cOHUkgOL_-Us0yy^a%KSs@F>%)lPbIQ;QXVRLHb*VXFs>P7-}`S<(Q z^B+_asi_RBc3DTHMBP@`5$4@tJ~NdyPNPrP+GV6f!GC^K3jO4Fj*2Rzr8R03>fCSU znZ7KlGV3Y*UI5Z*y#Ut;{|)=fW09LAo4aiax$Y>Yf31Z!9+SUbnH8RF5+?vu&e0EA z>S1Aw8OfD^kg81i{px~zc zWF_*)O;4>8G$|bz(AD@e0@ZX$aD*}n)?btm36M!m9|`huV$3%f^1BUmrq-e&s1ol= z!^xAtN%6+E_^RoSGCr3LHxr)RqxlUDJqp8c*sJx)SDg@QkiRnqlLK@~fx&l8vo_ey=-M zhL4>JHU=bL{C6Oi2ov{PeTzKnD0{$hG}28<4vY-PzI%r-vuU}**SM(5f{RVgH=6!* zob&>{8Qf&-Y}|s1d#AtV^47Q}pcy1K%ScI?DCgsOOhN2$%sE#AN@l?XugZ~W2=u9< z3IV^cT|PCON7w=!hltQLWk;FaN%Z zkZSX%Bx9=Z;??sc;k_NpsH-OVY{Rn|GK&&qd7o+lvo+ywLJc1)8sT7Kc+x)P-cixk zgE|oOK@$$`51h^}{3{_;6EI@G=|12QV%ym&I;vYF276++%ie%GjPS$F_faLeLqhng}fbVw&*`+tf`Sl4V}7ee;@tWhb&`-^ipm?L)V~oJrof&Y<=lpy&sn>z{$4EbIS2oNDIOg z2m%*?{B5(F8kzbrVg?s;v2m_0yN-@0KRAmudzN<@Iw7Lr`M_*cHSf`u^tb^YlcH2d zcLjHHiXREg8gKH@I+P0TcObkScYnADVFFHowNy0J1EMf%5_ksgG8L@mG5v)Y831%O zi1wYqPBx0v?Nz)DVPh!3sTpF{>{MHz+yjBzlIzZ|ld|cm=0#C|d4=fvQFi%wsk-4t z9-dntiO0hCat+hFrQP%_W5>xAzUr{CXt0wU{UX0@McpOLTIC4CQbCm*WL71PgSpE5hxSko{f&+Y;K_Tp`#j1x>_Jp`Cl3ISl!bPpZerjRammj?0I^~ z^hsuWHGBRT9wCps%=S-cpeH$PH+6t(NeG4w4a0aU%IxTv)U*Km+g@J^_=FBT_2DSuF5#o zGkRl>GPsyQG%3!$mwa3w;*ApWDL9^#6H`|)N)>L40tylmL)K9T?STYa-%+q=_S3${ zIKAtbQIYKwOp!zBaq^y_i=)E*ySS(&gw*z}&i@&*Rs*X* z9j07y1j;fsD5|Gkm<5q6)^l1?7+Rv>`CdqJ)uV-3ju|2C%HL0l&Nyq^6whkAV&%ie z;jZ>~<`P;1cDJT}=VgMM@xC9$z4jRC#B&&oA=Qbz8}ty} zXsg2g-&}U0Prunq|D`k;AwB|Xs6r3Jh#$i;l}8D@@;L#yW z(F)jAOm|Lm)VbU%f1+(n75a=7OD81$wRvi1CXW8qBIdFRi8mhQvcz6~E<`)}%8yFw zhuL6YjS?~?N(h3y&LJdwS30serv+J);{IxP)YotLwtuC|>0Z4nH(Z;Q0RGdkMOq_S zUp?BP1N!}Jfb@=f=`oRP9c7+s&zrkwkM+1qvdMJpuTVx$IBM&KfKK+W=Ol8><8512 zlcLq3IH%&vS^UL*5&7|hptKb^1*ugJu6wQDY85m;$Ob{t(i>&Z|K`j6nWa$RYW#o4 z84$5YT}RoN_L3e&$BUL4U78f*O5J&+(nPB5lrQ}G=csIRCDOC1rY&F(7_$RD?=$7c zRQ;pf67;9|ck}d#+FG0!JDB6`5(8I?wFIX>z7M-88Q%@g?WMpQH<%b{PD?XL%}7X8YxS&j|>RbSFZc8a%Opcn{h@VHahV@sK=kf?H)(Cmt;N%+rR`2 z=-4rIVpF0e5lA5~umAzS(ju+qg61Xl6Z~4B3*yMQD^+y0+pP6WumvDTT(d)93aJo} zEC{|VLB9$?=Otl8C3()rB_^Y|DNfoUt#909zK9@z!{Rau<{obGFr{FhflO+}jZ(5} zG5E6awEc9R%FmebMZKVWW1()J_x+nC^EkIK-D>n;7-`${tXk;ZJUSvMZno!ztajWzF(|m3+Z{yx^o0 zO}v?IATH~X1fL=Qu6-xgcxSWmo4L6bthK&At7GYRYA>CDKmQJiFQCf+Lo~!6jGr$P zV+Zp)DVgeADtTLo7s2+a!Dd+odlP>*gbCE*RN|kytlSHF#cppoo9%XpM+L#u8oSsy z;j8qpGN_d?5}m8Fo&7vwZZGx7YW#^Y6rm)pJB|8s5u0gRgA>B%smEw_AXTloQE>}{ zh>4?^lO|#r&di$pYh@grB6zqJ(p-v*_0&gy%#RZIZKGbst?ea}zj>C6caBuTnh$hg z+kJ14HQVho>uL_iCb$_~8gAP~Dj<`WY?C1KaM%8Jpix_!ILFjyle)$kB2(nDq1Y7T zT%RxYp#d?T&cT&e;jy3QPMWQr;003Sc^#+=J3Y0{RGJp^>w#sKqEzH6`=sc0=d}zP zWB{6O3OUr{^)7e?OH!kx7XQiv(*pte5-TBjGlPFuF_fQ-8(QLCjDuq&e5j=L*U)lN zkNVgX-K!5uxZBi;d}jKTUO|A_z!Myc_HSDAKt;B=e;0*{fa5wv#cErj+A%|aq>cL% zc^~YWaf}~4r0+o`fyW#d>p7R!TvK<}ItV<)uBt1|kMA35o~|Gh7mXNmVF3~2?+fO{ zL<|w3bZqVvw%VhdcnJ9Iy`X~5r{(^>E_z$ljVq0!qwc+J_lQvuMEF88=sHV_RGFq!1_oYst}DN-||)c;>1h zt7qv##Ay@b0F2uW5rh_QoDt)5h@d&DHL;bpjPU0BrVu)juO^~w@J7_M1hI=r@5km0 zA)qDNYpwa?8%`lTE{J(++JOb@7L6Pb3GHudH zYzpNL4;O{@SCB-ZaJGu%F0vCz-`?7`=V{CYB`$B~z>L=q2>$*%4&gO{spphnQ ztp~Q=*5m#Z~NOx~{JcIg(f|XGFoBhc-joYwykQz-4GDoL)?ZcH(9c*nXo#eZ_PY62u z%p^!h50V3*w!0~`mCW*C^axkDaQbCv`0LW|x`=o}WqOqgk?r2&Ek-F7g(tTBI+>f@ z-`LdDP|6dLDAT^9+YBT5JC5E%A`Fvp#UL1ZcR%~m2~U#Wi~w_* zg;Wct%)J>RBIu)Fg43#+*5#HiI)Y_1Cp|-ZEe=iRo@3qwYTP4+cGA!}@kDMupAOb_ zW>@n1ev);J(a5o7Cy0&xRvw=hvVUv>n1U0=w>q7 z5o}5I6=r7wfT&JSmGz#^T2k9(XcC;l5{jojRAo`xzVz>gr!Dt*Qle!%68B*ocRe&_ z?Ff6zDc18oX82pk2>n{9+Zu}wSCwf0z_}0;hyFdQoPa{GL>OR~qZvofha-lglzKNF zLUjHRHdz^y{f&1$=)jA@FQ7l{IbRKZ3y=Yv7zs#u$9lTR(>5NGM#Kb6Q;FZwvAR?nWSru#~zWo{5ntNg11$? zsoi@^5DJ3q34kR~TnVs?!5sm19DmGk^!UBU!u2rx?5*RPzTdcQaHr5io!T>^Egva# zH}M%;HHO{S+4e82bBB<~%VX|}EF1pw6cq|_)elk7vvBw`>gND9j}muDG~W&NX99tm z9S6tb+*W5o(3Y_k9`F^>VNcc1K;nVngYW^-SQ6AcFXbI-J%qmEeE=TJ2G5dOd`BU@uwo2oFJnZu#udmm^4KwVk4>%ej?WctKv#yrbse0&%aDDx@|8U8tK_$KY^VQ1>$GpML zG}eo6UToIelCMIW1|m7i_}hz8Bux4hECv8+xS^aLQI6PB4Ats7>!RG&4AD_3~>QnqwCZOWw3UmJy&hG~LdRo^3`?xOq3eVh%8 z_^7L^kzg5B$bB^wcO}F6lHn~I#owcI2$^qn6NKBFoyj}c=MOMq8#2f~Qz;eq`<)B9 zKG97OlT1aaJLH*g#;U@lOO6R_QB6VkrFbXX0d3?c?BQ73Y~VXmuDA(G+rQda{w~Y* zYdRhBtT1Tns6C3f&&)i1Vn}cB|80mH=L+NRvf5s?a3YH5c;UFCPMwrr!hUsE0+KH= zpl686*6JFxEs{9?3e#yz+@N84fJ@|5UL#8oh_;b8r-yh-}@FztJ0ggy*a9Tg`15rIC~r>;~W1$-TkX&n}(` zWZ^fJevz)T8d2(xa(u_nvV+vN7Vs?{N>1S6w@OCZ7(+hZ{}>nqMW9Jsa%6xj7N?pR zko!FD_I`<}k_?=R3F(B|DpQcB&fl6*>CFZP5_ip~pK7P1OT{cb&of_E8lVvbj?fQI z|PLt*SB$G=9lg6`@Fcl+!9DC zepQvwLb*N^oyn4mBMhc1nS%jN@H&-l|KK1^*-ZdjI~kzQkzJnm-6v2~9r|@L#;KZ# zkwMv)2^^!U&*0C`y56K(MtOJ@PkM_ttg0I6u=ZsdZOnMoD~}3}4A1U})sVcC45Y2S zB=4TMtzdLTx4L&l>7lxSQRB&`3q@wX;R)W3*#~u&4Lc$)b{1~z)t3KW`==?5GCa2D^zm}~bK zo*$jX4r_t4o)l>KM|1X3tb4f%n1n(d!j)V+X`GcG=y za49oe=SKR?g>chcMdsemYD#?WnR)u+$2tILfoPt6@%Wx_*EIa3R65q;ErzyC89kL_ zKT_SPW0@NWUnA1skUmx1<(W6UoRZ!sxr&iq!Z31uj|o6;wFd{Myfi_##yEX*Nl(JS zuC!pBd<>nffBwo=#F7jtio=D_tawv%!}PD+3k*YV(UcwobDYsGnKrQ0d=;I_S3S}g zDyhe$ktYyb5OF6&_R~)qyJ^wSJN~%rU=FWvKWrgx)pDl5@{By# zjx1j8Fu8cF+E9PQFqrAl98J6O!U-{gR(nf7)(EB>v-*f2efvD){N4@BYLUGp5}( zgW#q0U4THODXRVtoK9=!CtnZA|Jgveor(gpk|2s74uYlHZh_dc9^ER6A1qKrws}T^ z1U*sa68&@M8NVX0b>Y|uVtsgj>+ZU%>qa5LtR&VXLqp+Gmspb(JJ+^j3!xXoyD7nX z(SO;!m7~+t-T-qiX)q`e#R zVEiP$w?=oW*(jp|y#rUAsB%(RFHO9xXLAqIrx=Ojr1*W?&!dl4^Wyd5O?_=+B}Sc` zrBa(S%&w5Eo+-N+8h%qYH@W-f&p|2{Vnj*LHX+dt`%NdKIwG4;8og%wB#CIZ@S)>8 zLd-EWlDVu7_>&_Q>MfPE*vuWL-!s{&YZs!&axop$^VJi6{l-`Wr2hRq<2-unwAl~O zRbmH{wSbi9dK=_-j2Og!TPQIoV&7VrazbOV$KtI+p)YQg5|z`8Ks7+^a#FNhRVPHG z7L4=d^%*m zefmrr`c*f9f{mCEQkT=bYF6lcS+tsmT{OcwEeE#6%hDbs_Ui_8kd*UT1??;nofl(ox@qQ%uWMHBrmC0_4Ljf-Fq&K8IM$wutPwlNmV(nDA#PSSr1DuM=|{ol6%1 z#o^#DNr=-yX8)NUrT!73!c@dUa;cERsvkJ(Q9}!h$qKgtu6&%?|Z}T@jE!c(?)& zK+YMfnT_c1cqCQE%seOEfef($7V{~e>5{f`H8t9wYDc#t#Z2TRFu1QZm}Oiccr^*N z%?Q3Q?~67v0>`ISDOtpAwIspPmCGmhQ`h~XD2%o?Xx?x^QP^qw$0DEmVG`UBxS_#xc#Q<5t8Ec)4&`z!|t<0KK{KEJDup! zlK@>9bBI5Tu;=WUX`}CcDXWUls60L@(X+&B(fk!}{4P|SC)c^k`e^EwiktieVCj+9 zendNKHY=+EM9Pgw5QCqo2M33M!8>+WHDelPKwVEz@r6|8L!}7A6o#`w@mTsvdV{vWvtzoyzhtK6^UE>;tcD zjEZ&x1+?Yjwu64#OnJC4? zn;RnP-vyC94Z{)b#3KJKTw)JlZ0@b1?TbywKgsTX{;*v6Cn~BbBJKy~4=}B+I(Qsf zBvC$s{-N$0DM8OLyZ{G&mTW=lRrwlIS2IC)rxcNwiIEv{>N0&l`C*;OAo+rUM_g?@ zd!aO<&-+02)D7pj92A@bV-=#XYsRRA@bN_%Fom=dp%`R{9kWM4wV8C8vPEmmnEoT> zYF`H3ur(j=NlUSwE*+Yx2lNOvW*sZ0t*O}?a{$0#46@R@`AR+dz&U<>pH!z=@F(wJnTnkPdA6r4w0wesY$J$asJBS=o;OxAyZ8=ucHA z$x}@`Yd>hu+Kje($BpastvsgO29pJgH9BWd6>XHCZcThQWsN;E#7Fi#L*;2FWKJk| z(TXG*?^{SCTvfRk-yc_fi;zcw-xAcD8YjJfcw>JDs4XzvXnN%hDEnK%y&pc|^oV`C z^J^oDrhNZ@WTv*%3y?yP|60EIu0cBfclmk?;s^TQv)&INwEugy;t3?;f0yj9Aktv} zDUEwuXg=&ejxg4rgYLhjaYmgGHLdp(MDV|N0R95e0{PG0ubdNs5h5TUVVnP90Q{$n z{_KTH`>jC;m68f70>1L#`uvcx1zH3kDOh--{yxO;D$A;^jQZQHSKZW|U8%0$pQWzR z>|Gg9(qEYAp2_jPgBbn`M_DiWvfz_({+kb+Oqcb|Y{xIJZQ(4VmwRV<;nXYEcnaprtrP&*0+DQf zCFlRs)&l%i8FKvLmOwlVz4-b|9~6~t~L#SIaRv`@6&J|gjJrGq^F@_);k{c!I_h~ zCh;zHoL>!pOFr*_k?Cpro^uZe!xoA|f=>w@MW>i?w2FVLe+3Cn+2-6_(uE>>gO`y3AUx zN@ddMKOmmiR9pV6w*b{Nl}>f>8ynScpe$Q4m0@m_3WK#}RreTEAKG~*d0ceMWwfc2 zx^$7pUS-=yWZIcYRTQ=9t@ zVU}7l(Lv>&X_^IDag2w}dn_w!e+C!(T`{yb690}IkA!rgnEkY2RI1D~3OjNuPRT#L zfdxr6tSdGYwu9Z*#|&ksWP7qJyv!sH{?aa+ukhHrHDs0#)h(xefP%sS7;rk3$IKt4 z3`u+yXZMrMJu?*$5b$K?RAP)SNuwybGw{awR;I=*06yAVLMDyDn6>2W*^TnuKz|l* zR_cIJ#X9H9wHwG?D~}D)Yjv17S+%@)fjkZT9#gy>`uGzDq0Tj?CI}=Xl}(Cgpa~DnrZ@)wNBK08cDM#~cYMn?=yD797QKFHiY$>Uf|l z5(sC!irFw5^KON9BfbnWS^0R9=BT4j%S1Lyk8>`bI4pBgHLS}$H##)A5QANWVy_ha zfl0r$Q7-rhyJ0-t`=2~q5d=?{5)V!y6ma}D-1N|za~gF{^_nbzaoMh71q-;!u$#`m zO}dG(j%lMvB`!5%p|{oE*jshBA`DgZls5=pa$lstM6qGWUGA@K_o^YrA`g*C8bH#0 z=kc(jYreJ5Y7(@nhd_nLRwoU5ChUt_h+kaic?6-VOTk0;T9CXRGV$(yv-v{E>5j7C z*Gmz|#plNnDoQKB9@t&ET;+oN-n@3XHH=(>v4Ve^;3~VC%%R3!&XWMecwACBy*xt_ z^|UlyiW6JYE11@`LfGd;Z{gH7QgJ`h4je35{RD^dl*Nrc4rP{fD9#c^!5Mk7?bK`y z(-6|h_`tD?!%2e~nS7+o5PUPyI0Qn6H;O$j{>>f;5M~P)@TjSUpQEzD^djH}f}h2V zH!g7RZ_nb7Nvh?9$9h@aXT=7@4}mYjEQ_ecuY8tP^5{}nYaR9D43CziV*^^tVnN2( zL{kH$!HB}kuJ&Ldf@ly~yUOkRa}}1DSHlrTV@(b3hvtB*XqoKoVrs{1cUwG5|IAo5 zd#Iv+rAh-3ltq6HfLL`8n>;?@zjJN)z988V@BKzW;hI5?1^awj@|hpawGZU*u1lOM zUH>bl=Wt1-HnzP1O9)~8$3|!h;GTp2OVJ^^XUIGGG3;U6ci53+gP>QHFf&3)B#$jC zILUOogh_p|QqF*n=UbL;)|^8{D6og3AX93eM5_moYialiQHC*>mBMRgn#!IT1C-7m zB6mkQmTFid)T8j?lrwZK8pTFi+*}u)n=~Xr12X$4lN-CPoSrh59 zx+es8;Xy{hy5K801x4R?YgT4xZHcNLTrsoenPh8pmU}aYp6!*1K#Xw*TXG^kAITLg zYY7GLkC$^_vVX|onxhGS2?`Dupo#(BjbRTrC?{+;OlY}U~@(u)~05*Sm$3)MQfNg20}XJH;#G@+6(zgz(ETWI4Q0|&`#D(*;mIokbP&h3cIJs?>{6IbcZQ?Q{ zIBPdgEsgM(!{68*mWe9ThcjmLI8*JtgK%6pb4FZf^S2!H$dMgTZb!kwflXI+h*0tr zJ4NInUQ}ckN~_G;RL0=|#7njHpMV`q?$h1X++dxgu}7bWS$dcV5rRe*y!iwLK~-c$ zm>4LufRNYA*VhTTf-o7DNOT~;zhi7FIxlSIi12YSdSVg4*XKz^846fridWK=R2K2- zqJwq1O#bz5eZV%YAs}IDGEpqI@H%A)2tbQ`kh^C+j1p2&$+6pWh^F$Yp;B%2pi0fU zW0ljMPo3!?Gr^#$b)9)$YQ`}UpCRvT@NmysR|a_)p4bAHL@g`^tgr)MWerOM6WVRR zHK(wbSfS)*%6NcZv&{iEC?J2OA;QvXo?4eg)ThnT~9r% zQ>Z{E36^$K<9fklO-PRoHcSH)HR3JFL-6^!l4Sm=~J3 zwv+*Z?U;x1CPWW^$r9`htql?T84r{UlMHHkk&qSeO*L8quxmQAHZQ9;?>wf{JcKh6`k(zmZ~|24MD^Gp?L0V4!_z_ZYd(vsUy)|3gzxT|5o zN<3`8WE>1|Aw*_4{`0EvAD<#u0FKDbG3}9M;R}7Fwmr)vls-L z6x2wdFOy$K8(kk(veRZ2glG(yf!=b7LPeM7q2gFkjK9-=rl+VZl;ZAMTeX=^>5$5o z;2$Z}tRMoWnCXHgjasoisC+W~@{ZCQ28&s(foj(N(kGLbX{|r+hyK$1Xt}&gCqRLI z?cD2?HR&1%ELhmRBELD>_u+-Qxbp8Z)#L$|>ekoZS9lJ%Q6J>X}Go3Mx#UZW& zp=6?2Os7Zcg9Tfb&BFR&+Pz7I-XYOjn2oRbe}DiGDbH;ly|7Gfw-rbTprlfmDKSAQ z*cr?49kITw482$({7b)@z&l8lFyV%7x5b7wr<7FlC6S(z-%3{|7V?(V@t*?}-it!c zZU~rkRmFHgDu-qWuv9rxBy80Xi?jIJ#O>XyAS-&lJ1K+Smqf)2e9jyL1rXo-2;kmA ztc?M9lf=C}vGi&Gbd)9f72rH3nR>wanc0jap|tcWX`Z%Umn87i zUd*_RP|4PN4TW_$?QD81O}w}+j+S!Hpq!f@o_X(s=Iww;h2Q&hQB7Fi zqV7Ws$uLT>MwAp8NT%Ct4wA-i6X%peRCfR^J|f$*<{N=>(8c!Gt&?ivFG_JUoVQ5l zdUVl&6AHnHvDWn!$S)R5mIyXj+(Em_zi(DR2H7!hfrwl4VqkKw&C`2>I@>uamBft(f`Xb{5e9VEc8su;>d}YFYc9E{MA7 zT=v$ql{_`@weT(oTdsohRYf+{H~EGlfYch9)&{vV@J&4(j`{43JOgB1cZ*oW21*uz z8Af!TG=sDqyPy@ch1tAxFh3H&1o8_8Yrs}p^wdLM-X~FNwTo0)6ebZ@=Fi^IQhSQtgX!o(oXo>>2f*duyb3XhyMkG zY*LX|A17t)lRwAApm4Z?DaV8J%0|y=PN9H-+jfDw1O6sV>Ps>WsNn@1LG|8#nr2PHq(HY>yO9}hxWpJKsH^wo%1iiiOGuuRuZ7Qo~%S8gQ5Ao1% zfw4sLw8<%b;J%9Xfk44N$CW;@1Eq_d?llAzai4gxm;ULRB)5WqiVJ*YbszxSz9e(s5JOESGMQHMu< z8tOc`+2%+1xS8;TDJp`8@7Swn1sp4XLjQ2VV8VRsG+u%BMd#?088sMSZN#R$MRzLg zjiLJf@c~CG?(zX57H54T{>H9iW|6(ghur(M!+gaDj+FZNy=$+Jaw`+>GAtA@g1Cd#gH%#uaZ-)h9(i$=3VtDQV_A5WcnS4W53))L%}gWi=HY30JL{6SXpmLjpvaH=-qzQKo( zC+p>tP&fjVGwnL+Q_A6NYcM)gE;_Ir1FV|@@deyYYrF3HfGAD23Z;2 z7{4f#$4a{U`$7>p7DF!s@X;!>O)sa5#G+vngZ&m2Pt`Ep zSoL=C_43WLmALP`%{`<~+wP}*n+|V10zMjnJse*gxR*siGZgQJ-pg}@u2WWxX#9R7ui_dK-sCDN7Nz2k5fp$N;QSs7O= z!ObBII9U?kouQ%7g@4CryS~r=j5+Q3sj#O>`XNz_NVAhlvb_=eh)8<$L*NCC2(aya zPpTb01?0CDHHnf-joLkOp(e{s=-Z~Y>2RDx_}7i{A^gTMp=CYubd+GaJlhAeW)KG$ zLWPcm3E*4{YUEw>$^xWj$8+MHM+uAC=@n@ILB_@VN4r{z3)ZuYof~|J``VN6 zw&fG&N?Hv0sNSCFFcA<7lWfhb0S*GBXgA6l!|X|9cDVco);#JjGCEcB{b$f-NIRXu zZjx&n|MyH9P28A9&R$){*EBwL?H2A?vob~7eL~O?8n3TsjPkxMM4Rn~D}#H*HHdLM zOxj|IlP_i9#OSD;0-Z!#aH>5^A3GNjTuPC!&zvUGAjh9fhQfHl1-AR z5j{=qfLKo{a!XA;IUMs_n-kCt7n-Jg|>G4wKU)LnLobKmae z(vONb`J|!Lv|=)hkQ;m?pw}6OMh}M|Zj_>H-@y?mPw!Ng9#q)rk~nw(7oT+dzg)&P zdB8rjVHY`d)lxWmb~~x^dz8ZX&G|4jP8I*1yXq?&t(S8T3O38|Oi<6bKS5yLf-#sq z?U9U#Xa{2&(Az6p)mW6;I=?#22TNpkBdFos7YK0hwp30)$*l;Kr~(wzPDL9TxIi3t zeB_ByaZ(yfKu1u@yS^JB?LhlRjKJSC;RvZx_a0c9#<>yc_0(KP1B@-SKG0((LWDP6 z3OLB>A!LxWg81TyXn1_>5y=>+8gn51K>HKM0_CbB$In5~YFqg<$B8AtKX`|@B#a`B z7c8(N^sns|Gfu9XIFnHsQ`aAajea-6LK;&n!DVh@;HyLyR$pwu%;DmP@UMxm*x*k1 zSAf4QF2y=lJuivkAPd5=xRM~nUDnAWHYLjyVpm;qLOyG{Qo2@rjh!bamno~x?l4w6 z5F=HPt_pfAB*&#km59&lM6QxoDOko_J;ze?Ieb+Q(N;D9-y|c6xYmx4dT37e(^GiT z()PW-+~8~rZYTp#Jw&4E?sxaWi-|IT>Dt@9!Lf}G=T@jV`kkTBe@H-~tTI%LR*%Wu zve5zb90oIM;5c#;>c|1{8~6IJk@q%ZmNND+c+nE z@7$HhgR4UwWCrv1B=e(;yqZHZAI_r>B@LJA>+O`5?BoK_HGwp0I{kEc#Nhvg%~`O% z_|kKK%l4kG2|LYqvxS^^mSLaN@&eOb_ELNp{~JM;@sUb4+VXj z6IGEpSIv%!d?K(0Uve3VO55J*@(o7|?MX=Ie8p482l~OvD$w~yQ#3vcahl9mKiuf`~*`vGKNd{Y3 zHq>UwY}}rd*Y~VEzp52<3SH* z2(xs#JddnKwBZpcrS!`N_cYR%>nkPgr?mg>Nm|8gdrBQj1o<{+ow!A)<8#5g+)6su z{yPC!;=tL2!c_fhLStJ((JiakW|2PZe6xV+vn-uoU8_!m?8mq21C9O|$K%Bu`hANg zV6`#xN^i!-9BL`#8xD@F(|WihW970}a1EJ;ZO2%sr2fVbx6!v)+cJ{pTwvHoSDD zyS?DIR2tKId_4L(MQ(Cw@QZzu43DgPl!Lvj$BVR&9QIHi_V24jJFzszz35Vl@ggKQTU448Gj+ku(2Fcd%JXzgR z{)@s$f%j-3hLxk8K+%`V!dJ{43|plEBO2ZH?{T)PgX@;s)KyT;n#ydO{{(E_%i&Oz zE`cNaDXY>wtc z?jwT=ZlD16s8ngWG2AXx$XvkV)rx*Vm#!!fD+r^SdhPZZz9>9m`~H~Tz==U{2-0B~ z4@}yv6zC}l1{Ay3%6!Htk-Ck7a)xXG>JNj;5TPu+F)ia@N6JHzKXr>}IwcFB$BNyS z5=`=?k+4BJsku#&G!jVCyv<+0Y_Ke`BPk;>jXo4Wy=vydE#jCGZRe2L*wHLsl&ukN ziPgAfgYU#@x9JHNKA_m8r!?}X!2{BT#Nbnw&?q2OpfpzT@q%Lo-;a=lgbua-z+@jX zAvuz@RQMp^Zn4Wx$vPCS{qqx0KQd1Jr|0T!kr$56r2B;JZq?#DYoK#}Xc7LxaC}dL zC6f69SA#gQBZ@LtG``EE2L?o+(R6Bh8=5TXFeDnPfop2#WLRDJxRATkwe&F@Z@lmO zsK*B(%T!tIAGgtnCK5!^`hvJmH3Tit8Jje{3WJGZncf`?gX&0!ayNT%BZIF*Ro zld~?BxAQw6f5d~)Bi__IQ?^XtYvxDIiVgmf(-~v4>yGB3!*wvM7Cdg&a?|0HH1e2jFVmBBb zu)0EQ>Eo^84Msv*DaUIrJ;;Zz4NQlDt$@K;>44*9rY-N7ZN0+-Ll>0*_DlW8+da`o zgY)NIe;ZZD*PUyHZ?RtcMifiSYE~n!gH(dGn2!h5-ZPJ|6d8Vi-$k)2lZA(H49Cvi%Iv!*4KGTC{;$#~W zmbZXk6*%}aO`BWqN&gVDLuzews$YfNUYF;Ov%N&H6##Sa@LOYV3CQ6V{+KS{nb&!) z;ymgL5p)7@=2FX6i1RzEvSaczl?u1V&iyg2uJ?XVoUJJ{!dmjfnH#K1-4rAKRB`h9 zP~r@6bK;EUb2J}#DHJg53mapIF7FrAEj<}qg1D{7>287lC92-nR-K@$@n|E1R(j7u zpr(;IzCj;vuMC$uYRH?G%JW9v)fBuXXxatfF4_VZ#P*i_YVsf9UmO-RS{1dcbPd8m z(oOo;TO-k-Ar=)R*i5nd3cp{5U;`L?dHU|8NvFs}ivr{AaRq|Z=lci}bw*oq;J-QP z@m_2iHCbNyg6!~@v!7w|Fmexz18h8_+zC8MVX>J6;&Ae^YL}xAkGOReNgM|93Q0~y;tt~=yO;l zMJL*c&D6@Q6^q~De;3Tycypv&GSs-t%d}$LHrubnmLHkk`;Teo0`8KpJmGi9Y}~Ct z1=|mytY=wM8~xsZ^J_iqyrzcD@w%tKCn!~>SM@iEb%2a#7g$UhYiN`?>+MIiN1J?e zIDlG^ts^XPp`nO$b-eY8RTSL2%Yl|NhqR=9bnZ&`*lng_R_c8-N~*fYvq1U5q^ge_ zm6f<^&=-#laj9)>0keyZ1MHgFEhwocp@+}SC-2>BN90xaFnLwv+Ht?=R_a?<<-*=$ zDTR^bD1^LQn$8V-F^#)Iud-x`XWB9qIa*emj+Lxx_qH?(sa|lfIZf*Rxv`oUoS^vo z|Jv(mPH*tQW7uiyKfr#sf##%yLDi)@J=imO>bh`T^yGw4%cq~}UZMUqQ9U(vXp{^J0ZM#nJ z@@t$6(&%Mg&ezA&2Rs>sy5E7)9GyF&A@_mZSCAQvECkd1a_7$2mT3;@n=-3=7oWol z-k3w7WnftM3eX`LL`l&mkAStGNu<(UWWbQrtbcRQtMG!C*D-svo>A;1r3tRs=$m|Q ztv_bV>NS;BEC9LE%D}@`)x1U?E49158q(F$Mf~&NI@+*Eqx3UNuLkKUV)=r^x{EwO zazSIILvSKb$H4(Tc_7jrCi0qbXXHZ!7ieRt#~A?(0E<0L(vz;UNo1K3Q7hCsN?0q> zp4qN#2fzG(8?j}`vGf{2V{LZJyu{1rOd_=jSUA!er{&Qu_lD6{J$Sb0{(&Chr&Ds7 zyD;y8z1Sr*`rw0csIB{UYSI(VBv_2>RAo(KgSi_XY9bK;;1=xoE;OcCkw{(KWG<=(vo#%Hg6OSX1 zBc81tKSI`G-v&>^*qvhA66v(obIXnwned)SLN`k-4lk0lfWeAHIJnv1+giW=HjtNwn9EbHjOcn{p`E|?dOt}s+ShO#|FC`8mPDd@=oUz5gFY=qJ zE5}@dwG|qTj9uuZ+9x1dvQ>H@!h%%`18{tSE{o6!N(b(r5{0Nf<1-oj7Omv>SsP-j zfcW9Y)*CJ(9&Qeawnp5nxjE=!%`3!oCQQAo+wr^FbY#=qVX}cjx1BFyT*2~j zQXIrDqPdwoDKE?~s3O_g`)0KE_ZVo~7{Mjhu=4AX`R@2gvDHLkbeyI{fHlkt0Y`Cb z3Pv}O1&&L))p5rN=?!pz4C@5T0a%)W4W+U~Rm`R2=VzR0#U zo(36m?+_1_TN-~v_d}JfjdX;-hY$oL{FFq8 z`SLb-&Yj?@o-%}RZyWarueR9PDB|`C+{i;H(Bfsj2wlq90-UmPaEt*%KpP}KguRGX zvOTpk>p#Pm-e|XGiU<3rl??dYH#sc6FDr%h@B=YM{p5YatG!iBICSngHZ~QBC}J5a zYhWj@$oZ4a5=0=Z17NL$?L*0UQjL8{2?0QWWPBi}CwP!+6;o07i1WM*#JYhm6ib$; zXRYDv?}CB=;q-EP@|Xx_08uJYnhyj0**O~|EcbYyuRG{^ZLI*#yF>R;I(xMCT(HwJ zmh5bxHC~fXfRHr;wEr$-f7SYdrJ`eYCa4 z!PP}xGWuKS07g>^bCFyvcQEiiwV-#P-ROKfjJ*@G7+KeH;`b1C}&IWtvWAT7FWm{pi^$w4gF*ZFLPBStZsx_&k1EB zb2ix=p8mw#j_KG4H+6P6YMG2vF!N(%g=|&`;d1h}vo+tQn#XELS&(pJdNG86S%ahfF4kC_@|JM}(iO&pf>gyZ zChwT`KL%FG;?#HD8;4Z_P7YqjswL|c?<%ttUnbwx+$M`8$2gqG${_WPBRyARM%W_BClNvlQ68)pnFK`Erm%)pk90N3r-&SZRLY~QgY9O# zfgclBcs_0@0#5q1p_N~XT&Q7ejs9Q@1WP)ot9bU}5o;pM-RIL*$ubpW8`zLB@(7z>~f1SRYcFfqi7~|&RFSS=p zk{h%C2InqpD6>^^aOK|pp#P)NGDugw!IcSVIqbg?f%_xZJ%e?lT2F>eSVVD6fB;6% zMQ_slhHEsJURpKMz6OQ3KkTnSESiTVc{F*!jbu0|=_63<7$MtV_S!;9SR&AJ^1^MZ zOe()2rP815@7G3LXmDJFMQR(5Vq5ioXIC;gLEP@{i)PsYQ#?MneT$FKd)f;z8j9#M zdEtFZ=@3jKgPVOIndDJepl9qOO0MRGH8!qSddzJr7f#ki?Ks0iJ(CeCGHMZDo}qv8 z0doLf2r<0fqu)+UF{@qBZ<*B@$Sic6#*gtaaFN<8@j1)}``t3VJN-VvfW2q9gQph8 z$ck8d`DL61$FdIlGg)DqS{nuD(uNeCTd+>y!WmN5=JCQ>%ntsBh>1*%`W^vk;Y z?|KCp5e%M-sg;OD%GaQc3C;Sm`1Pzg{fU0U@4d6pAR#Fx(_7__+y0NKpU0ex6>=x+ z#8cDtgV!~bIz^L&UdBHh|NEuOCz*z!A=pphIJ)*w_YKZ5#6ydn-Y<8BVw@mTcml>k zGXFdW5BNoG%ZU};$z}mA$EC86If)XoV(*MOcGv`SX}7r+c_A}IQ?x;f7mHLQ16*cf zw1dyWYncXwW9Ij04m4G!aXWO)aI3z~9~erbD3wG=>7)`I3tkR8;5jb+p`wQRia@xY zj*r85qc6Gh_hsca;CSk;W~HE2QM*Kk)bJDgk(~X?&kg=QDPXTD*xd z8TfX+iBbbO=uB@F(sxdfXr9?dh9BB2=W%PoVinOdH(O@0(aboH!z7Al;d#4>B;RNf zN6BhCiZ)2uq|k&(5P``+kf5d0wK~ZMb54rWHDY&cVZ4Go>bR+^(B4Eg_*`VJ-fSLw zyG5l#?Ul$rBE(zRROw6(931B*q8hY67Z9ILGj`C*7V?!1fd0?8p-1=@*g}$c*^#00 zQ?YSQuz|GAA}QzErO@f66VO*PT|0HvOqma5in&RDxMU-pfm*OVor)52(3mhDpAPov zfraWh?A%wy<+^Tp6e}F8Io9A_AQyb*IZqt>YZJ}!3f@-G-LoNAaWvv%vzkLZz0gaA zX_+1qn7w5?V2GzZ(qSJnP-gc|SKk%SLGvu{kZFL_S|Z^>XIWT==lALuI+v`XAKX$% zD35DJKMtvcJgBWbmZEehBF>8h;+du?-(QCy$RGk$JJW_a%}(5l2ZL~$Fvv6_?u;$2ui@gI zq#Xz3`GF-_TyIcq5{l;?6ro{7nEe5|S+DhhPNaH3GaifZEDA<+N}^DHlu+428}W3- z%@+fKcUa&UNy9v3p=PIcuFd+MoyCwLr+KQkLjJDA4(al*RNldNN#@#xU2vMQH(Ss$ zj;^Es03!(;Ggxmj67`=gP!L*f189d0L#jl^?eOP+E8e%}wH&sk7AMzE3jv+|K#^-U z?SuLtt!DFoKABJL3hFxf=e``>7SVS}!IvQ&e2`orgLBLDP1`$o{qA$|o( zK=Olam<{S`PCO`{pE@qQh=Mz5E>qIU1Kgiegj1kJF{Akg6+tt2y$>&aue^n5BkY&& zo^AkN!F>SV8u-ha&AV2`@{MDO$)mTrfV|sv$z)<>caWb!lI!YUc}-qcZEl!edb{at zF-$p-oO2?gbI$<9Hbz-L91Ic18WHR?zy*)NyfzEXV}6QCxIEdvE-+U(@gAbV%GO#i z0uq)o?BOtkr$UtX&|1=-I0mC9=Ochi(?N`Tu!j2QG*#_P^jF$CykfILoI5E*6zg9Q z9_wMeU+X2)3UgkA`LS9JO*0D_saM7PToq&bQoek6PamU;PPdYrO;6Wo3Kcq2!iPiqnCj+U~0FJjKa}>@z!BStzKrgFXbh7?FF(StHtLp1qXci2JxG5 zJ)+2@v-^phq4`|qrUo((n^FCmf*_15+z{5<3|3fG24Rhu3%FBJ9n{tgnpTqzgL8xG5A|D*oQOf4e?A#nk^5#H6-hX= zKp?n}ZBnO0&G_=#LF0F_9`?mEEUH~N`5eR+pg^{$*H@irCq~?zBjqPH2UtubHl2Iv zt5l6^{RWT-eb=cxK;t}$#5Sa<^UysI`3`ZyeMAn#=rKyN4()vmP=aV=XFCQT!P^ABI5;+SgzIHr$nq@3`S)2)oFa6yH-M$RAa{wn_>B?Is z@)WauogLq!2Uvjn8UMjJ1}x9dyGXn;XWaZjo`-T-hFu;DcFlC6kthQ$19dag%8U4G zwWLkV8_UONzkV74Xp74X~F#5Q)0a7v;sO|Wio_hcMd zJk$9+dq2I=hy(bqhHNa%dfe%zt?@Ir$Jb@7fP;Uk%z9sD0KiF7Q`=dRm7l*5vxdxV zg~hEG0d2;Q`z7lk)#%~Zwc5UEK;hFKN4nO0WO{DyM5|>vqXxe5hwwTIH{zA_Q@Dl9 z;N!2-*@v#9Yb|se?G|4h@*eSSjfeCo9pry%Ehoyz`-cYquO4dO6`82}BY4h<|6e^6 zsx6xn)DG=Ge6KNgd%9KcO>uY-rdoe9DN+|JIp5X@SfSk%=+v75!4|d)5*H+T0#9u zifj%DCssl*@NKT;Gu^h#oVE4JpR4M1pm*!)+`P=c^=-tFJN8lko97&JQBCvwz0R$z z<{7YWyU`Y2d^K8o37IgS2B3dV!g|ruj7xie6Y24W7JMD?yv+A{{OCGvmmz3-?^%s; z+o(>Yd^Fc3pOu_X!}Mv8(O&}n_-YtxJMvHI#sK7rH3f1y;CQ-H{Hp4zZby+{e$v*o z-%jHizEpa;5f?0P{SpyWR6hmw+=>pilmO^mq;dC-^?u^YDLbvT-nd!x<(>OI z@&kpd0KzciWz)yDa}zrQBSXBV80syFGil4N7mq8iHz1t4Wc)1wmmg)!O8Y|+NC56r ze?EsW z5p9sXn<-&FV3Dhf=*aQ(T#{lL+bJu-{qaYs8QFYVt?W9G>tnd1VPel%br%0cj5zoG=OTKrd!@z66a@y&U>d<-DF?DO+%p_rQ{(hFxTTg=ipYfUI%w)5}qqMroyd~MotD*)ktO}$0C1h3pH$p zlStB6Q@nk`xJ*k?zTWJ7g#LTvw$NlH6P}SRVt*>)k&hAoC0mzAAaL*q05GyN?v6gk zc@Auk#xS&$5~*ySavH#4B~{tzO;-78op$6%{Xp`~`kBe-R~{bAiyFUS{_CbxU(Km@ ztg01ndvO)^4p`%=ACWQta%s={WT!sA%g?Jg=;N@l7K}mgOC2#(3_`xH7Sb*)V8a>Y zW%b|Sdabz((lnLcE!X4EXFCLM@-Hnu!cl=v!}bLxy?#E2;r#hEMR>a4{mw9V$kpw; zG2rdAE-F+rrv7v&YM61w+xN)*h$=9Hp^bgNEj3DWYimcizfdp2M#afm^oFAcYS%TN zP~kvI0W7qq-Cl&jPA&7K;Xhz#DwRlqi4@xu;D7$Yvl!rZ`{E6Jya3*fOIe|e&1$-a z{p@1~dDXXfm`E=oXj99;s{g~)IfQ2xb=x}07u&Wgwr$(CZGW*{u~o5Av0ZV+wko!h zlk@+L`<&a`joH}G>a4lu9AmtCdn+RoE}`rmatxt54Mj3WqahZ`HUpaJhZsE@zdwRB zpwJG)dE;Q{E)B>{v+ai4+QF^Bq|6y>|3E@I@Fb_mr>49W%5&JWrp+S9mv51S%v(rq zkbw@VXnK6eA%&jm+5H3q)@vk)_s2jBhO9VhzG5v1EdSkP#8lbJV!;y{6uP&=pg6t4 zTEWqj#w}v=w_VhXz2$2M5ymb(#feqUcxp@1PIPAUh5Hp?g;$lBLQ&&Frw~p-l~}?v z=l^noq+x8wO{arIKW)su+yrDc^n80P+Q6ch6;SYuK)Jc*08iipGO7T??M8Ac@HDoN z2WcUh3#GUmD9ZLOj=e7TU|c=&m9sF z&5m_K(3J~7wLO_pdnlmRwOtey3bBLG4@f9g=wB#{zgq0V!Fkhiz4w=G@i_VPYHl4N zZzbK#`Zb<-INC;4GZ3w#Ak2QM^ zfO*)8Po{~@p1Z89SGmG>Q+7ya;6~Qvp6|5nj)z^ZdwQ8*q%zFr@ z!?iJ~kvJp++od=u4mf>fk{XjIJPdD0n`%UX^-kLPTem#t-G@`E4hMd~7!WX`-^3so zSdz9y{&;FkXG2<%rX~?K6BcRI?hb8sRC>+z05Eo3if%hp@jy(jbtVWn@U&tlc5qL- zPo{GESz~oF6}Bd4YNc_SLe5FKg*^{Yj1sBFpt(&>f;g7ogys4o;~c>UwAnsP_X=a+ z8Zqhb`7PnZ^#YbmCF2wwivz!JnOzg53a%$;M#&QjD=$$DcCyGmac?>Igzrf>c+|~d; z3dodxC5uRfO@L&xknrwJ%rS^1XG1%%_u>Ll%PBAyl}l>fo&L&+7A|Yd14k!MOf||v z)l_@j86=q^$jj`ioQcWo_$gD;N`*hvZfpz&p>YAQ=&Xs4YeXXx(ZUv?0vIxRMOC2wEfV zv<{C-iA~nEh=Th4hM;qE9rb&F0kVG$I$lhhhCCveC7dCIIGPkm)jFQ~LI5t_v!`6peBgT2KO|ZR z2GN(F`cWB4WgVbbAq22PHfhxvE9;P4qw4K-wC_ZbIZl!IQ(zH|pbfw1Uh-PGz(EOg z8qProjFDqPsON$+N2Jw;ek|%v^|raUgveZfLKhGmpxP$@Uz^F66Oz9w zqeF8pdNwLvz*gPGYH%^lqO1U*1QCQRl-9pZYRQ4%EOAp|&Phn)pq=(jeiU$tylB}} zhlBRT)S5o-D@QrcYAGK_jXkI;oAuCWzns$HSA$_>OQpyD4RJi@61?N8O3#P1?K^8B)>ew_Jowpx$i&XQU@8e*T~w#DCTZs!Nf80m3@75RTR~z4=1)gQvjELyolXr*MwKBk zq9&qoAYPmp-KsTyUzG*hnZ{5c!c)Ln)38#~D6J>#Q!o_)r+PlsJoR8w+*e5jImj%tV7$B)AW)%||sf{!qjN4c_tJ5bwYwH{VBZgN$U^gIr(C_@o zlPeVNZ!kEW=sRIM1YaK-P_qq$2_;1v;DaaN_r>UeI9mifv65o>?>e}~ckjxN+#U)2 z?{nTSHGWX~Gv^L|3ufBNVcvFs_zo^0oFw(lGY`CPBiS+}{IIbeBGJtZ7|orKwdInt z*{BG!2lI8C`jh_XO97~iHCMS|?cEy05HZ?LshjTjr~(hFtDweZJ>YK6g_LuCz$=3e z%NHyG)#9sdegLX<@s`GI5R7LzwLLsu40<$jo_p!KaXaPzGyp7BwOoO4;nf;m=L%sr~jEu>R`2FiJ zdPs${e`ER_tPq>7EV&w#RqhxSWL;wM?T)Zy@JZfxv)a>Mmx6@@K^!weQ>G1*_BG0a zXhqFGtfbdK)}S+un2GH!0X7c+SL!LYJDMo)t(-`EQB4t+M&B{Q3<_FJ4r=Ef#~TR% z;!5DqP{_4G?IkGv#&$I zd(`xQXpew!X!&Z@8Gj%~ap^@?nur|tQO`hk6%EImvlCB9O0L1zQz0`9LlN&2J|;Y5 z#X7%<^2SP2)jyG&DEY?2hFfHTZYJ&lGwLOL;HlgT$4ZYrngy&nTg@+;t9PRXD!)m+ zUdafH3#MuKm5i%*p4tJQFIZweN+aj;Z~nehT!joWy>*S$?5ZtjOpTbo#e|;SS2baj zUD~%>0Bb;cspUgwsQ^E5mbScNf;i~k$+SEAxQuL!OQE^N`X|VwS}*r+iI>z!8(&H|~h z)nz>3!18&}Z?fHlNeQj(o$P}=aiT0P_`jr7|9;W0D|dWy$P2_lOowTZc@RmL)i?Hg zGnYplvXml^xZpB33CFUixP@g?W+gtsON}6-rp!)~VFg3t>H}Fa{LS z1JOwVAY#Km)`U+N>memz5Mp-!32OZd)3 zky2+S;-+(y?pA1LG&G-oH}#V=6I=A^{>>_;KYJ#qh%09fANuGVZVE5+(P~xMm$u*6 z(Mt>!?Ww~yk?@VRP6Wt3J7-09J|eEHWeQBg?4BqZ39xJJrtB?K_2m^>iv9AXv%+mD zP`W0lJ62kvE`@|VbQ2dlKF&MVRB5EUQurjp5*{l)ha3NUkF7XQFlKCXQ6t=}clxQyuNMfB~>1fj*UE_-?dU?2%{u~%f zluCo7O^seHYcqzLhHg=gdjCj*E#ad5t}3fXvLQmh!M{{&DSk`A@3l5>TRv_#SXYn=0*hYMG5d6OieiVxLRLcN1QoatzaM5S_^2tR3?514^6S;ML=(+C!*^a4sRCsK6OLAhZID^=D_ukLkzMxR!FDF?=LhQio4e zdgc>(Q2KvW)na83X$NcUUu6cg4)%zN%;jnWCq$c|`&?k?cXH7c9G^NJ%DuXpt-0VE z5!rACV8pj(O8OYm#6pDMRy;^nhmo(u43~(>AdXo>*MGj+&F3?hB!&53Hmc!L+hJvc zgZiizN#aixYH{&{B7~klm5(Hshup!w!zCaImvdf5zTReuzy1W7Q$1RW34LD4e;Yyv zZraukRF=8+T_R}t6V|iYgZ`+~yCvqZB&#n4V*SQki#Jj#__YN3`PA8NQ; z48lP$qY$&qp=?WRj^2L?pr9Hr=&^AOa6p$&;tH7V#tS&j5$`WOXR&k^v?KM2}o z!1mN-HHjJ{ZdZ}3N7YrBKmAqfV^U18Zrc2Cg@QnMA5j*bS~c$PY7MLQ?sn zaT^}S?`jv>9}K0Tb$q~0?vS$<;{{D;Hg}MZt~%zJF)9fOHEAbRDN-0^jfo>yNOgwS zXhPoE8nOI=B^lm(TLqB^eP^Oi5sv>n_hG%52spfEqK(z(tPvS-&#U}hN;6Zb;4tg- zo4!NH-2i4FBz~OL2h)a|PB^v>xI&DFRmV?CQu8B0L2Ar?Qb{8KHma2CWn>pm1H5X+ z`C1jKr0SgRZf^P07`cDME`BF!!h;`r-u|bfs(_><-pQDn-WBd7p38!?0aQ=4RbSEz zx;0ro7Nx433I41X!dD%Ix)B?-cFFOtE;f-D8TeF23$t!7N6*0JB8S_1;2mS*3@MpT zUe+5ief;Q^jhHckIktIGX2WdR$6AX9jxzhiIPu zB&GA(`LuyWRA%@|*MPEW5$SW1U5Zv9xbY}mr?W>Mx27p|whteRkHWPMtZ6FfL>y-8 zX6#N41tSUj&rG4)l5OA%@Whsi4|+aH%dW(20c4eA4tza6nAUm} z?=irHm&hkZ8gG=~w}1VL0~=FTuNr0f&2%Uz8jO-gzY@C3 zlBEnnoDrmSMY_`$t!^HoJ!-~Z5{!p$&uQ%i?{c^l{h}x0s6mm1PcUgnwQo7jGis49 zY(zBmLph$94sEhRV+85L~QKI^J93a#WOogR}mQp9!!1VdFBP8>ePEwg0 zD=%-t7d)lSCAx>9`8#jrE2CzLEr7AfGFYZOv;QOSXviV#!*>e0wgF9yvQ&}B&ycsk z9)Wf3SZbszZ*I&a5|L8CBeyoj+Z~I()PUwMlNxvZgz`&1oBp3Z1G#SIk4#=Y%&>C{ zHk6)lpi;Ng_83V~!`L>jBGQ9f@*JNGX){>;rIasd!ux9I-W`AE+bMmAt+^-|3?iQf9!6SS~EHllOyzU5o#Pjn( z{^4dlcFmWa%tR(-HYm*e%SfI%MxeYyk{Nnn3#6vtG#oS`Y(Ln#Isq$SbLD{$mvz3i zLj6JW43)a`Lhk9Lf93R(cz{8N`bh_N;N<&FRsD}&w`J{AEwI1rFf6tPK?Do zTYwkm%z{J9iE?Cy>gU+WzVgEo&MsUoaCcFoOi;^iCe>)bbPXVIDjCGTy?0uI>KnyJ z1{A0mjR3fvb{x5;Zo$n_bS}Y&AO3rNj1T|2XT(?(GCCHnkR8H@>Stc&Z!aAEwDWxY zw<`KQA{)zXB#w`Kxbz-k}3udsl9bV zPV(>7*;588KA60FNG^VY?=?w-^$>C{BrsaE;-j7u%$1~G(U0w)5UAuIDQGhu0_y4n zMZ)BU{*(S_?Qc6ex<*O#V$Mt$6hm5Pqr;vB-4@N1KGqWS@p%))FB8yU`O(OxWFC=ny#lm$`{KlL;(SU~@$53u75 z0>Gtu0A-yBJs8nbh%Z(qFIYlrLXKFes^7H+pC$_kNMb3=fvM;I<7#psTu4?sfc|D5rQ-2S+w~63$zJZ0eIDPGQYsb@h6Aw2rw-g@uZ(UuQ$A zXc?F2o1l#AUm8~FfCG24LoK_}3#_h^VRJi_tF7zJ;I;T;L%lDW;IGgrQy}6HSevM6 zKuk|~H#7OdWZ$|KL)#!ZAJ*x$p!^Nc#&&px@D_Xvp#uLy$%pbcGW<~;VtSL(;kNLz zHfH=McFoD$)IxD&N zYHO7&;0ZCEu>+NUrw+W#H;F8_Ed!F)c;F};(hps8ySsR&)4=Ww3B66I*aRX4)peMS5X0_*fFjgba5Q%J84JMjV4ay6i)%qq6 zLqzaP5^5;ixjrvCt0%zF6X=$SOYN6|B|sB3n&Y2d8!JygqNpz{!x88ZCjMHTTV1Yb zxy*gcMsi+gE;HZsV?{k$c*5JM3F$a6Sv&eHzdR4+Fgi^Br_Uw?Og{>Uh6|jDp|sbU zPLs@Yo86x;GycAVkVpF}V3;sJxEr9;TlHPX6JCWXuLhO!C9?w)2WXrM@3WZzNCt}W zk_e5Ltr2iXexGB~OR|rYrPD`kFGU%lgBp1HAv4TT_}AX4AtG`nUJx*#U)8K_1vSe* zC8sx+Hfx+KHf*s~J^520E;)<)^bhWrv}sAVlyJdl}F;IbvRASm(h;K(hg6mB;gXqi8U$4?e+~9L*z^9B^lzCAJlT&=CI(mLb(s33 zAn>c3p8<@Fz@|)|jRfNg$&1Q`h}ER+;x0m=5~mf{o&|O|rB;^6HnT>d4um}u&cbqH#`LMr&dXV<{k_%G?mNheZSR6zKA<+A_v`tvry&0W zi|MH!p4Cwmqs5)C)iT>ATylSRmGiYk@s3RnFR7b8aB}`M-<{F2?DDjH3Znr_V5_P0 z`Tor1D*y#IoetU$Ot#o~NY$0e7yFyE&8FK2hVw&5bqR6)<~(7m)Rnz-waCSBx)@UH zJ=83YC<8AKGRE#&B_oW#QBl--s3u`Kbhah{BG)a8D&ODh{$BcaigUcUPRa7U`eQKN zaC$Kv*qo&Q#7=m*#)wT>zblo^#cRWIYDB@_aPva4yziJ#4DkVU4ZpYQCeT%Fr(xn1 zMhwRCkwbK$gFHB4r1nwwbY$0=jL`+TuUmzCUWi*@O}{3&+*^jz!g^?da@NH8rIf=U zPJ%R8TGy%U%aeqzn)c0RPwHEcb*on6+6TNHao%Iz*(0jy}rr2hCu7u5n zJ6KDO5K0U2&qo1hYPN{GB+y^qO~^t$4dlQ!iB*KSk2$uFF znCXk@cSGH^UmE=@U+C%KZp5;9dqX0eJ2ak2BFDd_7dHs4~9U zq3lr=+898EHEce1hjbzk>E#rZS+)ghJ&GRUUb2NO&3_mG+b@%6X&3Ohe@OrGFljio zlb(LmtHPH@TG8VuUPV*PxoA=Wl_|_$ti<5?B|jl z81Fv`Fr3%J%~QFv)OFP}KqQ2puKPn~ku=-{^Qg?z!+!z(uc&DQ#AfFe90bJZ8@zy@ z?o0x}NQcuRf=&N)03ZQKk`_)^Eat5>FbO&TbP zC{p(8u+MMefiimBUVeIyBft=0yVv-tIT$xIk2*SlGo8i>P)H)LGEKD$QEV@0BF4L0 zp2!xOM)&%#qZ@V~FIOk9|NI)3e(nUA89nkCJ-!V}c1jm>a`9Bt)57k#1+zImHJaL!Y-!+NdsM{A@2D=gfnxAYsqg`S= za4iV{8P%Ut5m#*w8j_l3(88U|doW5qwJ3RcRV7cL@ljXSc=hkjv`sZw%2;Q*gDneo zu1%KqZ0CVxE2zst3?j{P^O}k$xwvOluMv5ya>d8i-+da~Q*wmgTtElv>0(dOv5uoR zUeo^{77jBcY%+ld0%B&AUdRIeUkO<{ts4LbsGTL{)~K7>ahH0z4++3wt$l~E-Q$CgRaT`S?TfH!&x7RC zSw{uu;iKI=J8!>Yl}qYnM5Bt}FpuH(I6R)2f78`cG9J?C@9Nf$x#I_ClAf_D8bfuU z3yDGMnK8*HrS0mW=sf*UHfYaWRox##1OIhIbJgQT5kJP=W2Cp=$(HbK^y_T2wkIH0 zsxJZb74~%zuMG@m0eywnH)plB9a^K6+Q$aHHTaF{+LrcIoQH2=FWpz>=zR)^-bqO9 zmtWi7jE11M`t56EJ6c2~y7{%)`q@apj%`K99`A<>#cf5-^mTiU)Q9fN0-zxej^vdel(a8#xs#6K;VkP(^l$-j_1_dTUdFvL%QbSm$Zlp36J(M@9JRs z=BzX~f^}ji2koC}CW#x_p70xB;PE%X4ouUdk7jGe+6u{>^ZZU`2?^Jrways$(Yi`# zv1tDIv8pY8hoR|l*+eH=-DDTw;h`}WH}>kT`5VB0VQC2_Fa{cU`71C?ANt#qt@60) zVQ3JI0X@H}NB1@F^1#miTv|c#)!j}WDV^*$H}02c()N=$Qr#WB7cBX(G$l-EwrKUb zvoX0_OLbqQI)32;g}l*GNW}=aLKWu3$RRJO|NAhcdicT~G}g;ETP2~@WyQ1a__-(e z7@AOcUaNZ~>T}{LnG#CP<`q4i2p8&B)l0I{jvJI+rnI5t=#@bp%TZ*pT`7B29@=~p3r&;#yC|KBcZ zixoEkpDDP&tLbUFkaBO}0NA^_@|WDymhxGN$16_b%c0brfF}Iu*GwpQAH~bHBnlfW z3WOvoEp|Eb_4~ycV~_uvjW=~fr-dM0ka6wu)Kn%Q(HAi;XmUXBUr)s|YJp6BE|^af zUsgICs2G{i3ffkkxB=GluYQT=ddVO3&Eaz1R;I5#A#=uYQID=b=7Xnl$=hkGG&(?r zSu1N{q9*Z{9XcYhVRKtHq}q>@hpux_si8K8a-w9BX8G;(@1_|N0emj88x;F~igWwx z`bE>QJ3zKceSZ@{bluQEuleOwKoSn(1Vs6)`SdrIKkae>W@=$i;5dgqsC^nNj5b|+ z{yAfp_~Ik-GaeRj8r#<=^T}uqYF$D;g`3Sq!qFadOmu%b{j16Af{_X7``e>GW4~1Hq>&nS~{Qd$9!_e2ke)Y^xsGiNcN9D zj#X$+nOq^5)=dy1rs^oh&M-TaxzVz!s!=M(_vQ(t5?U-E-&V-EPYPEZ@ATb1Ei)dB zA8CNYqlu=&l~7uV&~PMa@EL3%TN%hE;h%RE^8L@_z^V}gy7a&m9R^!ya*`M8%=){^d4wL6ChKLbQmk)@sm4Y>9>t>@1z{@hEdrWqo?MNak!y> zW8rIvMfU5f%S$@ml{&m~sO&eDi|d@{rciv=pR&XP7gg8ZrGA+Yf+>}!O-R;BbOiK* z=(C;>=LwlR0Z9S{2TiYrL=)CibBUs@t7z*=gIXLYr|pq%)1Ky^KS;`xn@F+>Yn6%s zi}P?M9uPFcs8puv`C(+WgLgWz%U=NY&u0;Y<@cM28VeAF-XLTKdZyeH2o=C+Bz7PI zF{UUTIu-qA>Gu@6HX8~b7064HRGR>gb=deY@u5 zA5H;4PNu85o%?!G-OcIb^zjWt>1LOeJzU7>*5z$uiMtnse~kQ`AN@EPwQLBy*7Ht; z|0FFLdfd3W$8&TVlrE#;wi*qg;FCaUa3m#qgD_e;^U$zp90q3XiPgm%h6zFwTEHp5 z-yx$@MyXSzKS0^3A2$!n)t44~zI9f(rGvRZv$WuRBbT5GUl<3C?lb}2s-B(ie18oA z*cDl&_ooU=hh^gIRR&20uZ8bOLsEhKmOI%w7Q=uh1x6Q zXWFT#grM*&L!gT($=T(psKLb?v7mOTx`2#&AeP#Y*S6Pcq^E?=vAF^>VGZg#()azx zoK;cQWN_pvIqZq>Hg2Jk&HyMni7hZrZ>JTE;11#;#%E0xO7GoSuD^VxZz1xSH|is!SENZ-@jqNdsJfh&Jge-RK5&!3^IMoC^qlh zKE}DO=lyks(D|CGLN2f{o%)Aja^SFE6MBKR8L85+CVA-A2ivToNubJ2A}D6qA@IFPVaplEY4ar^7qP%r(=$AzjL?3>;PlqS_ zJ*Ye^@ZYhB2I@W5kY6(-lTdB0OuQ72$8%70!KG7!Gk-*>zD9u$tz?e+dnvz4vV-&I zZB8dkeNa~51XK(H_S4~S22M!{81Gj6Y2N{?_tCr&i^rx%KrVun(le{7oSmgdL=W^2 z47ThVy5_3nI*^>d{_3D4!R%N%D4Lmj-Wph9z?OFZ3O`s$&R*oYz;J>#V--;f85E66 ztsY)cE=6uGM8Z4)!net@S!)hl*ACq@P*o&t2?ow^ed%cCdDM`7v1}2dN=mHgpB&IK zj1Bv{&VBwn>p2uI@6rKArrC|YIo)n=) zfTb6TN$t&|y9o3tDtpAuOqWh}hrd8HYy#BC3foqTie z;Iz%bElnC3hZK_+5Umj&?PN6=E1tXhVu=~L5HpLIVv~tsl~0yo$`nXKJ@A~`mVB8x zKQCh-giA>At$o9W8TUMh5v`X(yvJ6!hdufMP zt=;s2XoPx$*0^Nk4A0O~!IHrkO@j^(r)c^adT_8cS^tTXhpXT_1FL~8t&gM}9`3M~ zZ5)vCo7D!@P4IPyj6cLSslCJ&>#*r&_z(yCBgT0e436SB6z$ks2Cs?F0t~zr-biig**p@($uA)qqokdr*hz*PAlEVTO-zk2n9;4StpJ*Cu@Za&g90ny;b z&32R?({qk_|95LbR{sj!V9u?yg}dy>c7&e#rEXo|!2D1C8i-LJ>BC~WDD%^kKywo` zuIKZUYTdW{N{JqPt|%Qq()15zLqs%7P?Z7L$>w81o?@@^b7!YElm!F|AxfiYiS#}9 zFL9CJWqk@5`T7uN+!ztA)}uba`C6H3OHGvdfZ!xS!2RY1oR!u$y*?0t3#=G650AvD z6k<(y=7Akjj#3aa=1!=#gjE1zU}cKnp1AL-4p0r$Ra_y}Tf z<|e9mT^Xo_qy;_C>9bI92b39JFBkXg6Do5!P8vfYsFj8pBaBW9F#ZK=Qm4-?=N;B{ z*C5J#%wdeMdt8$@}t&9;^RwPHtafwiJCUf-AlN+Xy&lN|&@)Z^| z8Bm;Qwyd^DW4N84ag?xw@@I_B)D<1tmThinI*~xyQAJNb#l%if7T9A^(5FH4aJ7K= zEa~5f%&03_ja+U}AGY~D)+MpgU#t()yJ&4XsIjZ^ixUMd@D{&@hz$;Zi?iUvQPEE- zVO|!Ath(zy!izl`Z^QWtG43NK{wM^#CkD>e=fyOemi-8}J}3h|D?oIlSKwcIvIiqfIZ9ju&=4=JXa`P#b44+`$6Nov>L= zFMAOVjLNyIKou#CsvH=8r?v6eT^_W_ORV#I`k{304g`e0Q$+JC3BSv7MzMf=Qxeje zGZ7Hg`MtfrncSFK61gtw4QCXEcwFi=xLK3a3XJHla}00yK9h8$t`R30!ZFJ0>9U9w zkV+9~sicVNv)c2}vpPz4Lp;r(PtcJ6nAVMRJuf0yfe2?7&|Ija5c!ou{Qp8|0=W)y zm>M28M1=QTNX5C&Rr^_?7^tjDNr#o|_gg=nw;@4oS zk?dHW?{>a2Z;%^Rc2ERX&T^HRgomuOl5X!T?YGx7nI1cO3FZ{D{TTJ!?W28En;14< z>E`860BRrj4l^$2t2{trkc&G>=15Y(-4jMmbd+zhznO6J?NrD#Rq)wF=ep~nfemXK za%Si2F>YF^k=jZyn>Vfz#@&&>F1B2TO8hXXBIT05ry_+EH<>ZU0BrNDln`KK-hIrP zxiOTiJMZ!m%fPyl5Sq2x%#wE|>=VA|B5tfi1JkscFPUC0=rvO+jaN=Hf=qwp%FSHV z#K5|NPK#l1KcVGz42(L{QR+X2J9vufn{(Ydg>{ZF;U+LHtKGP zyNo=hnR>E(O(pSkD}$e-&ch{WyL*KzLn?T3{3)sMA=VNt;3FO2_%M_!wf_ATY7u>i^2Q346E z{CW*dVaW0&nDyXegsA12!Q1VUQqB2yfv3T3Uym{v8ujTF8DSPU^u_ z$DwgFi+!-V=8%-OVt#RqGI91Hha3liC98g-oH4En zOA^;wKk_A>?D1SPFT?N+Nu9pC1+fSf$~k@%;z_ zCcogf!tRVZ%8Yia%SX~Ld3tvPAx1#X31uSnQI7E=3x6gS^mN(fSk4lhC3dtYm&YBK zOs6D`)~37xYMzMHF0OLt6NiZ`768p!P-OB-#L$2biU&ZSlsE0-~q9 zF>;9&XcU{S=Er!mDyoG>4pS)n*0`wHP(;05vBs8Yy19Qw@t`mXZ*1m0UOJ9?ayr?b z%WOo3LIX`96zSiEL!P?4@k+2Uu6}|N{ufXoW24-@T!`Y z@@k?#wH3@iq#N(nrJZenStt}nw=}&81&0<{rNq??{-d+bO`wP#bYaxn&Ed=5A{hvK-DlBGER~7%8wZ!D{${E z;;M}CaGLAsTCG`K*}ncQRJ>~!H0M@FRi$RIt=&)_&uuY-jA)Sq@wDp2cEUi^#Qs?r zPHIqSs$ZHJJjEM~jgy7ItfQ*!Qr;W8+d$#R3blx*ZXBHzCqiKHw`p%1Z>yMaik7fA zx)Srf9_Px@vE*nUfe8Kap6^HE`REC7kGGE3fz;-#EAG8)o|4P-4=g)2m#LjHqHik& zU;!f6bwzL1*Tq4A@`ou6F32+pKb8Db@F*wZp$t9gz0~^LR}O3hNo0do?)U|@GR;c(U0E8ytn>_4 zGk7^da9!ELZ>qJOP|cKbTlx+8MFULOh4NNz8}6(H>d(u86FGuj&C7!mSN^C?ajtu5 zyu>d6WHb~%GZkTq`l>6ig1f|rUcb9u!`_|)k!XCs<^ZKbD{8nls!lMaC*d*+1i1Ak z0Ud@0zRqOXmU`KjysQ24cv zqm`w_ox9J#LVF`4&3mC|t2P5&KJZj!FdiJvJ!c8z#Wd!;b&!ciilY}wo7t|0kbe-o zb4(U7kFiFQ?XFCWeX&m}CNK)X;r2!9PMllwix1593!Z6%xw4U8&=r_;(J^X#4`B}A zWsYuO8`G`}yB0d&BM(=E%f3x{ez#zjv`R?f}KQ>-Rg>&cZa$Mg|F$ z*pj;VgNNBLu6~92gO@uiaC|+P>$1xq7>;8KM+31tUexx>?_f3p9t!yvr+EsaH8?Ku zHsnMlEJ%Kdqf>7+R)BOBW!-5+4I9CvN6;HObCYS>eg+dpDUy}= zb+elQ?$fGbz($wo-OCycU)Yc+c>sF6f|vxz;=SeoKc^Q--YqpWT6_u&^c+K@Oqo-~ zA!7TRxjWwJNE+^}GY?S?P9itfzuo<*;P<#n#pA$KOIHqX57pvL+9Ap=*n3R#`vZcR z3ssUVpvT>}|gSV=77+77XJ7|J&m(2@o zbhH|@@LIxk#LP%oY(_JJL>9GCv?G&jNrd|c;E~eTHWh=>%6%_6HtEQSP4d4q6q}B- zPhLiLR7!cnM*WnY6oM1%5apkOP}kAX=U6()9{MM~zZqiseBx*ttu1iP8ge<$_hNs` zbCfEmwHvyFg+g!_QJx`m#`71SC)EH5W5Opse??+7tb@cEJ%}sbfNr>h0Fgc))$}|+ zUM-sekYCJ=<{#1#*!q{o-BUEP(S7oqwqFt7=|t#o1Tp01GuXlO?{63F$8a+~1cus< zTwJW+BuY{E;WqZOoV(vYo9@U*(2lsvzi#ocNaHw`PLJ1(;PW!V)#nUkM+D&L?R?A9%5n*tczda6uFA<5y0Mu)XS z1(A_xI&Wmxe~hy=G?z%+`^2pPttY2Hyc}7qFn_pnV5q_is-&D_#L6#{R?#qTE&(eX z&tH{bST2K;q(^?gx~$vsM0M~WHkJX(ABS=c)4UrK;~0@*o;iefm*H;g=>x##><~ZN za!?L2-kIGILyd3ku@FHvy3S~Md{ZlJ!|O5YPnEc_=YBk30!f^APxCl|JD(pr!F!dA zmdkiGLG63q)I2>A&60~=k$ClzZ^+}K4rH<0C&w>9^Xe55lSO)1uy22G zLi{`YL%`5V_zSLTCOoweowUFz*k9X$wZlk zq8BO&#Gq=Yh?QW;u^EaKl3UU(^ew|tBZy}f$5iMho8OD;4o(LyL7oAuJdJ|@JK}ua znyyg+C_hdgW}cJk97dyjY+<+LEKL?Je?~CXlk5h*Wrb$a!((HGLQ1Wkj5kAdcvg|) z@^uG3me43U_&9&o`fBD^$qFO@+bu!W>&{~Y%dS2u3VQ}w#PKY`Dh85#LXYmZe^G8F zw8$Vsb#VUL<23^Ed=?RgavG0kjSTwIRqVr1{k7Mj%JZ8eQSJ%|rAh7RD&e2fA*>IH zA>FV26&#??DA?V=%VUG=RUaPdlXf04b-SHh-!tL1U{-KN6ce?T*U!Ez<&!oJ0RDqt z)iO%i+zpZKVZXUv6c48>$6SB0Y7|&qJ${G6EZ{pf^2{+0K|;fa=kq-2D`%Q#j>w@= zzO_Y@Pi>vZEVJQQaAv0ODovJJ=9ts&gOFxghBpnHj|ffQ>{Mzx9Sn$DH9D5596?vw zU3z5#{V*)tizF`wl)H%>eu4hq4Qx7AB0%WBAYa`?05|A=KGQRj0FwVf`a;f7v9RER zfCw3YfYAQ$f)*`3pc(+v3ZD#6!2NHnK{Y`3KeZC`x7chDARz4DDHZ>(7Cn8p8h{Tp z=?4QpO)ObQ8cf7Fr7&Uni~a{wqgvF zPr@b3s%SqAq)2?8;|PXZz@&>(^^F8jQ?I$%*s!pxh#sHScR(p)!O9Efsd?qo7)y0e zAoNEQ;tff8^&tfti>=nvPR)(0%zgCauCtM1bC3c73+8qCmyQ|x8zx(drd>5Tr4NguTuWn}>GL4XUuSlm zOo|UOA%r3_hG^~Itq0e?ureHTMpiASJH$w3l_($onV>){slQKgKli+1W&jOGBg?JI zQw{)wcx+Vv*y=|8RAVO_~5}lWt9O+O{=q z+qP}n=F{f1ZQHhO+qUg@-m?)Kd$zvR52%QYs=O<&#IisNCx5-eds}EC3Cf^Wgir>Q z{}wGvK^N<{Y&xOn6~pBueP6imt!9s3gtUilo!9+szld@^3LtF-00<{9AsLMhp<2>7 zijhgd%Qm0ETwG=6uKHD0{F@xx4mt(eP6aW*-aqiNJQ>nfJOx@Imu*(FhQ7>OJ#=al z?NzKex+5f7j#8Or%Bct#bmxV1$tr-xP}R_BSn+)z5)*CB-<*-Zdz(phL;^u zNR1bu@g8E;0xvXC{3l>R=RLMknq+kl=q!nc3s0n!-m3BX_-ZT(gKz?)f>;4~{thx< zb@LLo-be3(^QnX9jQQ)ogw3ssAVZ%oh5g9|`O z>gJ%ZF6uyUTc$qK?ToP_T+B%P6II!vst$@wNbZENJ!T@7_B;=HlywwxtzQ~}o&xEw zxgc)-S)qS$r;KAum*NisvLw1_fpyIrbXX>zvJ5a$&;-b~PuzhdggVVy4Ec|u8^~A9 zlNVput343_mKQFE$0=ED-93}!=gP6{(RPTUmz8175l}UdL!CaA)c4>mnsl#P_2vE4 zTqam7Yu1fNrbZrp7yc}mb?yF{vWyp_PE!$B;UcbG@LI@bCg$7Lsv5~vcbam)qU9Pb zd%>X1s~*q=jcp7kZKx5dyUx^n(Nq;hs+|8;{Mhmy)y?Cvm8jJO42gaay z1h6w%&~F~`l&^ITqd4H@T-G@#=;VG4;ria`!403+GJYGNICXDd4NJZ3-U)f<$rc|I zzY#zKMo)}p>e@aq8a7^q4%QsjlMJ}oKMJ>-T`Y=Z+q$}4JT`u;4&G0V4TozJX3sK~ zg%{2=mwBIlECxJr<7|EL&8e@;UPGThFANPkoax|h!=H;v!jZuP<0-!+Po|81(U?RW z?I@&{8~|{a^!thRbOqtv!!DsiX5d`+xtUaiPizDB1KUI`6ft%zWNH*l=_F<26Y$ik z%RT4^;cxACb*3a3-52Jb(EnSWLw9+9{U8GYi7NpCG5jC>)vX650&rV7Z4SrnyipD7 zw>5e-2~wAMr|g?3DOhuw4w0Wdwkk^wfz(B6&`MJS838F`C;!9w7-hRtQ-(WgOFRb- z4oyD(TOHULH|OORA__XX$45sB(|Sv~xHjpQn zxrr}9?;T}8$`KV{jdoCjD#KZ2G}v9Y#Y3fdNU-zT()+eJ=mw@vR>f;D_M$U7!Aiqa z1$pJ{{m;`Ym`e1{V#mL6Wfeb zFM90XqmF+Pb2Z&S44i|n*u-O#Uam2_(xBL6p-{;Fpwa>;&#EJ7E0Xkupi1{%BK(|VQ6YmtMfdP9N&bQa(1GzPgnzb8)GUBJ zbJxC_QkfX2{-E~!m9agXVZh6?X?M|L;i*5A4h1tMZDo(s*q-l-fd}<;4EWuOIK5ys z$(ue;cP9>r+LXZwcPE|JA8wavFBWgEFD)8a#%6HWmKj((vb0f64!J{`5~24{ve*t+ zvsW`cZm~!^vXAS{dX}5AgxTBcLBI}LW8A#BK>`N^@0blWP(Y#v^zHfmAoUg~8+C5uqzqkp(+B`J76{!7N}WOi};Bz3-S zT;ON(NlkZi`8>a0el#qk_)(sRXy_dl>^vA2N0F6!PQw2n^!djywjq{D-m1no^*NaS za5@0cnb2c?^i>u%{1i{TK^0KCz#lWJtsk;bL_?La|H07>@TTC|WR^wB6P4{Tl2JaT zk$enFeve*5ca7e9*9!3lnYkQUGNar&1)e&H?xyn#pCKBK5HIyf&7j`^Kk{Q)| z519@0$Tosl9#zwZEfdE}DdY2+1tD-CwJrld!mBtD<2=C4P>&sqr|r;zT(8|rZFWKU z-KIXFq*>(s9qCc!kOGtSTj|lWqyE%s)XwWDLYRpwtF4I8HWY2KxL=yqRAecGEy}pe zfRswmT-arQssSnTIXyV4?4`EUdF2dq)`Wq z?pgm>_zjTtNGDn=sFeaC0tj{;nGsYH0=omR#_nmhx*$MpyR*@F6K{4a$MvSe^TI#QGQXTEhXhh#RY%?}sa$=m36@qt|BL44@=Of;ubCt|JFu>+>@&c;4rKs(JQ>c^-gE_Lw!wO3~OTS(x};Fi!8 zbr&L=`dVAue!1fKyk}2qIz70Ia%TzEv(^H3&t~iJ&?^b2BbWSl>z=2d4l03YIxV@& zdLfq$0-_LOA4_`kBy}3WPs@C=*gd%Q)LN(q~Y4iNz}I@s^k( z8kXovwoy?8#dgqpC!M!ak_~$eh!H1RTblq!*qZ81l!H}`|0cRR3eRz}OLmXb=qAk6 zW(hT|lqyzI=#O0=NcUkdFN2JtXL9hM7jRs;Dlt9iT@>b!r{2jfxej33Xy8o@;x7nO zi6nK8|E*6>K*&<$>}D@P>f)e4Ec$hN@P8-AG zIXP`UOyGzUKi&Z*o6d!god};P{7Nk1U=4Yn!9n4TK|A72sX63U%QiRiA>GuQ=fN;V zUNMZ=?IgXRN09xXa0NihY1SU62FD@4Ew`Va`Ox}$hngNln_4zEVPHjI5i&Ums8;8= zCyV1rKQFlxM%`ITGBoPp5^d~J73#skT?5XO zq5-abdhk>lXC2Yd+op$^`hCOVR@9dMYekier$FKo3eJ2^J{Kau#H8qiCpsr%!Q%h| z4xW6}{>)q+1`oi=3XPOQaFAA~q=H<{H^% z$B-V4@YMWuGmi-eTYxr`nU8{pc!ag!_;AIC(tDQJ>eQ&D`B`QrEoSB}F7suESO4?l z))R(S0|;O?*#${Ds*na_Tis2j7{)DVKe8{Pq*E~1s!@8?w~{1NSCUWiE`G&o(+B2k zPI#ZJ^Ng394ory>qS|dGLGO&RfU4X|utiEhq=qr+A6V7AB(rP6R0{nGkngMwV1!-Q z8>!`kGgVw4P$a(HzZJ^-XCC_B6V07$waXDQ#{yJVmUSYpp}?XLf=^dbK*iwL`*KQ! zRg+>g<6K;Tk^BgCHU?g{I9V4pB2JUQO!ZV&aMZw`k0Oaa}jI!Y6y?PCWWt0#6e_z!Z*2 zL=gva<8vL9q(5m!w(~yr*O;XQVLB1I{|nF_oz-@VW@pSM2w(5%A;gue*%5$rxUBocBu8wzFxCQCc6Qk9t2Js!MDnBk(1-L9@$ed+vPEMw36DSkmd;?m{RnYvd6-C~5iSQU+Ruw2}cu-+VZQ zB%AaR^ZU&>gQxI_S{E)+F6B{IOrSnFgWPP$roQE*^1ZM;FBj zP8ZX^HJy2viv((a0`R6v?r*>jw&#lHoG^WlsPWhre4d-Fturu(G1uxYjStpx@zyI| z5h9;y`SotUINb6tcKuu3%NYR$2?)*l))3MHNAfLT-%Q8)+cky?o38ReysR5bilBT^<1aU8DwS5r#OLCwz6i$IVr~99yK6yrPW3 zqmp<-TIP~#XXvOm%Hh5)to3~9Iudw^<(R_!!C)dE^ zRZkxY>1b;6$rVKkA@L#pz@&6hqhT=Hr%e>s`!sIe3UeIjV_pvYQQ@^Taw!Ox`Ch9K z5@O~N8_mOF9QI2#?Hd88X2LC13{9>NdpyDv68b-5j0f3^wtA=9aHlqXN>h%`B>q{% zJzlRU9v=2$vs4fdOS=}^M0_&`9vq`DkbzajyoDwt@&-9WBZa55e9uFpnylPN$7r&B zqe3?>+@LA>s9E*2EkCEZ^2LK;9BbD}V7fdrH5#ACy0rU?;(!iFZBmuig{F%967>6K zHJdvDa|7DwBa0#ZhW-ZaUKCn@!clh!O9)3J4eGS%9$Vq4yol2nu$xa0Jb;XHJ3O_{ z*gDiFW#gwju1V%>No4ks%_yfR$M%FL*#b2~P9$f5gw)ae)6V$QmJm=CR()8&4k7F_ zrK5rCn)g;^8WIPnJq8)>6rl^x{qqRgD)mp|)fKOPz2ZG3>qOTWO&%oiZ-_++50ec; zr9M1H48i*k6vxuJNDkR0kZ=f@UjTlTx z{p&dqn@>#XC#Qe_;#vK#Gz>xH#?9kLDwCqxHo^;+b^ulvBr#mKc3*I?6Iu~=HYcu} zZlri`ZW{jL;X-cK_KVhQ`cp_`hp&o}@EFai*yS*28DMaEEk}QWi1IDCx!TOf!_8y# zoIyRrZP&{SrvQ`Tp6H&4A$V%`tLA$|rpexHo2Ey88iO@ND%QgY)}G{DxP%XEMp{Kk z#7=4_Qoz*SD~Qs^vvLp54IUy|{4I$$-56}WDyM^;S@$no!1eHtQ6<;gVeX$Es^J$c z2;bz0cXtkcHOe>O-}Bs|GGi)2>y_77 zmU~wwQk~j=gi|frT6lWzPGRGz_!ZlKJA=Znx}mTR>LEx^hb+NykS~S}cJ>Y}j_g|t zshf)F{{oxH5}GqADx9>@Riklj6oj(Nw7B=E7J#_eX)HqU+IsNS#QA423TV}bYxO~ z_Q!P~Fssu{0*8*0pchOQE~#!w0QS=nyMcdcQTt>2+ZE$lVNq~x6$RI)aM|m$=BP~ z1h4i9hDQrYZ_6ALPn3J>D zIlzU2JMiG%p?-r23$G#OJo;zl=bBfhzbYPe^znhd4Ml5zyEO8Ik%wd>ctY$K{6)N( zM#05 z{Ba3gY6yxkuUQ%`TLRAO`~?7cHiM$2yQ5D}<8Q>tsC@2Xr$J!45}NUQ*Quh{Yh0N2 z)ogY;hs}#dI>|VPmA*Ehun@MK^fbR=6V(HGRR*{a&?k=Z!!I))*aHP`1cnx|lYf2I zlW)0_^$NuvIBt*^N{m~fw^2pW;5-$bNi^FcnbDXbfo^bxw%}2kLk$o2xd%a}alL({R=Nu!W?ResU%BG|Y7wYAB zMe?~5?6dRo;oqJ5-FRJ;Y~TfIOoZn^WjytI)O7uB$M*tS(gh5C7qpQ76Th=zg9aOv zj4%*&k1388mNp$;q;zr~(fo6yRQ!y2M&O4`caQ*Y2|ae~kU6TOGoEKo@a#YGp<`ti z$CD@~{ZId<>2-g@;agsvz_@vqXHQFj+B?nh2kI?@5e;y5jdLs(>&KhBH{DUwUIXNc0MoT+z*rPW?rA+4d_2QNw0n&{@Y6= z?8PDbNBjg*@dN^*{lBvuloUKId}zSc{{q7QIf_qI2&LXqoZ6$V94;KXcaG82Lw~sH z$5r=RS+(esacJXy0fU2HN35Op=(){QoB+o=#<$ZMBvp~poByw)=oRwhy7DB{e7P>r ziJks_EKyZm`EuoW+Af+7Bje_!)zTuj?9(z8G645I{a7`Y@SE2T;Qf&A4ftM)Jkjg% z{OZ8ed9T-nJFiLcd10ILrF&da3p}mJ5UsTnTB)$xSyYU9_h}kfDW$8as8a9Rx|tN( zxejT!su1=0(EW6wTQ8#DVFqLUzA_;HRzGv2rt%(n3{Rh`-bOv@GFQ{lyDUG_2}Y*z z1k!jhq*@n9PwTe4sVLU<0i4+41`@a#YSdWGZJhM5sR0cb$z6xG*THvPzteO+*%<~i zrvl&GtSn2lD>vIyhAhIYvAlpRF95y4)m+9I*X#x)vdHJi^?-w)`ezQ+YuW~RoOAek z%)xybB!AT+=I6nbjSO}a<)X&%s(C|ItgWy;9b)8q?0LqKYZl@!=*(QFx z7?dk4OLd!i6OppPi7L1N=10DvxkKku2CL3j`2O{`_RcBvLq{^Z8ZA_JvPznXR-fzG ze|JhN=*|q!_o{}cxNkQkp?Ui>T&Y4FN&lrNFeT@1Q!u3xq+zdJ0TeQ=NJG`=9p&iJ5Y!vp3U2WYV8e8=&q6&AsJp2xsxYekUr6{= zh)?yFYNSA47Z@ZsRXL9wLN$Csk`u?G)Hl0`@r{aviol~tiP0DuOWTfa4xm>1gDl=d z8QmQ@>}a~2kV-6*bud4hoY;YfbM*|gMzBbhn7c)FC=cL zNtVkB@YYH0^_vYef+VlQo%yX5$LqLbj!G@q&fo8Y-xi0IUbf-96GS=?Sb{O|!e5ca z=u79h;k2sPsLt5Iq^*yJD)8pu{S!C4M~tmu^@!T@r4mK30U1fu4&6udI?I(xl?qTv zcg*4(8uX6ffKSlzeqHyaYA#qp6Kpc97ab+K3qnIH{I%2Orr!r{zNLF(U%92ou%KTM z)>JDjNQ)gXrq#M1#~!jVB+?wCBeOIe$Kgmr#qmgO2Cz<8*=k;tWTu14eZBAn!`|N6 zuIzvCa4euc4#$umY55W9{$?onVR#Fzkddyn)(2T|144nU_1&E-Qlc?_7^wd?GCT?{VGI%GA<6j2--3^Q%Kb3cp0`jlY7Se8|#b!n8G1h(T z!?Vt~jTjF`>Bm-nsu`*6bUU53wA<+zIqn->PN+Vq*qq3C`&Z#R54soGHdvVDEPu150V zg5X9O17sYolwh4TZlD9F;bK@ZBC!@d$V~JQypa!fq|RI1(9;t&a~E2WZ%4wQ%d)Ew zHK2BzYmPcWsYi9PWa&`@M6N^hUfE%ZU# zgv0`2W3>%jWQq!La7;EaH~YS!FWz~&?oY?X963Im>Jo$@qX1-~uY8_{ zdXeT)pBwW9#B4bFteh%xjD;BX7^%Ky$RFjMeOKR;IWkSuc6hYe9#GskEit*PG9gZC zdFIRpLhG>mFW8`pVT97j7SjM_26z}JXday-I+|p#6~7f|v_&DNmDQ-M2twj@`g(*B zz1jp5n7K^!BTp*mBp~%rzRM0I>BM4ye9A7i7hHQC;7Q6<1uu{`FGe{_@Kcgci*krq z*!z*b?v1H0Q0)S%_Qx*4m1AX|#Mfp@j)c(RBI29c_Wd|&*%dYzCa*vd0~ovVWSLJf zzQX9tRpQwXf!h~R+JUEFG9MJ1Tokwe5_40xg`P1{XlrJ@lJm6sVg3+M|I1GVjLnE6 z`>_c*8-m5uYq!N0)!;tfDDT<0x%YT(-idrUL4xF?1f11R62-@mVB+&Z zp^tN*h+#g24P+;#7ze%P)yIPvmRt{|{Ujs)JlJ8rAz<#U@Wj|;0vD`){Y)I?QE*H5 z#k_H;Yk;f_C%P#q#9&)rR5{hmo`aXpPimu^bZ(wurGAR<*Rhxo%CkL1&|}eO^!wl{ zEy})gk{Jj-NX&UN0$ixTq7mKM@3a}Dc4qYwuNgJ0psE#Eo!4x9d2tF}tqk-$o@!U*{osaiC(t7On;Cj+90HV5TUWf9|c*Vy+r_kMR z>It18GUDcbik$JzH8gThP>L@1gs<}iy$*eR)EwyZ0D_c2T4maUpqwZuNNg4^gAsYm zyAcaAG@Y5kWz_i`Hkg=`h97lx`|5{Y>$VfwR(w@b^hMtEsB(N-S zdlDkx5oTE;07XV79OP}n$=l5!f8}1LO(B`i+KW1uC0q)vo9|UO4#}a2pXJO}B zk(44R49GWAVqj>w3f(+`v%v2S{McJyW-y?=Ug`u~-poLEKN+o~e{hP`v9BF@nyKAm zua{hq__EGbg6OjM!k0aAJmfmABjTD*1Ra`-IQlOP02W#Hn$_3dazKf)L-!K_je|{4 zV|6GHj+Ul=99ngixHCv%?T4*T-Tn8{~J zCt~gXt_^l`h=RA?fe7glTknrrFc8{ zCU$8MooItF4@3}QV-!i=t}Ze5`2{HnHTxm-KKi+`e4)IK{9()tRY57nj z(gVGn_AEakUL}EV;EgA%qDR5-_%lciMCD+cFub}9N(aegK|KcYFd(&FX;oI`o%VQ; z8C8rjY^(khGQD`sQ0+-bhviJ(bu;-dK&;T+%m7g)hQmPi)~UZRbte4MZ|pY$Z^4;A z`#sNM{@h{nGp5Bc`%rEAPWVxR!=~*k`(Lj7?^cTU2fNVYnXRaN%>o?_{fcE&&_g4Y z{`nie`Z#0GX`bZU7aFtiF>pRi!Nw(D%1Rb{G)+$Y)O%}W=`*HUaQ zDabK9B2I)jU^3nknhFb@pUrvJjf2&xLn3lkGPnqFgn*fu6f@~tt!?CqgEGlK;Z{b8Qy z`7f+Kg0*QGORjlr!zEP*-NWvV7#(E4uyScQ=`(=E8~5#NIAE zNM|Yde~gcvlATxk#EK3hm(IEMKs&5!k65)_$~gxmhy=Sv`G&`Gl9$P0YFFX^ovt}iGgk!jaXDD zVxY`>8i@UAoqdGgOfnw-!H+F$=v*;x(lNKE?m$WFyFKv6)LHvTQ!JoDH)ybxrchk% zTQ6syPYnFCg*jQ^U8ET*lX(JsoEL4fn|%a++BUd;^!qK$;Y|qEo`EL#nx<@@U04du zrw4+hl)r*C5Hfk0k2{BLsiY{8TmpR^)QoFfduyHrZcMudm0 zH>66o1B%4?`NevIv=0G+c7GSPOmmsqS_!klep_TW8~`OstwNb0u&*phVgC~p?gM?= zv7A0;kzz`D0T+?-Ar~V2X)$HAAAVL~$-psWT@npaA{&GsLe#J!(0aN1DTi)xAo|nN zy;x$i5rZ;<^=v-bz~$ui5tWSN80Sr@n^e%=rp+^m!^evo=f@WL{;A}+t%h2ol>ik{ zR%m1$C}3f@S;%>HMZkoRvA8gdA4)k5EF7Y%c$;5=(ygEKL^Yo*9nmG=#)lenKE4XTCn9P zw;z4Lm{3aoEw_-2u?2Rg|BjT{W$^f4WpSP~8o-=|TGc!b3J0jT)?NzT!X&-@K;x2O`^8@fv8l{z6UXS|^@e$x0*+GV^c{=^BzE4_9 z7>aaybqPg9^l!Vmx8zH7y|^ZXWw6m~%a3p?zwc=?Zeh{k_M163DPn#X16PzjbGX9> z>tp%t3L^?TMCgE9PUs{7rE*q*>zt#>H3D%CeomtNx-ymkM>`K5+t#8&SRuAFxCD3y z56W_kYznL*LG^&ji3jUO!p&Jb2o^O~rZgx^^w-~T(zzj%DKcT%6Lp zAH?1^ajDPHum<9Bxsl~Pu-IbtG5|s%n@i;$g(Uol(R@g@nAYQaxujLbDcsU5D}uxW z4<)LyE@NCOvxIT+3!wj+LN%L*H!}nS3`;FlnWM_+$}`ksHfS|W+?RPzAtNeGJwf>6#yrq<}hNq zsWvravU7QbT8+D>iO5oyG2Qq22aYS@s~g-Gq?-QOsly~&1NFb{o&{{<@<9%)%Mm2? zdvlerbj(JiaEVg0)xeuHYZ$gbC->( z^N+K>J8WCjI8CF|vbTJ5YXL;VFTg*9e1zups+dW62mChCOL!Dw%6_qqk!9i8k_}tw zCq3p5ewe&W9?IF>W!K8Rpu_?SZe9_caH$0CV8&V+2UKZb6+RyUV#63RltfIuS_%r% zQ})4-sp}#Evk|qOH6~Styr|}PJh11ZsOs1#uhR2xW(XGZAq|*akbsg|m_l~H8&6I# zSyMN2*V)lDbG$Ls!fF!6pDcabNl2r8D{^}KIpvKEm87SK%9f(!?%l46Xa|{vD&Vuv zV5Mu3U6$4eTCQ>^yNiNf`GM8>2~UMYRhVxnKOsdGHT{!|`Lmph+qg~ zBd_)4qWLcc8sOJ|s+%*+ug9cc0DAXoSR4an1mhp9?dqx?4yg2mWMSpPCE}jxn^A7i zQ9t%m(+l-kaB*AnJ^5E=(D9^P_g7v&%CKg6O^z?^VCF9rm5RqfNsfGNU5$6~La6Bm zLf}xl!4OgmrgF+t9nY$?fKFHub23jhPCAlx~4i=yrOHKCSLM+b07?bWu(x&*45B+I-!Xt1?`Zdo;Uct>3wGeQw=K(igpeh1reGn@*O4 zJ*3O$v#VFOOPgKBHTWkm_iT-P#;`OSG62yO5`#ioy_wgP0DARA-pCMsF%TRE3l)?J zgs)+L56?6mN(MD!BBiC4dgFj_#k?oz-1#6iO(Yg?LTY)|9Jh)utOo79lOSOm~%?9 z9`1RE!gl6^wYY4JQBQCq6V>^Cm># zmew*@ErWj|=b`kfl*kjTi2AIVpKhRQM!iL~2V6kpHbwU}Z{=n5dl78B9CrJ3$|(Y8 zBs&^10vjYY;aVkUna8F=RhH);Y@iE|_yX}?AGY)t@!tT%y*r;BHRn-X+sg4Ad5T4* z-68QujoRQ!^m6sq5iDbO%NT~?@|>^t1ii2Khnde=iKb2ceLN~lBb$G3Edse6i|g8lNdRwrxNHW_ zrR`lxwXM*&LLSe^@`QJ#1qkSvLZ8$#tj{gUWrPwhgNa#V>d5n4&xW+OH=Nn* z?&)AJhaf;BOD6y}a#=0!EVWn!)3q67xG%`DNa)Q_7Butk{!>l+6Hj~XcfKW!TnW0n zOYq)SEIGPsA>n0(TFuUM0|38+$7RZBwPj0zamuQY)wH{B9>wM-Qtd(Tsc-Pq7a3cJiAU^)(>AK@UK{F!18~?$>v%pw@`m2ybOt-I&` zTc3VmNMN}I=|&G12!^vhr|rRr6lvmIor@`(~Vw$U3}DX@^Vs5^7Nu3`lSy zmZX<^e2|>0VTdBAhEC*sH8P#302j?_Elz{YLfu#$wtO^72u)2GNsA1Avz$kGL)5|P zG)`VMp!bEw@st7f6CT)j)gLZTA7sQNcEmIyBQs1f0ub`}W|t$e;=T-P#a1paZB1zs z>dPral{m15LM9HY+2QiUWlKQB!VN+eyz&1IrdVFoE7nal69sby=0n}yE0hb3GJSkh zw-n;b#J0n|1j*Y1w**rP!4ROB-%Mj7s{e-a!%hRp01q?XDVY{?W1oJ#!wAZB{|$YGr$W>{)RH}{X0m%m*s|wW+`WbyYm%o zV)I71>S`j2k=8wiN*{~5Y-YvaSTC_IARf&Qny zn@Da2Py1hXG8#Qa{uPu1u%)(Rx50|w`=aNdqGv3PxE=}|2n>G_;Yvl1(wCkGFF-Im z&tWQ*K>II68Tfg_6<;qr<~mRPM}E?eF#OPmdpjn^O{!|Sb}yr|RaApA%TC>6h_+)D_DR$N2H%w!e^xemgw$$6gz8cEj0O3VO7&(9i-{r)pd6p#`wx@L9G=_S zSW1fAHaz1|R#?|gTXn9y%RCrFVcuQZxQ_BVWN8{L&}tGj3b&{8`iafAqJ4l>WP>t_ zKDleVWJN}$%OC~aQUjoLQL$qT6J}CxKUg~^NKm9V+MP;?+9hq7>u2`NnI_}!fliT+ zb=^c0v6QFovgua^G}x|CW`0VFCSnwB&d^M6G|}H2k|8p#RRcdGJgyO%H#Q~PQeOqi z7=cA)KoG`j0Zi@uIv$&Uc^2OkOW;)|Gz(s@RONhhMH2l*c{~=4B)X{C5t#lccdfuG z0&K|h=QeLVLo(k&P3&e}V|;~O%06bA@)N1*tI$dBfn7oaxJ3N$nER))dAy*H@194L zFDsTCZcpExJzKLfMvKJ!HL1S%OCR%|g9>`vgtZ!}VD=h{LGcS0K(S4DAGxe;p$Dkh z1_f2>R(TWIV2TE};S`Q1AvT&QHx;)EoKn%evvKv)Ks}_f^A4CtlmjNHIJ5NP1RO=T zopXL~3WF;GG(7DnInJB}_6WitHjU_!U*nF$4!ZN1%@=sUWyjPHB?z7q5cTnlOMa4+ zK_I_!B>%KG?7KH2`A@@_a&vYtPhZ;!W!n;884I9OkaoNjId!!fXn~g4$?lz?_mDv3 zyYCM1HqJa3wC@OU!TLJa{W^SY)u3xHjv-4rxz!v4SeeBrR;^qP?uJ;5_dxHL3nAT* z)-b;E1ai+Xxx*xu+=?Ofy>e?bP%&pCX<0}n|7xjK#Uj-Bcjjl>m4-0>65wo`)yg)+ zI8#%oNwg%Y)v2A2X|GwGd_gaQ<0Ev)1k&DO%GPW01hh-gU=Yk}6_t6g8ojZ2D2SlWJ>vsj zmqJsiH^D$W3@49?A)f$I+q3rYP%&uL*swh1@8v$a#NcI_%7?qNnIiT{2!5T0dN*vq zT+jsxbpbEImA2raQER-0_3~RBG#RR-YHID7E;anwwZ_T#UVX7m|(dt;M&|$j6ecmFBsx_{F z_rhcwHC67n2r|txttO+zy#5i7C;eZH2zKjTefj*1#H;}?=)XW8kh|LQYa~JeuC&#H zKP@e;mqnn{fJK-07;Bv4h+DDvY8{3f+LF@%>(++?v|L4niP)S8lm>j^|Z{1AACW>opANyjOFDH-g*&k13?1?Xpv|DP%shCvoHU@7` zH+I!uj8|5b_rJo3H0@kC-T+iho2|+V%;i|Bq1~RQc651V@PD4p00%kQKcCj5*{78{ z;ppqTJ9_7;YDLtl`%V2bY&trnNuz}pW~z!+7bHG4%|l6x^Lv|00oVTd$XuY_`Pfv>z!Uk39^R3`vCwSg$j2um6DK4S_S;H^q;M{ znc*34i<<9I^b#_)CQFjEUJ{oA%e7L2lpD@c4`q{&`Rz26*-|$s0wXqcmM=6G+Od3b z2fkmK`iSof;!YBfu+9mce@~vxa>slUqAbasA^MBSJ$A^o0DeT^VJ@eZhF>W%Pr7>a zCItTqg04&-@#da-=n5TDp9ImE{NaUp6gHxb>cpCQn6%Yps|M=x?@Szo}qi#omo*@m?3t}ydyb}oz&zI+bU@v8jD?tu} zcvbx_^v$Fa)@ITR_fSDAq3Y`RlQga(tG0eVPOv$7?JUA}l5>gY*n~`(S+K#})Lj&# zFl4**Pv5iNSH zk%+Rb8$|OkYAt&)St?~#WBIHa|Bco`KUh_95A%X6E~A;VE~FCuul|FqKtOi#)m7~V zJB{k5bs@bLG4(wW>0TfEw5|d~$f3M_8LW~-!q#^8CZ|GitqczBvK_9oU7f5;DdmeM zR}y&I2!P6;dUf`4tTjqfn2jmx{v+GG+RHKWfZ?##*p5uxoNz2vs^@fUI_4VOG1ENl2Ov zbQS&x@>dh>aTW)1nBhE3PgqH1@-0lbe7brj5bz1!v?gHELMcBbK`OVlMe9oQJhbFi z(8wh$q}oTSe8XRY&==kj=ZX7i-Ei8Z2@ZD3ct$3j|36fHQXLESeZBm zjo;FKw2OTA+ip|l{gqAwrzMDGj%JRrhZ&sPWbN|V_%q1X!6em!*m5H)t3l2nw~*D%QlV0-8AWYREp!WIF`@bIY(NSSo#m# z3|v4D$hpyd1+8=AU$|!bKksgn3!l|_|lPOS15=g)DflW0KlMqS=O1ypQ_a<<;|!YlF{ zM~ITyfzeygUyM5FRhR|~#ySQ=+bIkm=_mq;xP)yha_iOa7L3|SezAw}+Bs7@TF zlni6y`V4-53(3qzeZt0=c<78&`uuoNU-E-n`3JEf;8?tq-)Zx$ zP7|xAub?piam3?y8i;m|kQ?RHQAzp92}pAW?2T`d**Pfwsf*H@k3GiXh8|>vr0Ygn z_s6~q8ZlGO1nIvQ803rxb-4$31*WSzaB?Xy>oHTt`CcA#CqKA*MNR-KrkHUcz64>T zXcxgCqWI4oGKAp`XvBx%G2WDDS)m5PS~cPWb>KpZw8J%V-Z7XmBXnWwK_q=p|9BkC zf_3;YL<+4b-SKGLkkR{1EBXKn|W0boWI4}ItKU(!hD<3 zk9cd!9*q!G)sIB7XG#GmXi$zl)Op5D{rAAOTFPewptTV9Hm(K2Z$KF@9Q^v-9I_nw zx;Fho%WureZIP)-(N$3B&fS~+Ye^7N`{3V%d`nwj;Y8Rh$>_4;;~U zPc4r;cc|)QY+EzIL;^1rMMcm@Q9{pvwU%=3K*jt?ZG@?D9uq)T_BW!N{knfCzmEq% z4V!*+c&J!M_>(k(CNYFhDAD#|VsxwB@yys;4LX~X(fRgMo`S5E0E`{`x#}c73@5Z^ znsL%O+0E`*W~kPThGgHWN7TkEwhjsbJ+VHKM}QIDn-rC4#&Qi%(m3cskSA~AGpGV> zu!?d54g9{l=M3;+>!?C1bYq1tVD5c|dWGT{e^5J4C4vXS(!eh8v_)I-SNDAWx*VO& zJ)~pAtz%NCL+MT7u5|ikOO@K;Ea%jMfxeq{_-OI6Ho``CS~RU7PXfNi4y9bAvA18Ug}m>TGYQju)U5WhC6#>h_AeWX)8G$S+PL7`S&XXzNhR+ zUY-BFiUc+5}k_0Hw=~7YVAuY%f?xpfh3&Q zgkt4|4&IA0egZDqAcex9UaEnInLvLQxB(GG7fbNKaJi5~PW#%y>SpxjP>Pf@ zgESyl-^wNZM*4FhH|1P>!G#KMH0=zXxDqB9^B`)f+zxr$jMsq|&+p`}D}5x+D;TfA z(a`|Uv8QaV#}t@)D1?UPwDtvKf3)cLNA+dp(8Y%Zd!|UzruF z@pzse7N&2#fzS^!{CF2#&dZCt_WmVTW0s`VgZ5O zT5=A&GQrRPP*W5zQX;6IEG^`#GrGdt-VJIrBuq);_}7r@LDJiz!Y)=u2-gl#9YIBk zeZ7D8>VBWyI1%wzsHaHTk3HZEd#ZA_CJrr!8A=lEyUT_oC(%Qe&n*aVp<|1pJ(f>ZU`tcFA3^_NlBQ4ocQ;=HnfBx; z&RUhL_>9itsb}&G%@L=qImm}|D zUmhR#`s63O>N=>|lufuf-M2K1W=$k@d}8vgXJ_#Wre>U;AhaHhp*`K0P(P><&a?{7 zD-;*kX%+J&i_};z>Uz`832 zM1FwCKSxr`XWlfnCLsWP(XVn$gFmBIn_rJ%rgVgUWeSCB}IDk7tUA59ELX?!;_Gz5&9pR-60Q z!9l7E62CIwri2Q#q1gOZ?KH8;Wb-xBwl@4F&uahXmg?#E{Xsscj!gR+9bLxi4R~V1vwIrm4NPae zZ)e1re|FBsmuCPwT}y7axPtp)HKjM;Y$kHN6izqXG@42s*%HBYdo~2xd#oL*D&E@2 zr`-gpz0{nN?T&`__VP@S%*2$8`X5=Y+{V}cP7e)Z++GbR3RUvGdeMqd^R&KoINB(m z6g@Frf4G*8bC=?|ZCpMY2Xgom5uO8N3hds?5MQ1mxF`W@0rcpBeS8}pb2Sohh-`o+ zNLZrZ)@*g=VY^;6_~=~xJ3A;=yC^;%%BFH;-OU|J)Q1V5fjq_A(K;T8k}Or;r)MZ=WG~h$=bfeS)XO^PLeg)|Jgx%nhB%R(n)^Tg2P*S4dPp zklAa~Ra+-Sk|<%7BI z{QtYRj021U>c3AYT$ZQT00sh@0!bCh1j9*{G$e#;1rY=@`kzQ&tA!944>-)ChN`-i z)y*&wWFpflR%#t47)0x&I2bGBe>tYPJ_AkRfq<0hQw`36(f+#(u(3Bcx3n|=*U^~9 z`Lf>}d3OIohr|&dS1Cz#!Q#%Rxge9XqH6}-tW>vva72+`p+_sfm!K8i|M9);{@9Br zu`RX!Q*J>=u-omut54a!gJwF6A*L5Tt4eW7TCOgtc@p9~7SYL*zUxYGh(5b0lQdC- zd6T)(Ub|d@#yh+Xm~7B!{O7I^?^qmc1TjEw38lG)xhlA@J}f)Vfvb3tdSU^4FK3qQ zt9s48lbPtlVP=nXIopNT_)3CFUJLW~E>vMP#lW6S=adsYO?~IyicA`9Matpm#Q4xe zb*RLk#ys{LJ-u#h5IJZTx0~~YkWFh@s}oGr;cY9Beb%E0(CbF6lC5e6KI1zWu%yT) zxFoex&GJS_W&q7o+juo<=4oL3JIrGtRx244H>piKva12N=IId0q1l?uH3YFog&a!@ z+9~!@%8W+N9|QK|@%;EQvT~8R6LlGqs9sFYe78WuzK!(k_OhS3|t8rBa`uFm6TK@;MHNE)NnKwIQgiInF8#kUz1w%g$ zlt6LW1&jHrDZE-REaVl5IS0y=ZRJQ5t1O&nmv~K)zj7IX(A%rmLGYX zqlfo9;Qs3He27R(by-|CCie6>JA$LrO7+N5aR*P)aq1PQ}~HbXyc^%9y_-5|JDf}DW?Ff$XW6Q%ihYrEMdXA@%p@oQt+Xt;%C zQt+-B#YF3D#y}gFskXwv*$@G;QS|WeZ%iEV63!L=9Tmk&gD_BBh>W2KdMg6I!R?sM ze0>a>euUJa#V5mxxy3ETtyCBKUH|XC-W--VXd#v3n)!9P2)g3Z;$W;e&sqp3d`p=P zfbENgpk~k(WzRcsn7S%9JJcxMog29EODP3j);z3(BGM?%i_lXY|D~FYAP@}aQG8K? zbv)fU`4~Cp=kb|?uS=R9;WsDOr=^nzdK4X}w7-rfx#ZcJ3W;{i_9Cp_PC%=%nyWRh z>^T&XZ+Ci}`yjK)$<7F7ZcBDI85~%}Kb9ojSZWBTS^Rei?B<|ic^hK!Qtdj^{l#y*NS~VTa@>h|1$bD@xa-eXtoN zUL@ey4S-C6lS)d6kYOxKV0w=(sh=o!#vRNvqj$=q=MZvm8T0*d#Hw}x7!^jAz0R?( z)MmOD*8KC+rrAM+vc2xGo!nCoTrR5x-gtQF3%sF@n*e#c9To(+N+1Ng?}bVK!`hkM zBxIS_&MAs4BadP4)@ScBu_PIsAQ1VzGKF8 zZ|kDI9RtIzo~({NUt{(Y9PTAHSj^_8wt=tPrn0UD`uT|Qf!AF+z^*^vaST?$1_B=F z_$)`*kWr`wi_I$oN9>REnN7)i6U6J1AFyzQX|j!UPkMrCtoB%4A`xP?n-tD#!oc93 z_L;o8FJpI$@5cG6NNn)fGY=ZBDmV`PP5#3u3AQll1%wsiwn_~1bj@E%x&h)e+7|i= z`87~m@JaAD9?`vDfW9F)at&7jT2z;#*srcqu8Uj6ZKxHn9#FTIp+-;Ywd}4xc$1nm ze|7cBq!_SV<0V*z2*6u7TlAS4l|qh(Ybibtm!O2JSJ~4!t0Rjlc1%bfK&z|keqpJs zVsYnHQB=EF1;h9Mayk5GLjJ2d_-Ja1L86Ht&LswbPqvo2S$9#?@~QX&ZDKHMT5P zc-q}UT(>5+CF^YF{ORw_=O3j#!amB$yL07#1T&8D$NRr()2FEA(}rpp+tD<>Y1e<7`D# z$R^EZ#!PT}52R?FG-pEMAX<(>QjWWsEEQIEH>Z#r>4|6OUo~sI?B!zdCcNMjUJW_X zck}~W0h(6n)*A{|ovwM+3}T%%RoK`(AnDdv%Do9uUV(lUVyB?t#wT7U2DI97;qYf* zJ8P6-MTTSboZ@)_B^+nb2D~|vZ_tkFW{aBtlKC7G0Q9XDGRg^mjh0sHmMdmii9=>< z0gV$uIgq|zHfP}cIOBDt!+1Zr9~`;w7{*{i0faQOVfdtOS(dQ%v@Ta~pLr$Ez9#TW zT$ZO~C5MF_AfS(bKzR8=%CJ2f?|(96GkBSn^2sG1hnS0Kp$OJ*92nWP+t%~%4w(xUJ`vveG2si8)OMO6)(ZK$J z1SlTp^!!53(-zyv|0D0!pb!CAox{8gKrN3(6Br4^_!Y>(N)_Rqvs_S^4Emx5lxuBM zdWO^>&llOU)`rb2BH>JDOyjqF_NRs1vpZ)oI$HI7b8!^_kQYpv&fH$4_U3Ba)UkoT z;=$QjD~At7;+5r4W%AwZ4Z7atZU34+2CP4?XC@*-1nDorUvcsP-1E^7K!QDkQuW(h zV&bQEuW<;TqVnh>XzJMQ%^DP_87$r=KoTWEg1*JF%@x3V@^VOnvchCzh0JL{@O~$N zFQRJIF$ZQ92pUD>O4zQq>vL#Tp5Isw^)ISe)mk9AAhFBA&{3%I0;j9Zk?#8?0s0+j zZepF!2!s5B^#mbu<02|Zb8fRRr@(hwsm!7xEs+br)Iar@3P!_Ly(+{h$`%$lxZe*j z`BZ15^`!EfRIa(>uRPd(gWJu77f;X$8V4*W%hLsEAM8R8;dwRFNOcWJKmVc-m8NRn zvQ;Xmq`_p`c1|Aq8!mP3_fqq#hcpCw_=D46^f8+V8 zkFGp@n9=c*mu-|fz^M`Y4v{3Yp3)khvUg-CeM>n*!^6C7&_0_5H0ycH8-HkNv+DlB zC2SdDpn??KZYXo%wwPldAYCCQq3(Lfs7t-Jl#H`llpQiJk27_St^sN?2ehG_vHh{% zq@0sZyicTObjC}35=J(`;wuhwBS(+=xQ|PQOtd50{o}W$AIOh#1!Iz`5>lH>8Rph% z9*YucN-B8!#Y;SbIZHY#_4v1lk772*>Lxb-E8m*=<=v>y;wkG8dgaC59MWR7G@l7U zgr`k`FX{z)OUL`bzp9=44WN%I-d8;dyB2h~$cWJeuk>Zqs%Ra@cdDg=`!c9d2azUs zm$UBg%VT0#@(@yOr?rK6PSTqiV#fXmmv0}qR5cqp!WVCal&nf?HIsJnSXG_~o0HTy zx$^^P7}5E{HT~+GddSfm5mkeLNn^+_tR76{l@X= z)hRajKJdGJ*vt34BZt*xHNW3nn+E{>|L}CTvMpc$4+yAJ2M7rFKV%88wRATAhtsbA zuikIlf9f3X7ekICOM{oPZYW$YUdN)UdYh)!jq8i$76{?<4#UKwVCC@#i-2A~AR;DQsfr5 zT4*aOE4#W_T9dEq0W~UqopKq)o_Q&@iI^`nk~rSJrCHK~Hu2j2wz_?7^W5dhTiQ#@ zQgjpNAxSHPG7@okf7?Sx%Dr_jt_c&K33`Xs09*L5w3GcDf57ZyC$+9m zW~!vG`%CDp9+_DD6m0peM|h9?#F5m>=T#@K?c2p{Lz3%)%KI?(RLP~3`^$Ev&bgfZ zN|=?>-8PpXhe&R#^*6 zS&Q7%O>0%q#?fmMK9WbT>8hz~{IcRoc5Q&Rg6+N!5U@5}d30J4qwWa}_{DDQtC*pZ zq(*C?y!bWRxVqc@_x|hU#9wxiB81MKE`6gR_&8#d1r){g_VwieivnfzKvZY00P?w_ zycjMGJdjS^I?p^x$tZ{Tu=SF2()y81arFcx<8*BH#7}X#vM5~j+Z~ow-JhupA_W9f zE<73*U~v(xKA2Hk#L_Ckx0g@roJ(<8Qf-P+Nl~fAQ%8r+>zWTLt;WG3w_%1&dTpe@ zgE6L5vReO-4|ORbZN1Y={Wm z05=mfzH@HtrN%(x6ZPO3z{uDmo5cGo%YzFYu;VKau6y&9tT_e>s>6(aGJCvZy$(dV zK)WgWtM4DvAPIs$XuoT7aY=0UPL%<0|iBHsIZji>N!dce7CB$)*%|$rjRFJo?zcaTz3rTFAeBM5aCoNA12`dxK04F{ybgfv+fD~L=e^n_U zLqs_>td-Z8ufT#vF?k6~WfDD25p00tOtVGkmzZ4C231aTLUM4d_DFBD`f9lL!- zaTNy_H%^8~D1N|oD)bvcI@F_tc>p?3ei4PqCAo?XM z><mBV-eCn zl}B2_%H-@^4nmt@i;KP4-2$<2-F=_#9wL;BIR=-KiNYxMJBeQy;V2E44}za*LFV;} zL*%X?uuxk|E1t_Vj_>c&f6rg-0kZ(%*7ge3e+({p-naM4!`jXe$cTxsEvx%X)R$UP zag;ZDTu*afUBP*Gu$sA z=5|^0>=24YA81T~0c}I&{yc&-n1vCzPg=Md0AoyoASk}Nk<2+D z1M1J$Z_bkC<3Dm$l7-9`BzH-tVBgS;_=(E$9xTu2&ccA`@f9#J!-u5{WtB~3M;Bhl zXXJ`B)5WUgt39Ghr8xTrfTF5CK5tA+3?lnvkz(=IoNkki+At_WC(qGyw;78I6Pq5y zZg>7u%xk}-Tek)u;2Dd;2PCo!VC!@z+5>3=c z$~fRf+fmt7BhiHKC?&o-{1>3utRrWj#vcLGA@L9@k{@~5O6`b#5bybcBFNX^Xn&iB zY7XWP21R)&5Qov7HHD`_Z#_H^pr zYNV^}Q*?WXUhHHlIT_OZLZfkkzFK_gNT1@9Q19*!LW6cGSSf+mI)P%qtq?azB*Efi zcOKYtT_6hfo{C{&f%oOVS!c`sd1_b6H0TaqA!pu82oN%#)*K<}^8PZMF6^3CEws^Q zr|}kNnXIT60sQcoRu{b9ugCtgqLQ z%wt{Hq%Z>?XUEgnhAg<=tO`ki8*!t5UeJKJe@ArSKsjksyb+KUc%5gsx`xvUJdJ1o zGNlnAE#%90Su)~9*Cm}&OY4-=6H)KYTopHM_l(Nf1Xym+K zAgkZCEP?vxO8(`S5keQ7!;KJGkTol5t4W&Zw`|Cvd2z;LkJ{@S8FN5#BpI%r&r=sH z=}gD zK%Mcn0}vpBy(Pzy0}!~C5cw3Nicy|yO*T~aDuF#E+Vf%|WYf~HT&%4LC^SRKhJj0- zUa`dO7Q%@Qpa%&p(dPuw0;Zy)=H?owQCy1|Z#lt<0&H*Q%7*gJ$Kv@ANn8TgLym`v zl@QY?Gzx0MD~+lzf70P_4s3@NI2vxX1}kKt`PhIcPlwlecW zA8&sn9~(Jp=gkoX$&Cu=UCh9y4g}{9WB#6OO=M$F_3zm#wv6N`8=HbZG}+6|I3Jl)+wvnSoqMHv=mlfpr=$`hHRHdw;$Bapqt!tlg>zoy%M!@vs$NH}G)p z1VH2rQhn^|I9QNT0+0x`%aMj;d={MBZ$UC7TPuG0qz1ZjDy@y|w>1^4FsS`VVP&LG zleCzw?32Lgg0(=T zU24;eHrYig!!!L(YpwGH#4B3;GVYYN@$^3RI-mEp*9j?DDye8>6d^?}`T7CE?8d}8 z-{n_hvQsvs!wD3-nhUt6S_MJ`l=kIC0D0iwYUH0{t?oY-KZrO8?qq^X*qK+U=-;|?gf?Q?n6Qf< z4gx3#_>@rJ5nuAnFCd^66a&YV8uSWUM63g({FD`*#!oiGGm%{BSJX<43 zLLvYIlsE0(00>Vg z)#!FNZi3f^BEI`a3_Nw40XvbG2t-ztm}e$vYY0a*t;8G{B0QW5hr~Fj0FiNu^wvGj ziMks08r6EtHuwXID@TC3{mdMB$#rMf7mw{YafS+f1P-E126|)W*y;9U6U~MywF7o? zN=Itn-2=O3zd|FnfYPz?2*){{iQ!^7_Rtdac+k!K zGQRKaL&{yepYQQR`lgwu*rmo z9ED?c;SiTr$yDdzXk3yAZ}YGHp5Caj{zLB zRQLB~r@%c-4*k3Bnsj#HHFzD^-|KVj`=c}W0k%swmLc(of{L-RI$`vXE~y0`f>2s| zA_?JIfFI=pT&oR+dsuouDT;tF9k^EqvPn>w5m2sr98Pg0Epw`6AhJ!Ri-Kvx7X(6q z6m;AX#dx?EnB?IM;2!-D(HV4?S~uZE9tLhH-pn9f5bY#M=zKAl3D;&YuCQP*TqnI5 z$4Y%-iu|%iEEp`m}vMc`DGw9ozf0e>}P9e*e+y~ zF@;frrJ?#KuZk+0b>byzS!WfKgiOz*L@c-473BlKDqWn8cYxfg^A2u=MmC=PGb~nQ z*7M5oXJz$e96w)t8e+53Zvg1^MMZUMOk=w`0*}fZ^(kzlPo!{nhLOY(;+E3hqO+Xl zk&U_}%_=1pa21EmN$!kQWLva7we<+}!UDY~9OMRhU!H9P|8dJ-@dXwD3^YX5u{0Q_ zdD;BRg3jO$?}WQ0>>by^QdIhoB@D)9m*P?zhA-nM@|qpNWrl^~)2tCzCduThfIYBB zfn`eTaN3_#BDT|)!o9Y_X^j+UmZb-cRCFoK%w*#OxIe30C#M9u()O{_nxm}Lsyb4cBxEEr!Z~E>^!C44RPgHVWHW^l>QQpBT3}o3QI;$fjoe_ew*mnd4%!d z4eQd})9I^vNg?7jKoxA}o}9vl5L{S*%Z#TKqQ4(dP2dI&jK%}$@NRgD@YK9zH^9N8 z79N@d)VdLY(vQZUkKjrVI(2LdQ0&h7)v1<^YGbX3*peoR13*cO1eEeUCEW;&3q`2k)c?NKC-&vBGQ4*sPcynB6S(>u4+uQh4wl0!%8@jU(m+h_$j0MP7gd=iM6oDuy{-#DA zp#QeMNCO-R)vo|DEBc;*HwP_|GnKS=!8I3bDQ2m`q@1*w|A6CFx^JLcwWIW}t5Ab0 z5Bd0tAEjfjoZmZQ0Af)1R97zz#zRJT*jRJMwUDYa zrH%h`?<6?nN9#viGy1k}F}Bg(mLMbMABav5)w$f+JRLg}4MAm^+jGWu z^d^ZZUTa%YibGC=usFXXH(8VoqgjX`(|JK`oeE5TjSIvA+i0dT(goDiB{`U~nuK}SiX@gm(mqMK3!&}{jVuBFs7nk(<}69~28WSDKoOZJHy#8$8&D_VzL zU`hXMGu6i(TcUpBAABN!y!~kO{waqzJvU*gSBMsf%Hin<&9z83JJ8xu9D?ap&eop9 zRz(j8)d>jgy%q0;Vhi>FHvv6IV4${?pY0+8%_D#)ouf>HF@d=J`v(J9Alp|M z?-n#x%2wAwwT%Mv$0D#C;6mN(+sCIsGmC8(=tcHVmLlux?k1TSXAt*Y`jfMo=m5d``KMLdgiJdT%SoCOL(kzP$kGuyBp?3(7! zlZ1>&54KYLxT63MCzC83{W;Vu6m+(Ny>@NfH=0jYC z)#4(DKVmPGLAdSCNZXEpn7~H$33O;Y+9CT9TzhN}?cih4HxkJb5g$`)6>9$oxkxY; zUAg0|0 zzp>WfnjMtn^4c06V9HR>uT9Kd8DT?v=D0=f6G~F!9xpl;&PTTd4pCZ#Pe*hN7i}Lg z&ew>Ul9r&{G`k)5F)o2P8ShQ}&`iS`ZY+y1+O~gY_HCMh= zn8A-ZonNG49jZK`l8n(C%-O)P!=LQGd{y=P3bPZc)}z&oSz=ak7^no=NW$$D01 zh&Q+vLP^8VrcxXQ)V-Jqx2DV9t0W{Z#!cV;lNC3|fQTD4GjlwlX`P}D6#pk?+xcat z9w+z$KvgKo++jwlRNpW1hN;kp2-nAz5KoWEllhxO@XD->WcEoLZViMFx_FmV=Ah4V3Rr!_}$QRJNG~WM`eae53oCX+2{A!#yssO|S!| ztx-LTdFM$c>G`!n9u0Wg!@-g7~0;k~DJHDZX8<8q|1mA1yYqd*rH5kbef zEcT7LDc&Ew0YcA|YbiZiJlM~f?Di90u4l_O<>PPwG$$LXtNd%59W`HV+(WgL zsA}_jhmEcii-ftMSkU!x`M<&P**T7_i|~cgXPID`L2a;HDEA9;K182q=*BwY+kHWY zB6GY0i1Y|O!W2lUEwwC63kk5FeSHG4a{qaI!eN^aX57$!|U8AO;I#pvJy0rgjm$y9${xR;R zSR9`#Tufw!{O!HKjnvJ^ugP@+k^pruq78lm07snuwNPQgo;%jQCSGc?4)yvasKeA9 zkJI@*jWJtyEB5*x$^c;j?R62k)~Bf#E7?*Q=)MAjh*oWo`d1CqFYO8xO)MqtW22l)H+>Fo&011 zAnO*gR3;KMI}cm5?Ebqa4Jn)B04;Dm&{P_e%}J55mL(#I60P&jDds1L%r~pu{0G3C zgHT7$nfV3&7x{UiueL8i*W=Wh@{4XElwm2m^iXugo^so-Pyc}~k=(4+oZZq^$ug-a z=fIK`YuXNpR3kn5`YJwQIEvs^&_03z@ReT>dGR#IAFg|Q9E-mJV$GPV zRSOO+>`Td@;PK9tMqo$r$+K!oMkF2CvUh<%S4&?@0@?9tVX129D4FVY*)sR^t|T0J z|27T1tC)IE8qtx}&+98F7h1!gCH*PQC`Cgj5zvF43kx9$xll9-C3uE1WgNF?mhnaj9}0Ah;h2;hjSg zDdVfbsYo49*~fPb=${V3_R|fr&wk+Y4Q#1s@QHuqU^IH`-3M(U<{P~rvYBv29n8LM zg_B*4JXobasy=F44}nFlamAxETnh-D2DTjELGyE(cJqg{pRWon|a&?Saw<*f&0Pq0kt)iW1i;ip`T4) zymqQVUUs@V0o@@w6>F6v#KiRe4JGqm0N1QpP@{GR@*hVYoTY$J3{Qsw+)M4RA21Bu zr^HO^0!ytbKw*R(1%733tUGBPs*JKqt@^gjn#H`fmJWLxIq8C<&eu^3^u% z*p^s-_=GIAk6f5iK*xsoA{!yNtUHnM&JU^MU(7nPPplQ; zeh~wzd#d_L&^u!UxMrP!xIbVD-EIzy;iRA&KZ~`Ba#F|!NV#Q$jDe@}mw_Ca-3xaGeSUu8Kl3uVaf)D5MK^RvF}L%FS2%M_26|53pj9!7ejvN zc8G%@W*WH+ux=~^Ey*Qkbn5IzJOjRQi1J|kc+?iD%m2Du;qZBHLbh-(oHUbY_=V2+ z50TjUzRIcEW-GzHm+)Lq)4MNILKg;oCsQLlT=;P_f_4@?Y$8 zh7CNSL@yWxgDid*$tyt-hK}XJ5w;U#^@73ZT$yd6%uVY1Li>>iK1_KjV-@cEQ{=gY zkN)HWSX<)`SIy;~JX}L%hxZ_cH_=A~0`~BquO6uXCUuGHlF)aleXfc1$+_D~^~xciR_`<=_7p>~UzP;7ZS%E7g*{mehf7$B63Vrc zVS0}{uO~hWow+gPt@sjvnYT|uy;(<*r`}H7q96GGe@4pF1Xt(&j}%)D_8%`d70;Lu zx>dmQ9}NAE@7ga@T{!oTw#y6kA7{4p%nR(~f3#hlzlPeM{}dLKk%53H|3}+>@dLw# z`0p_c>~ozFSRkNR($sqj5VX`gKQPEtSQA1xK(*G7{b39G&o%(ObdcEX)MEH$Fw}s+ zAywMDe?vNMvKgzZub7LGs?Ve^R##oA~uBW!DOyhAwq3C79)3tRZ3!Fj{~@@rb;yKhorG~V_F@LkFpOZs-&{m( zhh_6ay(s72%3rmiM`A%2*L(7*QRj>wfcOj924zv_&) z7`$ei*hOk2g7{_cCcK`(})DquxfrQlZU?2#eJQ0!QVkD8iQy=&|`4gE@m% z!DvpO@zJg4NM3e!L#tZ40Q0zhMUpHqXpUJbZ^a{rCW|mp*7Hz%#_-W|7`7sl)doqmHo}ObSDz%a_3@Bf?NZJ&ZQnP6n!j+v_ zd&1z{XPFIgLQJA+t}AN-D6}vSj4Mm6ZZ*p=L1)>+si3R=Rtnx53R_muf(>%K{)#+F=4BW>a z#ZhD!N_dLoPZqww`&6SjaIh5t!q7YB(^O|}X;GM#S82DyX^ze(ln=VSO+B&QsWKxPFjjtQ-son8zEvg=U=U{lif=M)>bCS$g!K2u($ zaO1bL``+)8H2lLuYoA1h#>7cL=QIY2I;BH8lZEk%=4rGJn8R>M{Pn#iaZJVd8mf|m z)k>s!qr$>IJV=WHf5Lk=95ln7#2hVB^Mm4_R@ef5wflu68U!;$45>ewSOug;uwHTc zmx=QnWm*`J3ys_a9B6`Cy8hkIq`lI}V=i_{6{qBck}`yQiLdbshn7P1G0z8x9)kYu z?)3OL*KYfgv++3pS^b6D4y6%IMa0=(EpqNt6 z`%*nz^3MB{fKovkP;5dYd|7*9qFoe=$fE)XE3@$1;$8Ibvx$0|T9(c2$?p-A zr#^A^EwBg&!2;53sFtR-;SJrQF*X+1aSS(|`lX^P!-3D|ZUXarbboxpIH8m_WNk#v8h9e-@t#eVH8@paqp!`{|P-ba^ch}7(bEtqwQLzFCSst)e_s77?!Fq z(?W#leDw&~wRMLJEVIAVxsu(TUsVKt2epiUUd+?Cfgnx=J0a zR@3glfM{L*l4`?lNpf*Q^Ot91BUx5f%H3k>qGxdj{a^J%9uRKZd5XD~Dy3*r!z%s> z;i3;g2l1?Gwh$dKsF^B9ZwXu=7dJ^+XZR??jmX5XwHYTpVwb8m~ox?+kqe zR!mwRCuhnD(%)aPxQbxbGaIO3ra7n1G+KD^LD6mu%i`|KaM4c3AT3aGGm05ONaLYr zVq&;eEp?-a`2h*51Qes*JmG$z95o@g+r<21exR~BvVTEK;eHS2qty+z*9ja4DLVTC zAfmOeT(jT$ggQq(w>cQ8ynwJGKxg{b<0L&{(fZ$0o7yp-hs{83;k`!P%fPO}zI(!w zU@CO>4FSq7abE3}J7~B=pX{4qZ~@>VY^r>WdLP8yO9C zc05&dNcGy6ylvnL2?+Z-OqzsHaF>#RNsUQf8bNOpLMQ-|6XmcZsYi$#XvgjMC5yWAe?H^f}ez(j00aJ$SjT8aPbk{2ZIhCGAoI5g1BK#^yGh*$!F zU{%EgiG+0B}R6Qn>j2cJhpUP{fH?y zwDbXV1b*GvS0%BIH_FE!sS+krqWXWhdZ#tbqHSq6ZQHh;m9}l$wkq+ZZQHI&+qP}n zwsx&K|AW2O1@wL}o;JE7+8dy8)kbpz{yfvmVa&-QF;bkd}&IId`p%_7yfE#kW zL%4hU)Ai$(V}uI|12i;N850-S|LUhH!2m=SX|xXDd9HE?nPr>Rj(IM zV?YhK6(Q#B%ditnXL9adG>B?vw13?v(|IOmU)PCwJEqkm0;+!>Kdm_`4h?{}6I5&Q z@;OSv*>0$pj~1T%5LZOe9tG-&22oEI5yO!g&gSfd%;dKSaF&fN1!>7`XPASbf9WTo znBCcg{xih0W^(mRb^E74zKdAwQ|eT^snz56gn=A;WI-q#=7^*L+IdVS+?K*SRxlXE zfq=Nuw_D{$0Qw6=o>_c#dnb89Q^g&6W-AH7BUOVLT(>es(P*mVH?v z3)Cn$0xaqX0epXwfvgb@=f8fLfmZuJaZ z+c##uk!s(F4K8o2a4_;gv_7EStqSZDhns5gy~8 zZY31+TavVmYL#qzLKV$AT6P0fEK*Zf44c|@HLzNd&-xFtww}*M%9c$Q8I^xqn@TpB z6acl|DbF$BfnuI*C+IscR~SS$1W&Y^k@~A5@XEm_4bVY5UT&JRL!|ntJ}NLCFPi4d zdVJ_9vzs(m_Kd}MjQY#!m62)J*Y}E14y}~$?SY(8V?O)fiEfF~NUES-^O{qpF@(g(8R}6d`GVO7+j%Aee6ms;IYjUd$iUAIJ zRV$chMGq1)%YQ1$J&b!g*PxXcSaYv}DSf-XC8V>}Rp`47P|Bz?ToXgPx>jbiHFTRs zJrPNK!L@-F~_SNar?<-8(b4w8qBi4K+@nLHJkL;`w((9E{^sZB!&qNK!|v5_I8Q&TpvFodA{p0#a( ze~b>!T;BuLvHI0uD?m5G@q1FGL5G4R=T4~y{Tr>gSqte#7cCd%1%0xM&C76*s4+`w z_oG4XV46ie|DbHW4~)c?ATO(+EP#vr%4II>IW_}@b6guWCt7=;NyBal1O~icB1HF@ zgi!0d2ihGqm4~ys^auQ=C*0O-awQQ|eQez+Sw?3O(UTmO&$*IvlLS>IpWm&hO$UZ; zvttvrSHMtD_K_)=?E0*;j)(CreeixLZM;A7__v~l^fr3t0oaxjF1ln6Jc0q)8@Eq(8>qtsw@;FIoJ3reg@Qo-a`ig_%Un1E2 z2!dv;7hoU1~y;4Or!1<-y~s5rX>D#2X;r@VQ9na_QCJC+x}7sa~{g6qm9&*j3RZ zKDW_BxvM)gzc?#ak$yn*8S@=V+~vqR3Z>rolTV~vc!tb==*)Uo=ZR2|ExoK~==8C!%Dea}Mya<3EvMr)IEXoKUSbV-x ze7^E?`QOkTzUNLuPDSky1n^{1unT4LdH3mnW(_Pg{T2od(=%khly)hIxGxfil+z}E zNE7F-!SJ>OLy^R|AT~L0dM`nCa&*#K+Ff_kFCs`BKPC^!V{7e}ZxqGK+O1~ixM!nM zj0`%2DkU>9Hyyy?{Jz(q;bS0j$rW<~!(_QHl-;E;7T@*|1xk{Rg8pJYR5g7)MopNjpEL<9x*aP%nU<6qP}fIbNjA&ztqGp+eceHJyGJ`PF9YtgJlDI8uD!QjL4aGu~kBpdmFut3{oRqBYFmj zK)`ds;p?WZ1!Qwnrm#@A5g9v-WI8$!%^U&3oe56f2Lu(FZKhzL!L*Tp(q%QR{(bij zUjM>lI&AR8N_|%rnCjLx=wtS*XNF>-z8OM(5C*_X*{WL)t6OocKG37G@^*haDNaP=krCyPT z41d+__Pgx?Dj8CXJwZ&$w&Z1YCKAutAQS+<(_x+IQ)Y^86V~Gnl-VlQ+CP?Ie8My^ z@G(itVgQ!1Z|~!Gay;i-NbDv#%8G@$J|N+uRhXO7YUGHlAr1um8(3u+E`C-OOB@y~ zMCY2}#lQWn7CfrZ_%=2A3yi~qt3PT%!5Ay>vd{Oy|7i9TXv#OqpB}U5?>R7#xEz3m zEaHlDTXy&#AG@}HOin~F9!3~v3M}REBQ&i#lVLw5uDRe;(o;_a!j=zDEouQ@nQkXP zb?=4%3o$!}F(*DAmFw{!tj9S295kmK&GeoI5_iE;-BSN|8^k5vt9M@y6kn!JZDFdT zA07b6?D>8QM`~C!_<3OdZ!had;~=1y77&4k)ZMZ+ZV~s!>^d^J1p8Ztoh^~66UmY0R+l^{0@v0&33c|G+6VG?*eL$$AKdQjlO3Vs3R||| z3^9)f9&?uJ=5!VMFbnyTJRF$E1$-6Mk|PWW#e_#!pL@pNlXY6BFFaiuNDcreySUdJ zOpFvD5z<#*Z}t&vq^9QESzpurdS{F3LTZzUa=lxZ?72EcqR$?9$Mt|P9$#}F1aOGt z2m6f&5kPB%SEbsqC4yKN5u?NI=tTZU9~*eqw`9mnA(^%jC-90k`vyLL&~wc6iXUH? zZ>@cYUH%&F?H{%Mon^viY6Jin4!i^*1p6pbwzC%6Y@yoniWXhB6axLAvU*712FuHi z!F5|FLFwaefKQH)j|cDtL3@8cV(RwOf++B@ zb@OvOBJlObSjMkMrw#PZ2ygVgARp0R26IU&?|W(~)M>pIBFy{sxCQ92!t4+7irH{D z`i7NIj2z&LIp?lxvFB$vrLr*Bg63n@cP|UI50tNjGe05_$eo0r1cz@cs)-mBIHN(| zk51E>oxFbjB}VUcY!TGQ$%iF;KFB#*d?e@>-7Ug$BJ%g=dUDRB&Yv}qH1N9dXb)p? zR!WHN7xo%lUqe6=q&L6~$V4p|(Q&7oB3Gw!W`T~6V41?MUOk-%S(-667mhGe~FQ^bRB2hE-Q z6jD5*ML>4_jz38?u-#DRfJkv`qQwp5MBM`--akngD+Yt&MGinb`)pxgPtkfpSl+9} zdx5UoA(R~qa}ygsuH;BHiau>YqKSu%SH)L5^0WNdle-4cOZqAF3Q!+!U!3K6%WO}! zpI$Qm<8>H5T3%i7Yy1})bdktNEp$e|-46C)nU0nw!iZyq#a#h5z$q%iO=&ZhJu~i8 zir&YS=xQqf!&w&-i$NgTOLUXsafRU&xm|Mk0>BS&;kw4IG!i(VbFw~VQbh#ddhUFY z_r@RaJqKIM0QV9m(?uSd>ldePf75H|F2l^Kgf+VJ~Kl2mgjOXYViL|aE(G$ zF7t2j&_A^S?>}loTC^DW#d=k&rTl5gzUH~iIS7K17Z6^8e<0-CP0jxedntk7YMrB zlrp7?TWv~|BmiP|#()!O;)!AcHq~^ZL^e%A`DaqKc5i6>d+;VAdn2!{c4wS7;AECV z$Y7EA=IT3O1wMkY3GHsx#GzZl3lIn*v#;N$Qn< z5IuHHdYB~m4RJx~$S27nL2)T~`7;TMl&_@QZ$lDXCxJHIB)LQ^m%O=^xDBha^DD_NF9k4bC?MDB`T2rUWL4 zs~@^_hbO(AF93)A5z^n^jNdk?8zY=TU;E&6$yH|BJx_r242UAa@`xol3=^GQ-Nt&m zAc-BNqzPx>f!GY$qT*-@jg`27vTwd%`&L(zhiTu3$Y17`XV(m>gaNPIep_ zR)p0aX@D9NlTU3vemsOdE;sL5Z~H{ksM%m4`ivJC$EfOXtFe`OEs2TCp+-9udLCu} zqeYLumnn}JD6I%xLLL|of0i74N32%r!M{*y-=H4Qj@t{wh7AeQ_a}Tu*$t#y6xX*A zkF*n0X^A>b|96W#)$rf}bPZaAs#iFS#JJT%LV#_q+1R8j{Wxz4q+bEIl>70)6i>Q@rKtS{z3RHt}xv73XK9r~0tfw=?f1=MAWbuuidaga9dbqf#CNEKw{ z-2uS&5ErCcffr5MVh@>eWlisLoFDLc!bugXFvbgyhld}Oi#R?hRKl!C3>zXAkNZ<0 zoaT>DX&#tAa5W6mGkknXNR*68B!_P(ohKM?FYljkus7k`{GeBKri~awG*Ncm5c-VS zL7cq^t4eRTCF($Mkt??c?&{2Ami_FF9RR+{Q%Uskr*^t{L{ZpcbYBpKecA-N;nVO4 z8ZgfA9$%c$Jjcme2lK)U>VfIg4Ce2UY1$nKF~V=$7#a{~A&#R)I7hxEZwp{XFOb{( zrG(+2Cd1QYF{%S3AW#LL?8jC)_r>B5|3}bHgIMVTY9Ir_3Cw)>*XTu1?pU2KcK|JR z)vC6mCM%3RWVMhP74;%9@Pp|oZyz;QrDcUj&6&^v^C`I;lLRH95m0Y|q2ELFrySuK zBpUlK+E)}lc#DVZvbpn~i}<<&>bBbOpx!5>PNI8+gtlmWk3maElor>C0Gp`QOgBoOHng_(c?yA9F92%2W}>o zLQ%puigafvc(fK0SZszSo1yD3MwO_M&(OnazJ>V?yUGwN{C<=|l^I|x{RaHtWxeu9 zEZeJOu&v*64*hg;&t)23XPs4Ag8F#o>%ct2Jy`)f=Hd=n;`v5uwFh1>fq=&@)>v)f z*C;NV^t!O!T&=KLQluVI62jHXh4em_nei9^3l8z36~7O6zh>}Ot`hILljpE_O!60% zl1aL!XndEu9EM)wC!y%V`A0}@y3I@GG1k-({UC!WI=9g*y(!pf4|`!`xI#V&26(2T zY?Xx2jU!@w2x^nTOoOO65a19))+aT5muWKmsdN?B>!5c1ttjLnvRx$7JO#8Bh|Rb& zsss+s01S==d-y<;1ar7?c+4RhPHT@bELlmd(I`7E7a=rDxsL}(fs%+^4T_+_wog9y zTW*76^6wE99I~1{P(2L}#Aczg5A~@RaujckSI`~I;`6$TR^Xh|JplJRZyBJ|1kr3O zEej*KJ@6dPmW2UEtu;MxsZzd#gjp>Am2`KCyGHC+sOMwNUOOYKv{+^i4t!d)`6@nk z2ih+lf&8gD>a!EHW8DrC`=^5QJO(hk&y8&LhdOJEpy>+X=@lW$`j=jP8O>zUM^%+H z&t#}w%vca12lHV1DIi@XeF6)=iG!r}2cL>b@aes2`L){-!lw|*l0wEGZR0q37^g#Y zB7_PEBf7LMX}2{VSZFHsB%g#q=1G~|Y_!%i;mCWiIUc{arPoR-`&8CwbajU`bC?Le zGL9OLXdy6KO^_r%au=2zcM?o>F&(O~r5Z-L_0wG&{#|in5>P>z7QY4KAlNgP2>2P$ z`rh!+IAMK;C4rCR2q&WQRxtgy3>b%ruIxtQ2SDe%f|^MEMqvMbT?n=|SZsSb5l&u>!1$d~w~^5`Zv0B@Pt?UB?hm$aTY zFCbK{Icr`9EcSs~L)n%a7WldvaB+BH@>IGS)GgxNd5a}@$UBC z$OWzkY$EVbjHdc@mIwga1O4Z?e`E*cFHw!lCog17i~pDyBA`f1N$0Jq|C{f;Sr~=^ z4jVwqEIH(yDu(2sHmipw;azod#MsG#p2ukzNhI^<2axG;>G64@@J5(>APsM^*mN{b zzeE(S=0$=wvE_?GpLu8*-!buLEPsFzOEK1X<{nmPd#*+zZo>Ps$7D zNm2cE z4dQ7`qw&DSEfNb+2EHQDIIXr-$Nam`pW?0BrDb}TQ4;v^b{0`JFgd`mRXs(T@tUA@ zAVOac@>eFgjuLPVcVqKYE)Bjt`_34@g}jljM*8fpa|;m)VUr{!tW_Ww=t>HAd4trc zIsidMi2vVKpRZ4_yXL4RJNH6hd3kjRJY7S*AE16Kx&RNzs}(K1Am=0pl?>;7s#Gnz zF6$%<+yu-Ill5UqFpL!_WEDEG7}N?H7ij^{xpAR*f8-;KEdi|}4sf6lRQNqxm-w1| z`l6}}N!-|Z;0y>&cudSp#ZQEn%hVR#ZNO|Qe0XwYg@`W*DvE4pDL7qrhVou<8-~OP zzOV$sg}$Xv$Q*L8jk3pnTN4!$uv)ZW);S40H1Oq8f#?&CqyK7r(jXodflphWl^QDd z-nKWf2}y{zl-%cZEMetA-Sh6Rjd9n;WymU;$UT>bz48MbCB#f4hfx`P^R{O_ZUAaN zzLaCCduX)7tI4W<|5s1^o;7OSaA=d?*!Jd26TnA!i*aK1Lp3bRt#t2 z-2f{H`2Zo4nC7j;OYSHAT!Wl01ZpV{)PRpQi49+m*UgI|Z<-FfmzY#`n`OW2Fhe3l z_NHWcWUQvFImh3 z5*OT=ErW^dS77`)?I$u1UkUc&keV_+id_|7qtz|CQi^q?i|LAAXh|RUIEOE@JLkqF zJs+qJGh;SPv6Co^o@uuS1QG)Fmm;64C6em+U)yO{`|JIS5>7=Qc7*kOxuWIyi8py)giki;dS;O<#^&Sw)Qvva(G>=7TYwLdb%{H-vEK80p<>!+3D{ffGR#e!{NA^TP4 zkXCVN=|eDg3%8zqa5y?GCqNWuh<&F)EJK{itb;u)MOm8Be687!Y z>etb<&SyIQl>Kc%8mh_xR}STxPbxx-kLdh4AS`qBqflq7PXW~iZ$5tdJ&Sqt@8qgrjJ55*^$K0dw#^WI3q1YRwgbx^tGtsSG{ zxdZ>X?|L0yY}=K~7$91hP;)yMW(;qixwpp+ zBQ2u*fmd6&Ey9bq|Gh~|BILYQI>aKV8)NLrzj*+-6+Ncjl{hWj&=sC;_B=8Sh@G^X z2pz{+I`%ws@{hn7j=o?XgfDTPTad%}3FKwTWJ*=7STaQBfE*UeBp0cQZYL!bFLpzZ z2UCGm5VGg^Z$LxFb3(yOH$R*+R+w(fy@3LzO4o)r&au(&F4gEC@MPlCOPJgsCu1^D z+r~UtT?lo&QKf3G)l1K$oQiIhX~0vgQ%(S=d7-})xy3dINIKg> zb=jm|^7`i5msqXpsc#6p{e7+?!=lG*{`wx;b8?8tFxRJ%ioMHvU};R4m(`63C~wEd zaq_{GQ34NVtc?YChJP?cn`Yz;t}5k$ojo;WTEQN_SzV3b{$<$qj;UvJwIRcOW@FvwhIn!B`i1GO zfo{u~xrci=05)HW|Vj$Dcw8LwP1I_N6FANDj3r?2*lmH9m+4Y{%6qX&?zp8R%&8?E`|OXi#6#u;$VR z=Qftk^FQ(XU-%Z9E-BPNh}AGhFEN%+zyL9OwT&A0_%+k|)KeL8p+7r1mEKOFC#yz$ zp3dd%5%ZbJZ9?dsVVX|_f0BR2`O_LUy7Cr9auo29XzNWe*$xoEgIR94r=qHuX=R0E zpi#qHy7bG1^W@@hTp*4t9BgxGfCAlcU{yLREOc_Ud2x5WZM!h9^LPi_z}Y5Cu>$Js z+D_hKlJpmu#GRhYJ6;dBAql6MJS_3N9OJkoU$O(ZpsSwx>b~{cbAGuqH6NVd6ppzI zTe#{$!E?}JdVeB}9Ye~#B)q7o*XoA5yW^{@2HDOa7S_WxCN80UL$G1(MJp#WsvM=Ww{RZI-?8S9cz3ui8&uLSgxmH&99)e=jyRB!UT*o0= zT#L8KF>J;(hh)<^K>cPLk9~t z(K|}<%$y=HIEV-!c8!J4(*@{vFAJP5h7K-FyLwiWG)>$qaO`K!{=Het(1EToy=%{o z19>))lDMI=IckO%tc^smX4Wq=I63J0B z@n+Z#*D&bflb%ErzKe?m-@@Xk;H1xxQ`!yk%Zd}u$(sqJwgYToKo?-ewY1|nFryF1 zP5+n~y*nGr`}wyB=C+PPu5g8#Vdg8kp189^6f@p`VL8LeV_H73o8*wX?IEE+iQ;5+ z#@J&vPfC@dv@nZ+N46F6qK~xY)iE8ZNZ9 zUep!VH|MXX$2s-R2^u!ExkJ(Mfn(M^2U6n0(hYXLNs_nGVz{WpjkDUOey0YlRUc7q zk%>WMdYAngE#C^;e@(Yf4poKV9;$Lm_$bO>A!JXsG1O(nhz4BZ(|Gw^o!S95MNa(F zu2MlEnS@XUnn;qyG9ZKzx5zjS`!wH~jADAOTN#W_A}$wQR*Z1ydi;*Ges<*m==;FNbDseSS5yqV8FWH!C9G4=}PomFUb-V<9w5 z`CSW&EFKb(^aB(wtYMA<`8I+L61o-vu*EO`qO&k(pIY7-=+3?9dfTc>Aj%Od&Vig1 z4pqnN0aZmEbLU!EwTmmU392c=&kR*@eO(&NJMAMj-BeOGk8Cr4_x7c{K>XWWR={rt z@&2M`4y5tM4%iis%AFYpDhSp9uW*mOWF%L4{nQD~Jf#*fQBLU$SBy@N`vnlJIR3uk z?`~aS+6BQh+epbEDbzoC&CDWUwbkOT+7EYn0dAz`A`WVz%|yauqiqT-{jQ8D>+4t- zXn1WJn}Rrs5VfwpDaAG0?cH2)dbi5mL(|LvHZ>0dmcf%pH{+RPdKfEb(vEgA;Y|M7 z5&6CT;5E{vzLn^bKez;iR4)al)S2@_@IkJ?d1Lz~&d@mt<*bj0^(UnEppC!Z^3ba+ zU^Xf2G*-IcJqmF0);>qFzBk?8A{ff^6Cf^c6EWEDm+Bo&R_d_#Z|2mC8@xhDXNP_R zT(VB3ulcV35;Jh#6Ek1$j~h!>aFkq5o?C=6ad};BD^i~ioadbHcGAJWLzGZU#CT&;(Bo-?jV?NMt0?Qw3H zb;PG!d?pxe4^R3!;)@#N%w*+%A!FXF^TKH_@7w@gAyj<=C$PRy>x)rvG%;@dp(J|l z=9F&!U8BH9PQLwW_wtM`q?=w8IQG7-aRsrpXVa)$RP0f~6=S{DLA>+FkPheJ}C=*;_;!x&kN7ghqoOko0oiCCT#9H|B?|QhS8Mmk`?$Vf)PazguiBMN%0|5rBZ^ z=>PfPTJTrE;t~D}tgkx)>%)ToAK<*%qh7N4KY1&l1@H~#0fBo!znzL_pI1hVCvgDy z-?OzqgM-r(|91jz3h-Bp|4e`)*Rap=uZ`y_O&}JW5YXW~NCbEJ24k))VRGrsuBvf@ z%YwJOcF5!`C_fD#G@>k+m!7zm);N*qRNj}JTZfQ^6u-dyIwmNeNK|4yGrPu@VK0pB zGodLLnSbE6co&@MWfSu)ql;D)^omU0I;`9Vmajw%F={py!p#%UESDrK3R7ND`+ zEw{c31;j(x=z!*A7MUpJzQ;#3<@hj5N9!@*O3X(N0}@vfPLN?CN8o&&m-cE4$R*Ya{;Xd?Pih_QN(ixp=^5*XK`+=_ zh??8nbIZ(n_|#|9D<+-^>Ba=pqV+g-$i&#F$_we6Q$+T2={3VkdoEXSNzsE$L+V#` z3DTxMow~(bF`kHFn2l@J=-#}?aLVTXc4BPO0R8$u^hhA{CQ5;SpR^bxtsoAZ1`who zCj-ii)HPFsH7^6p(TL6n-lvQXs|LmgMM8Wj-%_wfHd(s-?cH!)ePvY8W_rKv)9Ho1 zyK(U~@;axf{qAV0Vtc^zHOMthv4HDin6#(ua)b|OI7?O|Cth86M?;A@HH&-(oqw%e6l)2H208mIUFwz%Pi9Z09z9)Ef`=jnLA|YHET+ZSfm0`K(1zmq*hM=B7Npts72i==2Y5LTD~~U0Y`p3?K#4~AvGf=2SC+;wnm|F{!L#7 zva2A-Bt)@&lzY^MRpEj^kC3zeK#mFTRv_fP@lEh-{1xB^^);l}VU!gjpbNZ!OzAzS z7S>pu?GGU2F1Pr@KmI%b{Q+Y6q-B6Mj1FW+1RHsQhJu(SXBlo{o3^4_Tgc zmpeMEjwPGdKQgXP-`}9xlG;V&36Tw^iL$(&QZ|M#<}L6{a-3+U5e)xc6qq`wrjud- zhkX5>tfxXC!{N_?GBHplx~OtoDpDxR+}q8`O@6QmRZJN~_N~cRvsrK+?n=L-{V*U1 zkuL~6V0nAACVMukt5M^SRMB3MH3!UsrCwS6=g-*-%d5^aS?Rqr;zOa}JM3I=vp!Z! z5W9qS`S+h`wQVHsmMMO&pCjxvWUGS#_6W3esHV9|UD^o*$#p=9e8&PTu`*Y-=fwf= z%K4B1K@wWA=7e*RthX$kqy2`dw@`W*hC@sH3j=P~L^}OAS;dk3m@nEOXAYmgQ{y5e z7Y3hd84+2uXEg(q&7C{LPc58oG`jLn_1(r>CxtE>8ptQgMw`HcD5@%#o&75S6$& zUsz~6-O%sSexM|C6tu&=n}I0DG=ceI&?38;cKsPg46^h*sZA;4YuEkvT8Oa{!Ry*` zk{jOASs1OEdY7xi_ph7S&>up4SDzacCzLMWrJ8Q$0JH*XmBXIVh3arabbxb6u)EUL z@>GaxW=mhdxj!eP#}Jg%m1pbkEDs})e3e_6`u%lEE5(cwcMMte=nQPR2Loj_s-abR zh~bKzixILn$p2mC#3nNyI*>p>uUJ4pr2nmQBU2+sS2O2-h2ErT_dlW3-`560WLOlK zk@3wNn4kpl?boo&7()AnxPgNCRW7ok80?9W2h|@p9i-&vOHxSrAc3QcTX&u_TLYwV zYe#PzJ@VTTXyzu zK6Dui_%Jbd6)o*gU;a1?!s7-30Y5Lo`UYFV`V3hW&4j0T-(#+bxNLidd#dgrx&#{K zTLs|t({M^&&U{z1`vGGozvE$uMRh=9bIN3NV#SBjSrR^5zekYKa$r&#TX~ z*|!<8!Q_r#XZj4r-O;}7-IKFl#2@0Vj1{lJSDd~LYF$@5-{A$x3k!7YA0tw13daP~vnhzFOpG;x}&v>40A?BMWGw z{13M?GnQWwrELUzCd|);lc7*AAJN~u#t|`bzYkl=T>L{_D;nr@IN&f9tnRWISU5RrrT5X7 zZe*1b@h3+k-r7;b&WsR}t=8(ue8bP>jB1@QDH2@b<|JMIU=FF+CKfPR`CPowm!bOa zs_mXn@#oBcjGJ|XA#5r;i(`90yju1`!@*qFqG!0CH~Ed6QSNuJ>I5#h&&9$Qz68>F zjkv84M!5siNfCaH_Xi};Vrb1R;-7ou?L}rV;tnP;+;_;9 zGyVGr2xV8hvTExpBLRv`F#_rUzPGDZN7j5v_sX&HY&)C!1c>_D-wX&k!&z91?KIWk zrXUfUfvF}=MNP&zZ&Zkp--^6j>EqeogD9fwX2D)eOut_qH5u?TLNDXk6S(;#fJ{Zc zq(VXRscno-xuopN3R5dJk)1RlT-;=Pq_xV9vaZM0T%vr5pJ!$+1bC1|68VLErXj z##IkRs|_{bi~z8k3RSCX`n#r?Fqc`&E#%GN<`u3DukzIu`$qix!>Z{!G}SVyYqIGm z*Y1s;{V*4}yctGLq&vHa+c^r}K8h!yG}^LgR0+si6m2s|1Cxa-*(>f3Jml+X5q?F`OW`P!~{LrD?33p7A4MR=qaMG-q(%a+UxeY7-L z(*q}>pwPY{4l}0qa^6?b_XfTC%Qh$MLzhZo=ZU-BPt~0y=A<2DBbnOU_g!Zv&*T|? zwYisW?UsjV_Ka=x7}ihahKNH#y}nm<17rpwdX?&WSA(q%Y&1JA{a-+lu>5ndBXvI; z$8F4Ig&jZ>($SuzG~9O(GXVMz)2yg%dE3D$o;U$B(JQ2EGSu=0hIs!ptt(eKPyF1& z>+tU7Fn(XY#Tc<}qGkYx;6mo5TbttJ*}UVj|gWdI~_i_Uw5eSX>fxe|WEPt^&PO9N~Oj zL!u7`od2?9I)dlcJ~`Mt{?3Is;?w(MNy{vQfn){YgzPN!J~Q(ss0Zp78n&$?#Cmw9 z+d9Axt^~hY{H}8gVxB%shJLzo13}}avF?=}an)MXsRek&seOy^O^}DB5B$^^b`11( z*v6Uy!d@L@Lf+Fom&qxKILN@sWD&Xx?V>zlxwKj)Je>X3>#1uzeUu#AZ{7jEr3_qH+I=H?oYp=-i%uv zkQG4LLGXAm7ak>2fM$TZPN#sVU`$B@FB=CHccr{QKft>D?Z{Mi-Ov7Ro0SFLEkK!e zhRyP$Dc*$p6yYF(OaD>88bO(q+mXRb&4ME(Up`?Qe1xS?wPdC2U|Y6#bC$o>!U0gc z@Bm<>^nLKH(4X@rw%8fA4NpyY>V!!DiD~gLcWIJBfs~JZq_@!%4u2G7tL*V97o zy66x|nV*RK0sjB*$;fIN1Onvw8~=`;`TsU%Vj!Tj09!)B|H&ioQj>GoV1nzq&_KE( z2SzEPt&IwRw_LD?U1t2tWlIGiQFkW##}+$rbLrxJM?$I&wM7V#YcdQgadc#)%;Ffy z-eI~sP5?RrRgGPnPQyJkjwAG-$m3>DHwulRm7~%$?2-x0ARJ95=)fwp1ieiVe?V$z z0X;UZh;$eMfPk=S8xr46uNY;JuKlYmL%5nPyf9I-b7#JdmwvdK9H%mubS;X z8atOX+*#MKz!fw@vPzMBMeZ=_P#Zm-%*)OuMryw{rMY)PS?ihv9+nw+T1_`hxBSu1 zcTrX6W8ngM(r+`HGKujdsb=BN3~B3MdIQPM!xUIkfb8K{csej20qp@s6;-X6%}R8g zsfF8$^akpA;HcSYs8F?LaW}X%8 zv%Yw@@KB_VX#Phig4Vyx2)CrYBhr>^%vQD)nj->>A&B2cb-WdPqo!1I`_%|9Kr$sP zz&4P=05K2`;k*Qu&efUO_S0S6-uTxgDuuIHk5=kg#*g+2|y z_5iKJQejOJOGSv>lAmCnVhIi9MlI_!y}B+cU_>Q_J;zNyV-%k7B{!8#+tQHFk>1lv zqET2=u``Ae9Hgv}S#ZcUk+_Abe|9;J5Lx@@9*Q>*@5b zJ47-oKWS+7J|Fp)?AHFEJs$#aDGS=`cu?wg-SL-Q4E7h7aD&is^CLy4xP;Olu9!QSg;CDaou%(Q&7Y}70d~J%&Pef zbA%Dbm`jco{%wzDIu8@?*0U!)2PoTo9n#hlomYwu_cIumcb%>79!raTozL(8^&6;Z z^n(|6R-gg_%}M|PQ2?bi)xjX9#n}?Vr{;pg0hay+By5N$?!3_+=Q~Hp3Wle4t<%*c zXVXu#RHopk6<-!L!oY%sl93?w?h!|1O#ybUbFnAk^b2#Ubm{-#CG5&7DD)Ui0(X2r zUe_$VC{51H+onle$axLqjdStjc75XW%oO?Sy?Z546a~!%X!>A-^HRS)k{n85SCQn- z0M@c55jlK*UrvdWjyQ>P;!bmzWem;Tr@!5CPZc>g&}*h1lOwLtOlhe(K0_&JP5;#F zi)nc_x}=opG;98?tRH_f?kl9fHu6g>E-?_dQjQkC*V-N7h5pj@ja_)+mR>$LnO^E( zS$76bcle@uu4IXX1?LL7xSM5sEBp-81gKuK02Oujyls>{46PB)%zV4v-2wF6Q-_%m zk&x4TZ&`J@P&VqR9xxF7l`_5mMXs^bfOJaxZf6+W!sZ(&i<&+5?uZGk0TT)ShOkmT zwxZ)N|7zWXBW%6Q1iklv+hE)508GN{wQzHm!>{Ryt_H=nTi^Rlc%6`FHP#~3yMuq{OS*u7y zaG08q`OTqEhXR0Q;lPgZYi;)@=Xwcr%?e^atK`-% zXc5>-{|(r}Y#au`xKUX>0)U>?7%3vYkIqu74ac9`$Fb8BrhjU#ljD5RNlY`PDML z?n|}Jf~ozMVC7;(^REMFWpQh5VxU{B%U^uwt916DM9~V*EWYHLW&j@`;R{@8p<1Jl ziFC$E9ulAjQhzMM8FPl#dW~{B)=z~h52#CkBug`%DF5Omm`HN*n>*fM$ON5la8ITv z5h$TiMr}#CgMhAH;U^duWD?xE<}2;=^;>KwJ_Au>O6ez0BZ5IM(jpnK)OcBF*&Mmt zJ*naHn@TS$T5#QiF978xy@zhTmF5lv(@@O0moIifg5bBO_pdjxy;{s@`6VBulbmr* z0(+)6RTChY^OvVeif2zNTs0pMog7jLMt&e-aXmO8DG#YLfjaV2)lrl&2pGiKR%mgWEXQpd8PUZRY+~1`&6VV?bSLPAw#?5XwFx7&);^ zw@YW*C zI9K9Vfa{Az6yOIEjUPiW7lqDfPpnhd%c`4yJ$d7m$^g7a!0+ijA4xasdzz=&ok!=# z>b$0n)xzZ04ml6E(}qp2;o$EjZ^CFT8?9tn4Wu4Bhye04)pAZclBjifxKwoV(Zh#{ z@l6F7q@62P85PAnt45Tvuo=2-buq4~H&9Dh@y?mQ(|}4dJn$L%@uDz3!K1SU`=6vJ zY9OsO-s>a&)~lk^eG& ze7PC634Khmh9CmiN!#=whvWQx1`o4Vc)gPeu^!58bYf&24hIY4SerJ}pjVk=SsJQM z+wiTbEubrBtAf<7*R0#-00`P&rWv|77^twKzvXFz$A*#8W8izxF}yhxy=%%W^rUf7_aNR@;{V6hIksmOb=f+$&5CVT#kOtRcJjowZ95g)M#Z*m zvva!p!|C_K{sa43YwvlDIqoqk`Q)BRGm5x2ME^*+l(L%&!bL@n*_T5j`MJzgMuz#w z1NW=n9GFZcK1GvKF(kzCJt8R?E&wPV|CVh5I$-psL(B4tKF?Odop9IxhkK-GQIrl0aj(fk3aP)?G^d z3*dZoRwHsl9g3Q>;%4QW)Rzq>)_2H7=^VLMD0qcVIpKX{(6H{ z`;eM7#5Gi#mK(H5gUBNBqGCckyDw_B-i)36iuxla95<~Z9ZWt(I1Ac!b z9x)HtkKbENrSvGssOC)XMMw*l9{T*PM-ZO~JKOhvP9``Zd2V%aHOim?PqZ;{?x{z6B*^Cz!i1lZIM|8=3!cL?eeR`wL(Z|EKrA#E8Njd1A%5d6R{yt@8%{E#rtlCM-iZYHEMZ#5Zp3r>@?#JPN7)0RJ)kDQlR(bM9wY22 ziSW_g^;p{4s8uG3zprNxVzit1Aqc1Hu-L8}@cfyz=|@ENBOdRlE~Z5E5i0tYbHhS- z#wy?y#=WG745pp|78Vmy=>Z@j&p^!ln{QkNu+}l2tiu~r z#>T`k^dtsQOiY2OWuesYMzGS+BH-(W&XVdJy*&r=1#?xq7^{ZAM^9mF1>@Geamlm? z8KD^(*UNKh_soal{{i_dx$cXrV=MM1e?Bg?>@k%v%Q^T0x^#w-lQ@yD3OtJgP$v+| zn?khLAc|4d+N}7ow<~R`wnUjig$2j8abJp%>5~+%W|rSAIiP4}A$7P%NdV)-7s(u> zy++LGds3y#h83GWoYg=ZG8sqemp>_m5Iu20R%fkX0xNO5Q_{$Rtxj_1hcRcT)xa?xhC4YT#Dk)N* zxr{!3hl;oC$MHr~a3B5AunVGm%Tf4?l%L};-=43(-K4wbrFFBtE0-J<13C|Vaf0lJ z)T+@TO>zHN&TGbhxIM$9&od%hjGm?H57kR0CL^W~PPd>rodg2e4%6F@B1 z<1l3gGy5ITOnrY6MvM?D7>kLFw>fY)>>@*$olib^@xgdJC=+lr)+j;BP3$4L@+a6? zVDc016l^vpIggissu_B&LJvqsxK^qN8A&vppBc4YCy+GIAkzfmWT|W)sTP<}T!0

dIF8*FnUbd0S#cy5H)2HTWG_fy6K@XIvE2B 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 z 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�qBhn#x7w!D8Oo#QUxz^EazuzYb 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≠-_?L7^j)9NEj`89gE-;3w(Lo7un`SyBeyDxv z8fGuUji7tt`Eq5!>A*oQ3?-k*}UwXvrBVJYY%`XE@3c#%58w*#7)njb`y!lw8@F_k~JMai$p_ ze0b>C29tF77()yn8n5ia5Jw1bphI$b8U9%hf4iPX7yBe5c%R>~cmQ8abMCv@#EA4T zy85gx=Q>WP>B96l+Kugazkom$d9UC$Yp75T;a=N2mT(oCm8_War&-4zB=-K=l*?4# zlT`XbV$XaeWr%|e@yfG=h+NT$B{;t^a`RYe4fviAYZj7eJw$M3lVug;b}^}ZaO(P| zf5gJlSIwTkn3Q=r0+oI1r;9?_2&+D<<3k{gvnvzM35x=CUGBZ7l(ZQ2o>xDa0y@m+ znyVj&;Xy{7XalNyoE+Y$z99VTkK{qf2bEK#i4y@I0$|mb#D=p2h=AKc}ZCw*YBsK zXEfcG(btNaKubSwZFn!pBO9|TgTkbk6yW9XI_c3e_^hb5VwIBwUtcAx4=6&s5BBTH z)dQ+tPK~%l%K9xK75fPBM$fnoomM0hpX9s0L3gZ^A!^n$w3A|V6sYSjC#Ckk^gs_`$Z(OZS2ob1cNa2&3GUo3ITDHf*=%v_E~`l{!Iz>7=OzyQ({+7 z*_{D%_i?7`m9$^CN&tVS!*IkRr>RF$K#=E43e zNA0zKaJs&Lm?*#181g|e@<=j)=&xf;ITPY z5h9an52#IXLT>%pHMvI_KzvSRS_vt&2rw(5N{Ys+o3s8=o8+Ha@1pTOR|gm-;DvKq zVRN5W*-h$dkLz^i;qXGuJF=|O_Y=#0c=qj$2&$nyV zqQ8^K_h=!>mt_tIB!7}OtvcH!7eH{2N4GsC=Fxy@2hG62iO%qsqBX~fA%`H23&M;!(Yv2Gshmb;bmd?JBtc@VDEm`o8>rq=FHnJ z1>ky;AYW|glQ5OE3sa#gr|yB_{Ei2!+YG4HW{W=^tp2*iAAju-_|X=Bv_oLu;?D~X zaK7)oG@c`K#%*230TEy@h@ttv%)M8$qh_})e9y1&J>S{nrGK&IjaHLgWhC|HN5NC|i)q7W zY|;5Q^%mpDOTJDCMx*N6dIRF!f-rjR`lqx79@qLIbARAju6K|iDo@?%!PM z^-d}ZEv!p+34F|`Ww)Gx>b1%sW`{cvc++fP%amLWRGOs5MTwI(Mw$i==e4b|8UimB zxJd7F>whxEeP_dp{Ja7&gCRm;^E;o}q8c~VYaICf;!6X~$u{yxdYbUr=vJ<>P|}8R zo8j#>kT5C&b zZ_J=}_PLBZqXe8jlsl1RN)pa|WoWBRJ4F^gH-C?$m>)@1;De}G?mfG*V1MQ_fu~+| zL&W2b6Hbr91Fq_IZfQL?t^*F5bhHx4@;zf#)!pQ~1JKT1d95Wc?|jmc9!Iggt7T;q`oHwWDQ)NJ-!q61+0 zKwi4%RMF48Y&!p1aN-?Z#B{ypy**!-nuDIFdV3T3DiiNFg54Iztx@>-2sBy`jAE9=x!3FnFo#958rAKxm|U_&(OF5SK?H@OMf__ z-)uZ3bRAev=efC(lgqZ;gHiu}i8p$3O|gBHN)d3vBI}2)%>z63MmhFVQxqlwGrjJid2JkgS4RCt7#$l5` zLhgk_N-!~VDaBx2+oTST1@0(T|9?E$4lbZF%s($@+;UItTdXt#Ai6Enlxn*QJjdO| znryont!|6*P>mhvpKv**)6FOp-)c&+K?Y$BuaTqJqL`W5JqMsC;a}pD@9p|&bmf0lghGCr&0zXSRFEG=UQh>#Pk%ox5+rdH#T-{i z{)!?;1S0>$A@KOvr}!Ys9seCdrpPBSfP89CsLxQ^edFIwq!s1^&)t6?|B_8&>GsZA zg_-<-%0_-B{QA@M!%m}qL{GNxyNnZG=_e0voyeWrokrI>&wgt(h@5@9C%PqIbqtbp z*0;VXX@C7ZI<+D}9Dj5^{KmVBZwLIgp#4SB8Tg~5vsrV3>3o@HUlIJq1jnrdq!)c9--UHs<7Yab$i{nyOzz#K!SNo zB!45a$7^OwQSbH#KE9um`F6l>3w~3027WIh&iJFe<;U>`|9{&D$RrBUiFm(oQkFAH+bT6L+0iJ!Woncjm0~|b0VWIoDZc=Ju?VD58WohB z9p*^EY2LBc237E&*+dqP*GQpG2qmO&#m!(Y6$n?~YD~G>RbI^jfJlf5QTHC@FVTrE zA)KM01evjiKYuTazQY#qq?&GcQG=7Y*jIWBrNVh;PmBV^DKKupLS=l@^(b-*v*M2S zi)E>3jD12!Z0Dl=!_g6uMg0fJ1HeBcez+y10St0XLSuZj+J@i z<#NE6vJUpIM;?fr4&wRNg|b=PG3lYP-8-x(^{pDoR8~Oh)j<*Cn55Dm0p&5?V`v)H z5vSgT?0-}@INVm({-QgPQ-Z-soU8ESVr7Ah>PcG_0x0d>&A+`} z3>ES;yYx?poiKO*+-WXqEN9)PQ<~R!7*$fhND)1@JXgN+A+|j7fWBiddbwO(>;99OKgdD&TR5G=7GXVa4H+ zvgBBagpUU4-NB(>#u9v-;_l;i=aqbh9)DzirR5WU1{W~d@q0fQiGPJZ(d^w35}%0) z`#<#4tC&-VbH+Y#`~CVE{5U1?=>$C5!}Fs*2F4GO?kqq2c6t;R{H=pbk9>SP{x-7x zI@~i3e0gxAc{6xHgY`6N+53hpf1a}Z8QJdsAd2XU>mQ=qgMNbNE%fit3>8Z|xPRB7 zz54^~{)l~p1R|ffCsZn@DA-!kML4$cfvXc?IE>*0^ZBW*E1m*W|JSSjpz2V4W6Xnt!13;Xa*M zL`%4QLic1NZ@!|!M0mlUS(5ZE@g}ZM1ay4H&_GZT{D}mxLS&MfZwF3=oq#9abGBXZ z&bi~fc;d5jdg~DAy?FiOq;Kr$L9(RE-;@49#wQ|62!OL6MhkQ6RU03fH>QY-W^6gx zHZgZL9G)c9pL=vuSYxg0Tz{w{V<$_3M1;;pZMTyF1oFUc!iqO-{ro=n{Lf~90-vb% zce!VCk;Yb)wLp7cz1{NbO#oFHLB7}RCx8+7mV2IqE6y^n$bTUy8S7(Cu*2hqu{sQn z-E`XhquE1b=XWU~<1#`!s)FCyXgw5EI5A%#3x}a}D2NM$-jZ#0*nf)vU6OX>R%$v~ zZ*=HYbHf!z3{?%DmO>rris&vf&3GGFF&QX6})DWaIy zhQDq0odhAnP+m)$3QtE`LHkikQPDm&-l3H z*W%}uYKN!#G=O6H;jfb7m^pXMsGyJ869{~;xnt%vdGOHPGamb=N3wxI|H3mSpTT*> zCnKE2$3n;8i+}VulKd#!7347)?-)?=alqJz`WEuXnhOxuT?`vl8 z!5-niS3`ym&q)2|8BHZi%pF0t0)5*IXd8n{HON1y+`(T|?n>d$UU6qV<+q%$?CtI) zxVM>1m!9l3u}nO7#@ENb_dc8dv{silEk-9P=6jtY)<%Z6O7K6rgz{gw@f|) z9w%>Bq%XP7Bc17V9-N`f7H^-P{B1uV6yD1EX{#4lR2RxOaB*DJugcx-v9NLE4TNyL z2s&Zx&{bDt_6K%sI>H}VaHU6pw>$UUjl#*sxsh)SNyqGzFuO;AUb-8>X^q(LPv#qK z&VReouYdUm;4c#p-;X2xJdezW%P-v(){)}AM1h6vbR4?CWgPL5(iw)PYZlY zPy=!Wo`{fu*^&G#SAJxPzF%sT9$as0Ng4Sn5pmL|qS6XECrw9idjMl*8MVVYXWz7A0^)NqT zicyxk41Z9d4>eBu%ro0t{5KJJ6avc@mw&yoIKOy;dm$j@;sck+qY{OSotLq_=8RE; zySTLKO`Sc?DsLrJtL5B2)+X7`f)NDiwLZB|ET+hprIq;`aGsROXZMvN8q!;$8Ku0y zX5OvFyWV!jmt3T9bbkY@`mK799usM8*RPRp`$>38tPR9jdGT+tc3B=BSYOZc#D8+z_z#6n-%iZxr!?nTt{Yv=mG3jJUX{|-Br3 zx59(EkqNy+PXFvbOg=1T{biYf7JtrH7k!w31ijiyPfa@#a+FTH8(wz%_t0lsoEWb8 zbtGcsZW(E5S+g+6yHQ?L=Mi1+8B(8aP66;<5BpfE8DOU=dV=#eeDS&Wq%*>`>Vz}; zTv&}D^iZ+zt1aD-=ggg6<&~gYBu!8T2n-yq-URnxDRjv~h_^1j#6x*kA%F4WB^SdP z*8t;;VA1citq*D2p(Cy8Q5~k)wst_)_?oI__u+b@82g%476)$;D|q))KT-D+!l{91 zYt3-$+~kt)-@k>_Vkg$|S-;;L2=JS?eDa-e-VOS_d6A=(YAA?Q^5gk>_XZY{_9=m1 z*2PF}0i74~AdZCMDY;kW7k{Wp0CqU{tJ5bdW@%R^%t5;{PLn3-wP-d8`LNKN~y2)%St%O>oRY6-+T=Lud^)d({W~(uH zKi90gQg1eV+#g8|2!E4LJZt;h9`@&J1O^_Nid|J5~ogQ5T7`Tqz};h&+;BTn&|l(B=$W8fHb zh~FK}6U?E1bm$=CpZbmDGu1KwdE(+D*#whETqY(zg^SElJ%6|_oP2s4Qu08I$!CZB z(`vgfe+)k(|0*u?k-}rpqjeTW_wyb_Cdp?i^wE`zeugZ6Ia7LcP8`*f#i6f4eA*+9 z#@ze}<)HDQRf2xTe-NMO5mx+){cYkx<`2a@=<*GHa_nF9NZfxY`L__|1qknB=P&AT z+-(PmvbUsfNq^Ku(D1|fFhmS6!ogKGlrv>a7R`5 z9h!a6X1aYrpxXCBaIf9#fWIl`8F1Am@~cgD2)D!2COkT0{r$BpEjv8hp^(vr!MTZ# z%DdixcUXsfvLyW%O(r1w3fpwY{aW(ke(izy%wzkMynh&n;98@vzin%Ne;&XeAIN|5 zJb*twkpJX)0DpWS|H<zBP}jmbiBX#ZUm-{wfOs)vW>1S9GNcr3lN{ z8GKCO4u5^*$7_Ok4d%-CV$4$9IPmVS*xPy9K`V}%_2nBvbc$pb?ZWYX0vAnJ@EVSoA@JAyg$MgMv{I${aHXw4c_bPde8 zN0HNp`CX0D22Cr8RFtIg0EU0lXu#PHR^v}+ood1lndEB4I$cU9LJuUUufYH@LNPL! z)Z(?Qd(MXX9*lq$CIKjmGS~8_m zM1R(834WQcqG|Mq1VSRxREN7%B2mj8PQUNDc@JogB(M@xmNRDi;WbjxVu2#sldQsq zyG?GRFy|lfRE~gAiDKUJj6T?)#=JHpOEc+K+$=OKuaND(V(`KSV=G|y8)L#@#dJ$; z1Z1U)$vW(v8V9u@&gdeOF=6?cZfqz;v^T+&D zYI*&pH|J;IWk5RbN62GFW8%$YAAeah!=8L9@hokuxX@~xEu2%|Xki5iX78~nIe#nNzJ_7={kg7eWyP>T>*4lUcKr960MY? z=ZEUROX{^cPr48)_`>M8XOBFsiDI7|c}KMwq~f=$G`yfThMvO=FdB4bTsL_)rKvW# zn#UIyU)x6&#twvc{u!&e1}z$^w}0Gbz8P_TRv8h2SqR)R5cPnFC~l9`pPuN@kcREz zH**LdmWDrt5U<-w?m6%*%T5l{f?m6ZWt&qwS#mrv>JF1*prPf5{P21jrD6Dae~0+{ zDNX!v7sC;}JZ$Qu!W2|a#qhFMWf8sW?Ri}cZ!})rBkL<5VCr`x-C#eqCw~XssMD)S zviKQpgl4otRS9J(pWs;*ucG%HP$>{*RmCMjpsafjwPAA?r3+(HLA@r?rUiH^KB(;Kq_NZy0^)DgTkmy580&3FTA`K2&Y zS+N2n$&Qha`Py*&)0@u>&VM)bB+T|xCG)4EcsdT;)0IL`DWfLwGbjg1*PS(7$5X z-!|B+2duq~e}uK)H`MG9Zu|+P6~46l{pXPOKybjng>aU@@!t^KWk+zpe+p?W*AR|b zy+DFlUm~tYkIa{*8AKP-9oim6JLyaR^vsw?u=VK7`L=B>vj2G1z`wcdFXM*4555ArYp&+E=<9n{BLDR`^0?u~aUm+* zA|E+kS4nsD#CY$>m$ncskHN?9ePI~LJ|SJ=ee}VY+n4Gf6Ba*RImUm}7xQV0IjbdL z=9O0NNCAJAFMl!W*j)CH?CqAED9#!U9{BXd z{6q;3djDL@W#D~Ywf?!-mu<;kN*sfa zHdjCAQ-7b&hyK>`X<+=##>&&M+<%n&`qxMQCiwM-BfqPwq)?hB8I&eThGJlxB4H9I zDHz2u0>%-Nfk^~Ge`?3!kJ5d?9Qvf$7nNx22p;6wp_PSahwj%Qr3xPf8u-t{6T4|m zvQL!;b4WSEpQ1_(Il>4?_UV-*kGjhA698edLx18KWj+hje__WN@-Y1QC+2~EbX1RA z8TOgy^hH1{KC*1+r?WHu)LR~zxZTRbhw0z_5E37~IJ=?G^CM4}ekxe9;z<3ae;b~V z;=_(3zlA3(R#D71CD`W0h%EV9F6f1?*Pm8Ciib(rT*zhmdYUlg*u zFMr*JUs*Wt#fE2Ff_dv7yHUjTb#{^S>DyRz|2KgzcQqOo)*W#*gdM|u5m5Mkqu+4X zp9p;2bw2&(VGbJx?~Y;tcIsKWsMln{$5VV~=q&8~%mVf$Ijr-|dkA9dxVQ9MF=q(q zM`3EM{P=*luQcS!9{RN9z<(%-iVf$}6@Qk5;@uJW5a)VWSy-^u&$O9(RSuZY9PzBV~Q@|Wq5U)1Ol+5)61ptMn>P2*C@V!0U0^l@6!0 z!ihK7tt)X)zfs&ZqN)xOHA8I#@VxT^3mdl+z!44{VyB0;oS!~Xb9mUM#;JW|3r*eR zGeOMDt*hzom9ZvnQaNhz7wNfR41d{RFVN-p1?sC&)*b;WSX;tq7EzSYtFKeE&@9xR zxvb_e(}&eL;>dXAvb(DaOj95UY@gol6HvMA0pA;LWZPSY5+{S2E|b*e@fm^AIX=J4 zh*KRRy3TON*v?suvv`1C>VEBf3E>S%u?Uw zRMHZx^e*==&BonBX6cgTYZ)={Ck=>FWIc-s$7I_^NiYTN6w2MLJa0E;k|iWi?hB}B zZEQmx(?%MwB-LA#dPj*ekbqw=kkaf>g^7`Uj?yOiRUw%|+fVO>i+{*uI!iey_aP{j z*W^YmC*X}x3!p37D>3geBYT6TfX~oHD7XI0s6s0%bxB0}E zs!0GBi+wBjS-+k^`G3*+;VHrI)v(qbLDlhl4lcuab#~hP!h4M`Gb6cFS@Y+r2N|ww2X*kAg;uPuTr}oizz;K?r=Ptg<}kj9NhBl@qKFuUT%j25qbv z4>ZPAZ0~QcOWYAZyKyPjizD1l@5k$OmsC%sMjRwDTSp9ruzwIE$^7b(W_$uh?D_4b zz_8UAhhu%?TufrS6d=N6koS2nrf@IV3+Q>3G(9xA~p_$rXseqWRhGh}QqZpqgR5O?iL7XcKbe6pI z?uko#UwoEl&VOu~{w&rm9PLl4OriaAaxDYP!h;c`BDldPszmHk6yNKMC8!L@ccb|# z)9}?pxrO79dd5ZW0k)oCVb6R!ci=5B874sy2y7}M7#;}Q2SlyRQ!exAKP+&%{BLRZ zY5&XO^`nr8-H9oyM8AC7_D3}Jw|jm=PCx$XyUHS(pnoY6gK2a}O*l>O_-SXr1WwZE z4y5)M6!Fu#q8$AdW{#TFou+2k2VWqEeB>Iti{|w>Wu@3POsA8ByvvlXI`)~BGW+5G9{8L=JBfA}C!Ji`F9mkQ> z!D^35nt$lQXOF5S^fQ?AllLK!qXg=h&p{oHHOK6v&mxB$>WAYqAN8%`Z)YJ%_FWsAT2K<`)!%m-S+Vye}&E~d~FMsz_-E$Pv>cTPOQv5PuL*U}$>^~6D ze}DGlF2n$wwv)Jgge-TY#l^QyOi%0V& zV1JVuABKV$ms8X?lb|GT_6_(aDV?w0l*}gQYNbOX2$Esk3)k_IvC}Ju!1M zl{}3j;(TPb3+K(#*Xkt!GbaSGxRXXvAPFoD@+H(7)Jv{L7rXOM zw`_lN#B)g9o?}|9U<6>v)FdcD4Tgy4q-ro-YTZ{y2)jjZXZ z8ly>TT*BuT>F$}r14?lV=QTl`d;1*KS$Fg1>T->g(Ivs#uk)=@qfvVV64tH$Bj^U& ziXt~fN`$Z<(&DI_Js#{OgV({+seik_ zilTSsOV_5ML;>rKIJIMdW`p0T3Un`Jl5>gA1m;oBf;PT`XbH!srS62a0j(5ZL(PV)@T-_M{V)EgUiA&C;G z?7mE5$0naQnR^-8cf6~1y8i$ zn|eM2?x>sz8I&!2D9EksgHSK0)j20p9M^Dyd_zEGsIO)C=a2#Lk7EYcjCmE9NS=oz zL+zfi(DJQ;Cm=lEXI=P$trXV8birNqH5B&)%NXx1B!^~Q~DCC&)wycA}L*H_$euu zI;vw%-|&=|3H z8~f5Q<52G=Cf-e{w0{`dx`FH}ft}x=?*~?H+8sdV*QP^AM%O%(HEV;`4Yq#=Cf|2F zOk%d87U*}_AvZZQ!hV395zcmF1LjY2kY}y%}vQ`gXCh%Wjs|;RaZvs7y=}TUO^Il7F80xJA&qfLS1@HMA>F zcSH~G7-W?W4$~bf@*bNNxIaExHB`bUuq^bNftHI;wb*$zJ60FXb?#UD8f)|PFPfm( zE`AUFck1$AHvJH%`tSDp4OjiJ&v#KN97B(Q6wM$cNf0za(iD9(IUy*9P$&k&1WsTy zM(!r+r=!ep?0-|(Qv4d{M=Yqwk8&Bb_!P>JztZ%P6Tm*2^eO#oPX1hCH2a8mGDl$l z09*Md`2UgFf{P>gPv(bw*D=BpIl7w2_#>y4|BC!oj)BgPVwe1L0eEp}iP0ZuwZo|$ zro|t9(G)vQvv0&c$}9-^nd|crCf(0*D93yTtEb74<$u`kWxv5MMLp!-w=|g_V9|X8 zSPs{1#hd7$h8+m}$>H_w+O-Q+XyBW>zBg?)`EHi!|W42zLpNzZwy?A9fG9Pmo+@DGFJHXmc zVSc-!uYWlPv;4%ebcrz%hIu5nX#t-83f{Fns1%hIgGiLi|~1n1?d1@N5hpM3-` zZwx_+zE4sqo(6PtmQ}41z$wkf#|YDR`X{v zAt$_53FR(ukPNN2#$Dh|l>=r&ZwNHGE1|FVf(tl>Y zEhK5PXf4m_xnu9|O^>93R7^5(0YZN$;HFz9SP5!yu7|kIg0gXMDv6^y?yfWE(L;j1 z&W4boQMGFQU}kxc%dC*x?i(e}#z(?;t~>9w&F51l{!@p7{zHd?{sV`C z{=}isC`}U#i4g=sBQ%EZ?gb_ZntwqF7~b8@{u)EEpECBtO%O-6In57Zy;JI)VIT8A z;3IoMq`y+>(O!dooDTVOheE^$q0WmV%bZbP%Q1;hf`a<|iB1lku_J>)9P>re;s^rZ z$?g{ZYBpx_8LonUlGf?rsBrj`zd(;wb^^cqyW*4m-W}NPI7syHdOM}3v47)6Qv67v zA99q;;dc%_iVvBO@uN~R`TI_wk3-qMcPQ{ZM5;Tj3ZO6N*J}T!FRJk;%)I{3JCyCO z9mrT8u>6x`@&>;C_GixjqMxp;fkgyOrhe?kWuJQJs$N+C6pp(LX(;)GVFi>{1u1fMOet z-llxD?PMMWhm0DabGH^msHfsvq#`koNO8UGUEIUnzp7P|vNpb<4WTg+PBK--wihtJ znY9c~L^Ddm8=&+VHi>{pz@vB#zp8LmS zxNph8MG%sb02sL2022jufroHzjTF-K(#lFI#U{*H`kl{%?1XI|Te(nT&b++_o;ZfE z=TxqrDgeA7OVQB~T%(+Cx7|jtL~Dt8#up%#FMr!R4E(Hlm@VZWdWY>*xN^FFH6@=t zE0A+--I39%i+|Ap?w?TS`Wvwx$C}+?3qF8+jp$g%L=|}f2bQJyK)7FMe{4`dQ(jv| zb}#6}ydmFs<*I-3Q+mZs*nCt*oP-(@FEQJbti+?u%|f52cm`1@NVjxxLjWv$csT2i zAXD`$^w22UZx!n>-8Pyjs0BWZhDx6xFr~Zhe%^@*qJO>eth&DnjV5n4a3hTA4wlay ztcjsiuW$Bj$i-Mp`4THp~K#yif&uk0mhrzZ^iGMYCa+~4AT^Ui|H_L{3up_vL&Cs_^C?YSkF6GO;gHA25OcpI^++|C|B9myG zX-{JZ4UeHZxuXsD^38uu)cKN{80(9PADfBh(#NpLnR~ku2o&ymEly`5Z+j!S4VUS> z+`E%O6q*CSwGQXjZQH>K{z5@zi1?mSC*p~wB!9yVURaSB&{H_`u)AkOiZ?b{criwIDp+6KxCQ{!w{PyJqAy!&$kPtW}yh4p6Ap9T)B#j7`cth(960Ep z+Mhf`@n!t>&u8WyRet!<6um2X2xW7VTq1v^qArp zeEMIG=I7$0iB%wl`~kR6-vW9QlX$@6;JXj&A2e2`u@{4T+D^K7{y;C?}4* z2L2_rksT+F{}u;v^Wp!!h_Kz{p0e^vj zKVYEuzr{d*(&zrS80dgNzy|}pxqT=7y8^Xu1zS6nJF&3Ba(oxu>4+<6*66^JlaWNG zTXnZOHM)sQV{3CkZfOvX9bBaN{%t0z=@P8(d+J@PVYl08Ra;)hSW=li-bEX&%@oaK z1N<9Fi=TL<37F?+z}<0d5_p*-q<{5AyhS;Y@b$+!%+ffs0w=SzxYJ1A9$q=t6t@ZB zM&FsZF^pwt0~fi1ua7jB`OYMIY0*tyG5M5TpjUg|-tDuWUiE~}^&7n0Sy!5K9SAo| zIT`d?WZrQ3HGV#j}JihtT221?^J z^;0FjLtp4qMVk>H$$RP`R7d6Prxh&ws1fh`;Ggo{^yg{Mf;==6X#7ZfX5_(c_U|zH znF|T0M=|lv%XaH>yhx7V81i9UfG#>B&VdDY+zcU_&@Hf@1hC0`ZnuGEo3Kt*1{G7)zhLnL*#o}R-JsDB-$0)CpQ`Y}pnTpzFZy-gnP3hm?AdoC%B zN3#d|9Pnq=_mb6Rm)C_+Qwo_WDtU%4OxV_tPZ9`tQ{d^1M&7!;8&z#rfWM^|TF4^t zNzYwm4o-c)8ZP|yOMm6XUhW7h;?zqRUGaW8`(m6ama*OOI;|H=x97zN5OvdHbUur% z{+_bp_N*OxHt#5+`n1UY3v1-KoZ_(RFKXN^zu5XOwypi54!6UK9C9wV_)-7zzjnJc zE!`(E;4IUv-tu3#x~;oM-v08;e{u5Lp$gN~w_n&Pe$4gM#ecu3$05sp?6)&q-2eUM zB)_e^?WRl5r(6C$&hhmMez1c7f4shL*7sjs+jpkquK~sEG zPm_;GO!gVLhI};Tf3EAcBc&Y+?U;=D#E{aD*iC-unjXLj``|#jI2t!{{J>ZHVmq+Q z^Iy}yido?wVSoJ;JD}JXA^hyqL4kjS<958Yn|D0^NYP}Ual4BOB|%@$xN9dx zl(sN9nhef!XmeT?oRK7Ok3l7_=bq1=C~3M4uYme(V#j7_p^qR(8Sqz4`e|Q)IE=dE zA7}N*_%l;uu8xIV7=9f^y}-A^Tg|gP^VYhi``9mEnlqbooc+gz*W-4k9~QnJx9MWu z1{{Ji`hQosRsAi#^u}E;f`S;1cNECL`q8oRhi$rYvj6C&fZb2IZX<>|_eJQHd+ApT z_3FI)+^?3PMiuQ3OLvvkWWP$XzFT*HCQ}@=Eue_!dm^`Uih2*_6t#+EwK4k92^wSn zQJTTII=WcW4ypG0N7CNgZ;w5cem=Jh?DzcA9DjEDrXh6x_K=RY4!P}Of8H1PNYfh8 ze%2_s-@m-gU!D`-9P^I_^P5wfJ;~#3&*JQj`}VFF`;X)B$L&16Ul_fWz|ua-U!wsCt7+{qcPcxU1> zzJJlA(h&8w172;sA_i!&O;xseXdtKe6H3%ZdbJ5YUr^OjFH<7E=~E`86k0QH&>r7U z&SOXgMigTL3~Wu;CyB7&D|k;EJg33TvW&Ak7K0IKy@VF|O+Ud#B$*28`g)Us{xlUV znxC~Z8USIOnv3L{#fs18cLif$!Js$X#(!bhQ%X(2F9I!)sEW|7>*^~d(8KP|9Mkm@ zp=l!ssBPt~fw9C-XghKb6q~Q9Cd1V0bzyv4e-Mj*W0+E-^3gO!duWrzK{B2-v`Efb zya3p%o92lLle`o6jLyMEcofll?1$X5fVJ!wTiVVd3YbpkxP{8wXOekK(8MStX# zA0WICw`QcOPI_s5Cq*ximoBQWV5Yq_#hP3TK)GHC9MpI8zNM{E zT^!n&B&xvDrJkeaf}Fj}gkLLtjDIkOE>Uut`e=Ma6Bu2nXqB`zQ4=;2IeEu zTTy5B+Ph#bFWtS;cLfhOd8vgtc1EH3CU%dVUOV%GLP-Xb0>%KVi1_2Dg8k zXx_R8+fU?7#`wC;UQx8Ygq+I0H!@ z>+R<}W~=_y0M?{9B0W2p*csR{ZU_7HPi6-@q~n8%!Jj}8`YmC`6bI$n+24+5j#)V9 z5i$Cj&%!>Pndm{nkk3d~=5ISNrTR$HV84wxQwp;SpeJ`C`{AeEI^iDx0&ssI1QW)O z!K3RpfmhYdJkupkifCP(TUwg1XM18R+pfVKqUNUOxwgpIyW@y<-4~_M^5gLMkHafP7L9eE)+;zWug`CwbgT30Zo{GZs@DuJtA8E6uF0 zZ*8*{@q|gb*Z^aCnY)`OJ}52FbZn=(XH!&(bi>t)^eWLJD!TeSu8w1#8Ga-DE*u|R zp7a%g_U^p{boW$Tp{IYuEp#&Xcx*KZ7Rfq=zi*)eW_@NkUgqVtL7`w=Jv#TQpyx6+ zUjfTSGJ*opB3mempH^9BwMkht{I%X0k-BOZi;zk5CCn?`ZFal%hK0f5dc9D63HddN zpXPSB0`KynTV&B*r+P8=VzXu%{a9g_kVn zJkZFee9_*PM!YG3VJFI((IqztuW0bWLh*56Sx3?OM^B#@ZCwYRHW*LMn`dr6)`8tW z%ADJ-N%=5~=(T?`X%xV8;lrU%6TeB_)?KtQ*Vz#smL{ zxA$sx6HTLq@A(yTPko=rp{vdXNq|TaQRt4GgM<*_*B_9*-L`vs+&wdOx*U2FUZHL6 z^{(}V^?YC0@G(4$ePz;STA+#QxbQgR0%5p5OlOZI7# zJKvc#sr-?iy3DLiFsP`R@{i!9y&_fLL=s!}_G{ZTl65Kv|2iv^VaQL`ifUAiK>zbz0J#Pko zZDjHBItaNDpK|W8)GxJpNFa)uw^Arp*=N!w;CVrXUbbaT^WzTKR1ZcKq(<`6SnAkx zro9{U35{uu_iVI-fay=py3V>vqw<4?$LLT_xYbq`Tv`RzPa=N z%|*T^Y7|A{1O_4WUfqhq5QKdi!J&vfa04TInbAI`58k68!TVSAcD)FrJrKPYkHUW& zG5VCKh2%baaAQ*&sf*uZXE$<1lDi)UjQv4D@9NWFr+;DmNAUL1$KK-u!|+#StoTmo zHd=-gdk!su{s@zuK_+jLkqJ)u%))<$oe-?@)EQf$N*7Wg6?6bs`?u z?`FVAn*!XB)m>we{@&tV0d|l3Jw|dt=p_>A<*N_xtl7-#!=Yb!OMko!5$Icm(uX%O z@cHyp)^ebao@{^8Hc6z}(Q4Ql{>9Ku_;w6dd_FJLES`y>Au&Fc1{0(-QA57E=K{r)nUrxa$5qrOBrwq4SJbxm#b7Z6&1 za|-V9WM4k%W&(a$9CfbFXrMjP=yg$E8z5p{gD+S(wrP$XyjO3mgjFLZFdt(nm~%8h z!{u<>?z$YfV~?q~LHFttcFljVtKp7^<+#a0|K${%Yj-y@HZEbC*-&r=?)gg|MZCB+ z&3vVYJJXU>s6Ez8Ts|SAq|V4uCPRilve1REwg*w41}Wd_!y2yFCgK1NM?}|?=wvVM z9OWCM#;?w0i}-q>y7NOm-4rNnyJ9|MNtYN4dK2dRGQ{i|tgY;V0)&6>Roe2RvFFXz zrRmlSeK(Uz2a)OaeKKT%60a|L#ih6^a?XV2=|Tths;{W^F&0@kaLq zg$*n5#%Tu6{%zFf~=+Yz1P>CJc~Op+OBQ~*x;!YJL54R054G5Axq zeNNh%IHZ&#H;U}5!igCU^~>O5)$Vxc>>nje&M6{ajpx;k0LQ{21*L+hC|gFonz0sM z@e}xDH=^aN4t(I#YC7EF;{(ehny|Q6k@?7-s{7OZ{3jp1BWR~riWh$wA zxkT!oY@t_NvMhg?(*>2e;ea zJr4ZbKAwLzmi22PmCSC`gKNoC4@FXrPkt)8K-BB=NrAfxE)4BXg#*g)+c)zO%iiUD z&F{3@sQoP>pJri>Gtq($8&+xUHUX6gF&hC=5H}bpI_!TmEk+RDMUur47KtLkE{i8B zfHx;C%A7w>;<0y<_Ur`9%W#U|9Fwh{4Zu0;@`Z!ehk7_>w`z{HBOFB@IHXKzrdAvr zrkjr=<%_X5RWj5gY@B$~U>qGkl9hP??zoBFPNhv3Wt`Lbbi3Ll|EO$&h|YdcE%mS_ zBM6r^TabTuhex}kIm&WA$7Xp?9v29RMy5QWarEr@dtSKA9i&#C8q@4S6!Q6nJS`}| z2jw_iFK3X-Yv#Uf=}sDNyFdG)%Lfn;Gl-6Jsm&fLPsMr8K>gxA;&Slt5_FGev<%0m z99~ojj2!5I$zn0q1VpmetlFRykWeyrZ&@a(q-U6xs;!k*75%!x8~pizLZ|SlSx;9`0z*FSaN3C1KKlXNixPjxbg$i}(!D!rqj-DSLAs~QU~(VYga&_5 zzi{6{clJd_`x?l5f;5i3+q=WPhHGPu8!MynJ=T^Wdu$I%_Q)E9Y~1e`)DV4le23v) z)v>okLGLn+U|*3Y_F4=Yd1nlRcZEl?XZ&^_V`N_%3-;*ac6N8@`SJw2Z_rLlqj!IU zIf4JyMeRMMI=8-2!#D}kg*tW~kxg9Y%R2#COK6A5A86su3W5KI7VfMN_-|<8&I*A~ zXyNe!X+P1zY}R>Z{gD=aIR^YUv~XvIz<)yvcUB1eEn2wyo2{Q$d;s4{J_L8XyS&gB z8q(HOx9tOJU7V^jJriY)YMg2mk!4sBW!2y;`;_Xsc&N9l43IZW)}yQ?utsy>^5J(6 zkU9?MU_DwsB#=df4x}3BVyrmAwJVqKEHCGsOZyuY#gWPH52kg9iLH*z+s%vqMY3mD!8@ z*$+7{0ih#kQ8rM;>l07sOE4cr^PxrZg)K&sa98G<^g0 zCa2*q)|c^yxu3%ICYCoWPodo_oeuXj35x8lAt|$ z{|c^=k8)Y!e;rWR_5kW_inalG@};SW*m0YDn~V_fo@v>he0RA_cCC)2yXKGInVntR zCwocowgu4eo{hny-zs@xuf?FhnWf3@!|zwyqLTApCThHu{D$D)2G;>!CfB*PcG-(G z-*_kUjO*O4c^<8Q4y4^ksbq=tJ^*Kju5V=r0GCbYe;vCvxOzn`yCLouVZMp%N&m=Y zaQLqFM<1b|8*F(WRcpHeB=9ApuYXLj{oWxnbLu?-B7Za`?fwIMDhAW+%c{fg^vnXh z5Axp*{_y_(&f5e2-TV7HZx8r)@9*!tJ>XZ#oX>JO<$9|xhUD21e|;LYh%~P|ph2l_ zpKCe|e+gQ((FsAzJilLWV^&`S1HPW`{Dl)l9$#|y#2(5hzg`&gL0(&?+@K`|jO2Wt z9a5|ssF~Z59O$lXFL#mf1(Z$6DY{C-G_So+$k*cueThhF=dQys&)aE<<^aG#2@O{z zUE&K_2x8Z&sCuWL&t$67u9}xh?;hNFJ)e)Ze{q|@ZiNv~yu1VqRUeP|NCT3UzN&G! zyx`ZvAP(nDM_H)%1rP1PUa2TU3M#)NGQ%bBF-6DBu{qz031kVSWG&wWf?Ime!{}UR zPjwOa+)=x+7+xrkcLk1ufj*ygs)Fobk<3Y(3hI6o=?(TpULr&> z-S81bkWSp(?Fh}O8EDnbX`C`gN<%9f&#%$pe2HbRPiq7mzABDfs1D_GjgPLxq77>V z*(5-A&C2(&&b9iIfw)6i;Xy+yNyjMg{_@zWOK(Kr0~))=>h`lDp4{tg2oG2Jf9cWJ z=%Y#b%>{nZq?~?H@|Gs-K;9ZX47=-Pgd-p$C0pccJ*_B z0rVwcPC>jP(5=6puz#5NVLf2LNR{j1Q~3a^`6QCJJUTkJ90oym)1CK(GHv2c8_M|w znu<@C1zkXQ_pL?Lrb-=Fjoo~o!$$#(V+d8fJ? z|49{nQ0()IpA!i!+dg12AW|7&4pTI`r-_^)w6?fnDO%QP)IhZ{Jft7BbDH^F9Pz7! zh}p<1EEAiKo*t^FqX8s+7hAl&5@hlU3>iQ577?h)A*0Xtq={(1Nyby+e=dZfR-}Si zbTvHAr$avo{AwJn6*z=(x9AVCgavANjV8*jZG)rvbAp!=RH|92F)$gNhj!!H=u{h4 z<}-uA4Lx*xHP=q73HHi)V$2Z2cxY zkJxx+-G)nq22fH`bQ+Nuf3D}lG%XSC&G(w8oTXyC+P7CPXof2^=M{pQ2DKFRWh(k6 zZcUEBkyufHOQ<&-{2)`ysHyxu9cUuV@XH9D(QZ%@)i3kPZ0*a zL>i4B!UdQP(NP?D?}3_y#l~pWG#B4oliFt?SEhVPJFg7Q9eP2BfAd=NldKy#b3$5N zqO!-6|)ohMJu=7wi0T*Gd-()o0P zIfw)r#hoGeVj~{E($2H7509BF7^@7e!6lDJpAd)m-g4kS>2&zJH0_@V{Qq>Ve@Y#H zlKCjS(MD|h_a;ajf5UMcq43XmBTnxu5qVE-+!)?o;LTx(C1R$mj5F4 zf%n0ZJ^a7O06`ire@Oku_Zgt0Tl~!|&?j;q_)CX}(pe_X`QqXAnjl8X7S^C_j_b0} zqUa>DQy_2U)5t6$Z2fBY=>zjlLMI0dh`lMcRm*gW5W z8jm9Kk~Ow=%YYo$ znue5N>GAOBbu}f8Bu@6yh~1_I4w}Uy>v;1Yr<^Ep7G#U2nu!XN$>EJu6Le>U02+rLA>(5~w@px(eXd{462!5!LB z9(|ju)6|}Z$C0e&}>}$Kl97~}{7rr;!ffs-4 zUfY}3RExFrQn0tTAbfOwC_lV~?c~QfX8!rypXqVnx85NqME zQ{{P@0JVL!uaiNB?zym}MH83^J0QUlBS#Q3?%G+oL;X2iYp1hV1f@$=Ij!`z>t9pZ zldK7VIgOxUh*<7w*k0P?>Rg`HS!7o?vA~v~9L6kloqkkv0S0TZ7>PBU_yDC05Rq8@ z3RGm646m}t!+7l;8GNs<3wDB#f6k+w;&oiy1{({_H80i0tqr1_?ZRsy-0-reL`^;d zSl4KJNdk%o;UHgf_+Z@fV@acM+?X|XQ9}K~*;ku68Ypd~BTvn4%FeVf)*PH2y{cM1Fn$Iq#^mRj!vkK4?d z*Lvi;=yp9?^DPVx<>?g(V5Ns-HlkuYF8*LX{fkp%%q^02yt*akrghub8YO_1rT$UW zC`DLaKcvV1joDc4-ddYP%dIYC4A;cKmc>diTyR#fqO!qLuN90A|EuD^*4__^C ziqJy1iBCYDGa5~fi+Kv5+9jC;BW?+M;|2J39SCJLNEbx~c6li0e@C-A;Bd+EE=h&U z8spl7fB_tz23wtZ<<=OQZVYn5ms0xh*;Pl}jW{WfT9-L|3iL4 z>Zz8%%kdepfw-}7<1!FtCx#s}pLGOpJaX@kE z=%l!xH?f*xsw};5UtF@sU0j~m24&dzL_WchITQ|h$doWpX6_oDM><*~>d3-Sxsm18 zxRI<*k8IN*Xx5#qVJ_dIE^pD*j~0D4IxWg(gdiAk~|kw zRY$i7$k%E^e}ZpSjK{R=cr*As@u_OMz0nD#yy(T$M2?#?Edp2dX%amQ!1I0%?_5Xu zYG>N`0B7ozYZ9JyAE%yW{yrTuWXl%*<;hl}B+I|P(4#NZNaS{QWc;6xa~r?=;C0D25XVUJRiWj!`hSPq3k&?GTQk1dJm% z{%Ha|pm+a(O+F)UXHfdhe^P3%rzGf|htY2rl6`m>xoP9iJ=me0tx?$Ce}ZDWt__o2 z>~4fDeCyghxAUGuvoXOv0iN!Hcsqrcxnl=Ef1u*uD(hfRpeNu5g}caB@9_#5@XC`h zL+a5L5JCLKh=6_WN)-4`it-l!X9i~DLy`;C_RV?Li7gbHqWPeDH+wt2cb1q94Fh3g zeq>@?2*AW!MX4c`{D*ssiN`sQXJT^5$VeC<)yR|eMjYBKE0#zoC9 ze?6+@$1!I+rU#k`{cPf{%ijeA6k&?5maEqm;%*orPcrkt3+wZC+%YXy3{iIkZsLcw zcgNc%fFs1MG%K4!X4R0>jC? zpD}`M)R#%~&rx1b$XO%?tSx}VxtGU#fBbMcJ9N^+oo1?PnuemKp7#@`uzajCk5#H- z`7|!Y^;Sw=OvSHDb3du_74v`|F)mQqI)fi$*SYX`mi6z@U*Qbcp zyaqlg9*YU!3ABu7#gqbrtd~uVWaXi$D(=~p$x)%>@Kn#G*&PZx&AKuWB_`pF_GTX< zr63cU9Ax(FF|_Ke4gI&#*xA7G7%RGo$QOV!{*TiZ?;gLOK=yaneqiTcua^vF2R|e$ zsZa3|p>}B7uG&CxuRMBtJHgbhMYlhLF9E@Ksd(^?Z*H%bZ)OK9V83=4?{hIWxZZ?2 zitbfN3I2YvV1M~d2cy5wV4J&qTZnIXd5ystsqZD}BB0ke+3jLn$j=<(d4~SkyoI+* z;iCy?&)&+ehxe{@f+O@Yiz{>c2rvq@%U?Fw13qxiT$lT12P1#Orgoy%U1MGdHMaVQ z16r=LiSZ4!fUmG6XAAiT?mXbbXyNr|mGILYjlhq|L3lSIbl=lJAJ7LB05Z z9mLsLdWAZ+$3>165XBY0vAedQfvOp^ZbFK5xZ48}nk9dAxfvG(R54*QO7u)eD~tc$}BZ5gD5C^(0i1 zGNldi)vLyr1YZMszy??~!_rmC4Li3Jf!8wpeC2u~_ApSvjcaBZraV-sZWleQw68^e zg{N1xY3e#AfI=vb3LUQOzKt&%< z{ds@iKGi6K_hfyAi}SKZOBdz!^NwL>+X>H)iF{`E<#n!4^zDV_k%K&kkpj1GHvlJQ z%94=lo?>Vj9#5OR6E`VHv#=0Mr`AdjEViF04T=m0sca4()w^gEg|tM+@tM{Dd~bC$ zp^-+07=D04Ff-7yV|~7e4$fi{v=IH| za|qw0(lIt)R&?YeiC$QFA{$T_tU+#L+j(K^@p!t+rru4&!)h^*e0iAa0_SJ&OipTe z<<}KaP+}CHhne@gXC}KveHmJnxkeay(Fx5tpNdUJPd)37iRCo&l}bxsp{VxeWAJ~G zjIRsN-!ATmAJwp%GSJb2#A&qoPLS99bZ{dZlAG|?kr*(fLkz-z#?lD3R&*7B{Np6&9835gkGzD;8GUddc z{4UU3b>xE6Nc*b_^X{R)pT;~}s@fJkkc&Z=ta(cb>F1+ZIB@~|Lfqh+Thzw3uT)v4 z`LQ)k>UbE9f+US@IOue_J&&}==_W;?krAM{WEkAiu^tYufWv|cIEC<(w-bN#Pwb<1 z{vyoJq>YpKUv5wrUuEL&+0oy->wBK`t2=&ROb`J>5JHkTzR5}gM`#k=-7|5D++YFU zzJN#w`qb_&fOi3gr+<8zMuEOfly*6n?w*(Ad%Eauxe)KtFhM?LOaZ!Q1wv@oddPd& z7V_3~oBG@2;ch~Q?4kZNdb59=cg|pAX7Qd*{RLx+zf=*@yFSF<5r6XS|FAs+^)~K= zi2eTfZIrnWZ++9SjkzW70k6Bc9sMrL#`bK-b`U1_{0EKfm5a#lJ;S}7F+s{V#$?m- zque&Yg*Os=HT_$mZv^Bs#W0H<<#&iS{$*ebJS>Jy+*7Lo&|0dVI z#Ph!mMctUv`eTA1n>RHv1K%YGr2ph;fbSpYcTbaLa`E$K05F#?y8*xHsqF%V>Gqw0 z>$ywR36rwX3U>R>;ry}f*Wo5+u}yT~s9}us7z(f3;01dfhtsgh;=4H4g$PIDafA+H zE3o?VAje=I{^tu*(=LA&n+!(lO@P0!$C`R$Sv`)ds z;)L@hqVfwOKrD@LulRn{=>=O&%7t)`v2mOVx*jl)l>lY>X-651666Er>@jh?HV^${ zp%e0^jHwziTP|)6UhW+8mv)lI+28&JObU%FV37P2XBqf7zZQS~aU6Q?UPg!MB+b|& zx|)g8&8l)Zvh7YK+T!LsKUmGYM)GxVGDpIvk(;gx9&;mw>Bz%yZ-t3x%cAJz}{dY8EH*I;|%G>`n^T zlP@QyyJwfD#6p@F$*BeQnyFlS z*-l1hMp^xVq^=W+jLZn^Q|jctXAgiq@-G$=r3Z4auicAWVQy(^>os8w;c+Oeor+m+ zWjw7a;b1kbEwvTvreUpCo`b<6FwBVSyRnxNhv`^9b5DPy5Qi01M42A7=?8pH$*b){ zHnp!)*=gY`?HPYQcGQ*A>(ZU<1N9HI`K-qNS*ea-_N8mt@S z%MXL>;5dy<$1T@|Nn>2S@Id-+JeQE`g}^tJ#HW#kN_fEDs6;llWBi^paV5Eb4Tye>-f;9dPqKUw^zj>5MOcnd{vTtZ0{Q%kjH@3#sVi$jfV#g zWefsLBgQd-pr>kk68gwE*SsO!y>^YP@Wo2Sou$p28ugOgl_GwBKB(3_tXGHOgu`q| zOsEXAbpc#9x(WGXmgq!tFlEhP#}W*_p+=q68O(aNya+27oAG71TijcFI)V&^zQQ31pohmc#lxBkvG)w% z)d!cWbGpvw%3)ULA#t$jPDN`xu>-CZ5QBf7xycHcae8tR0qKwBwjjd-4urvb48j4k zNsCTmM54Cn4TcU@@`N4T)0AVY#JnAp%#hz_@-Bi`>0mnG#3pxe(Z^F2s^F`cfv9=* z>!Ju8BA*|6bz@Fe5tpl@-X2f(ggx0tzUZ(V0~$|KanZS-NL2?2&kA zqc#6}2h353+Bgn`{#<+UpRMr0uztS#dsc-IGznuEOi?6+Z#)Xa2zsMcFoa_$y3s5I zB5?x#wDbac^AQxFF~#8?1q-l!yi0=bwRbeR z2cF=!{W1BT>O#kR^TlpmPVdwS4|YCC<9jeGco%$ZE4)($e-OW|h!ee)<5+*;yRq}y zqZhGub}yRVfj2gc?6M#KQhxDe?7a7rEtFZyvrsaZkIFF4F}uKn@o@K4T!lzuyN?N` z_RfU>tzaO77a+aVzRXPJnN|PZ_jez4`lX|w4IAOlsV!ea^uU+El(5~bEm|3W8MOM- zG40s>*c?&aEX|bnATbYo30QyCLLk=9#G|2L`w5eHCiv>`s0r_3VsfVQd#9Ygal4Jr z?Nn~{XVGS^z_-}iM==|M=ie21d`iW>XJWvgRE*a}zOKc(nTh7&@!hifcTyRBJ{ndS z@do9&7x z%Ng`ToqrVhUEo*(4Y>0N$h14H4QvJ`KX%s#2U+qwb;=QXo!q!)snhjw%8!%!&|%Il zUTt=lt0EdN!;>FnvTuI?TYVP&xwgH=B-2w#bWoA-xHtB52?gUcGLZM8BpXu(n7GCw zXmb9dq|!`#tCnVR4sbaA>Uj`s-E*;`NUut>FZOAEwj)#&{SK=5qvPMTX+aGK%PLR2 zOvk~yW4f>O6`umhIfPkh?gh-Epjd3#f!c^@$=r_#X*A-du@`@PgDZ;CpF-%ik>xhy z<8;62CCetLzXEcXlvA&W;j5m=J0az`6VB3|*3z>Bjo$4*;xZ{+;e_?$zUA3;ox<*> zk}H)_@Zi}1*BxNj?vXmeZ;jlY>T9$#-JV{)5H*Q9DoI|$(^xDm{5%b{3r7b&ab=mi zRnm!rE8ygfjqHClVeo_@+Z(S*CDQjf#Va#+f#M>BEJ6s0?C9TzI$yK zFCm9l-ywg~yW8sDEb`XAz-RUiwOS%uTs$DJ@uQvP`D}}( z2uSWCVQ`yVo?>{Hh^B~%>{?NjaZ;& zkO&_uyfnea@s}IV*C-ZRA(+pti_rI=BYFjzugSH)%ELjwBDz?*^Udf&OsnDo=#WSk zPf~vb61NBKDli3!-tbn@iVgld1B z^*9l*g+0#Mb^A%0szw9K+ciZ)>NQQr@MN>SJc&DLX?>Z~;PyF?Nu-pfk{bZ>h_$-RkC`=IqK~X47 zZ*+KL#Sr#sD=;1H)xmqw2)T#rqW6Eyh=AOCb?JA#ZAk2K!#z;AJAHj#I~?p?8oPBu zx(DTgXphK6@4gEX-`QmF9tD`b1A0-q_lIn!sN@eS`jss^v=^%c>3jMOv8QqA;9Za$ z?+&UszVp-VHR!ho{Km4QFQaka7ejB(yRASF`xG7OU0)ng`>*(2ZcLEBolSrHZs};3 z4+5)b>9z8+dGHL>Lp~1&wjc2~;A;TxWPev^%gvf{^Wd-5x~yv1LalMyM?p$-b71%; z=8JE!4si4IE^O!_MB@H-)iQen>)>kc#V6m`?zZFDe=hh#`L6_g7@`|I^KQ}DNb8@O z*Y(F^ni%~=WNQP^Ol=Y_UyFZAroIl{cg1q%2+xydIg|V~D=W-%ZC~9gkWTsTd+sCo z+e99?A$1)sthN#LeRr!gbDG3&MJM{73;sFq<0%W#H}`Si@7Rx5&`1(qppZQr!nS2Y z?2t@gl8oYI1yV%E0@c>l_6WoHkj)j;ztmgj%MVn(^`7oN*7&iJtlNL%P{B8~cPp{G zX^;{_o-VltKy@NC<2U1BR<2V$z_Y}{k%qh!VsM*MqwWtwy$>*dNU!(ke0Z+2K-L;= zXnAPM5OW7&(s-R2VF(T6rpm9I-8BC2O3s&%SrSq5c=y1A_Y+g)aC8K3)49C1-6Q4v zQF-)yt^rTGfHA3#DvjEKc>RAOHttA%%aNN{iS~d_$T(`-`vNK?SX&lRyarD88`<Qa}Z)6k4kL!3(s7s!<=bQAvuV{Sk*LlU2Arwjtm_mRlEJTiS( zPQC{);qP4|q4qRaNbSQv2=Wi?^X|rdpe_1G8tf&HDe~S_qj&ibx|>^W`_i_r;?&!0 zlG+Etq3M5abQFf$cKNFsOk&^ol6OTDMD1JNZm$vVju+v3{t1fh`8@a?YT6x-;=SM# z-QEkf&$HXM#qG%h>b*`QkqpZMop&3_(8 zwz7|(him-D+u*(n0>9V>vyc9qZ5zCPTX65}TYi7+&jHN!ql+=yI~K9+@AV^%rte$e z_s7Z~AAf&(;J$eCU+%%$uv3#OVXtelN` zc%lW9D@3LaTkj-0!2UkGX8@cMaw#>*NQGKlRnEX{vW6G~5WO09j7HtgMlQY(akKJnIPFYQYP zMgCU0qUzc&vXowr+ok0J@D%DMQE*g#bK}JXg`9I)ZYlqBIl+C;|K&FNXX@d%cmGl> za6gCz3c+!B_x>ed6om;0!e|_a@K0+9;x`83@gA2(;2q{R6x*nH2<_qR4S!7a0>kjA$QAQ zVqYZ)_DK8&fE(Zj$X+uUzRNnP_prQeA>xgY+mjRNd+6ueyLf~8?JSP%uZO)$2R7hN z-g<$6Hc9hau@LQzhV{2bLzPj;Goyb#gmqQ13;CBh-0vv2TYfUI?$2Jq!R8wG`L*Hj zVnNk>T(;pN|B*V_Em{s+@|dE@`v-|HSO|1wi={r=!SQ_uZ* zp&I1)rYD@e%w8r7J9K|`Q>2&m zP~&22rqZ>-562f4$z)d5DvEPg+?CHaK#I?@*ExslkRU9Gfs;lA4`TK%MTeiRy!Rqq zQokbu-JYWjCgY+rYH%4ZM>~E<8w>&{KlE;|-v{b=H+qPsBk^h+vyG)rCI(v2ctnnp ze+H8yrJ7#mTZ$`2>xDdI2RVO!yd*$8Ye&cr*E~VnF|L+}>&fdqK&p^EKP1eR@eny5 zm`rEGgxi=cE4X+{r1a~l^nCzEz-(!EdY$r6-Ka7|9*u{(>a0qk7!z25?c8rybYoUY z*EK_}ja_gWUq$1ztKW{OzraAS)YFAvdk(a4{@UT8i)N{R8K=Io0dSV?O(AQ7 z*-F=ZWF4ND>-;q0)RRqdf$XXTb!8?*8D498{ytIqg>7U_(-?o(Qj%`l?AQEJC!M1$^Cg1RnZ+p<^?)C4O6!xC3O1~!!ZA+5z z9;@2Nx`uCqk#v9mwP#oHz59L-MQs}i@vAXI(HHrhkCA$BW-3LFj``zT@-_c`7W91KetbRw2Phq3Yzy^OZ$wzbi5&5>^?bahd?_KLZ z+Pi+vA^&%K*PosD+ujv_+q+Ch=YgMMZ@l&kC(&=tB4?!b1DrhNOiYAP0cZ*)(z3V? z&S|dghJs+Vd=Lkm>La)+ZzA6%ZW&g%H#Q_n^Wd_f@mYPkA!d}p>sbJUES{K_r7g`> zFt93I?xcTL`V^FIJ6sK+&!C}%ReaomAW>ow%`9ccf<3CDfd5|`3dy?{#_qH_lrAzXe=dQ z658v1DT*dxl)`a}LMV7IS|Jdcq*08dNE9VughC+dQ~ZPBoo>_b!5SpG6J-e5t&h`q z4?=(JrNPurm!r4Oa{766W4mPI)iJb(BapZA=MI74+j|M!k#zSDc{_LR2>@hArR_^> zLnY)F__v7%B;9R5_TjD39<1Mxn1=R<$R0$2b|BsFe|JW0l%0s)ejvn-lxV!;B8Bah zG4LD9;VirC@0=(rav7&=8I0SydMjY z_xVeR>d;Rp^ga)Kf5G=iwBg`Kw<)5%qZ~4>^S6&~({IOs{h%gNKV<-N^`n$!_xXPm zruzMAvhdvB%6D%svVou8pQo{x54_jkZx4CjlsO{3_iga!^z^=FRAxWO2F~;+S+~?wr>Wu z1gg+*HjbK$6(mG9E=j#4`q|AfS2TESooGjxm(gj&0pT!-4~(TD;ma9r!rldF&o+IA zSPQ4Yih?>qoq1HG+Xa+Mm^_&!Sv0l0n7FUVlUjw`r9IKT2KW1IT}?V$>|` zdHy-UE>jn$R$Z@pM$^}W?xn{roRd2RE2D6IEMk`M7sxSlwS*e8C@RGVUaBo}Oo<(W zT)UlSgbAV7-J4dP&JK#2QNiWNUMyM|9>0G?3zMxdWvqRP86{n!18OjdX0S=#+;!u~Jq9 zbhL8FuIDy|axmQfK@2rxI(n>&ORXwQqAI0O1nG;sRiaAz3SbL|up56SRJ_wtZXwE* zxZiN!PYxrOYa2lcP|!ZE zpuBz6t!!k$i8-&wZRLL-^xF|fb*X!KhV`5vcy&58O9zIhW>1)aUzR5yLpS-q)Vc+(T~h!-=a+IVOWHm-^Q!6<)Zs(Uh6S8kJV&sQov zDZZF4IDGdIBka-uNtb60A{H;TQG@7ZP3!^|F3VPc_V6uQ*}gcMfw8ed)l(EG(!e7zo+d-6Nk_44efJHp<_=Akp8*v+-qS(2p2q(AS|zi4P|`z{Z_72n zT_NW$pnjh(-i-z;1_#d}e7FtcdFW8nS28AOEl9|Na98A?%MI&-|}H^7i>h z^kw=rOaFNF*&pq_UVcK9|9@NC2X6iCb^Vx-qYx6oQHsRSowAeo24rCvLLiFA;%>Yerrvrd_^)@LcczZ*_;VLC#dl~8N4tv4zAgT?GuhU>QTc?} zm0LFYo@Kj80RiqUa`0|w{H~XQ_myv}*}Lk%JtdfbgL^|8`S-Qf_FikveOGI(H;{=O z#?GnE^!}gV)laYZywS7qO<{q>!GhzjsC<%Acv*y|hB#@nL( zyTR-gHy)_Ma~;A$uliPK&b~pt?<;7C z=E(13`g_y5otMyx2OfV>1>jez3Vk;9=8L$24tIQniiF+}?$y|SoIDXO_QEMXtDRVc z0DL;|vBONhPps;oI87!UB!~#@6P5BP5+7l0byAI{pDTaRd688t>>{@wa%ThWaj+TIqk;*r(A3y}Jh8(GtKO zX;1QMjR=A&xYA&|PX)QMK6H;60>41|)%NxDiptwfhX4%-F(Iq7?gLY-jnoBM z4rF&duuW>UuQl4(RXAk_RZLE&D;4!6=Fhrzl`nejKKQr~8zb|H21h{}fW5B6@8|Zh zeS{VkA1)~72GRMsk(;TP`!e+vQg&9E8U+wyW}=-uk9dC$y0M-P%{^^^dpAESR+c1~ z$Ll9iH`P&NoTJu6fZd=f}6@soaOvd-40 z`K3M8)hOW35FggbKL+#@zFI7bov8Duq<)iv>UEK=r2_qfymZP`13J3k4bA#Du(?Ph zSl_xefro!60rJFRpB4jode%g z6%-Pkg`k|)d)rDvj|-ND83o?Q)cs$GD%b$#o|+8p&zxb2k2ow$Inxv$A6^??zK|iE zmJ@f->X14c&!9)mGPa^np4&-f7ISrMK%7Qc70!S5@?g|!6O&!5-6*+_;w6-yQi!nX zQD}q}RN5xXNCOKwnaQa$hj`b4P{k*36+AFSOB*vUjp;KG;QVVA{g0?8t4VLPv z9<6^Y;Yi^`%?80OxGZJw+NF#Eie#j~m0|X{**jf1=@&%@N^w%-Mt4bBb19 zz19{~w=qjvF^U8wj2e(l+@2IVUl%4{${wAGbTGzqW2uFqU0!l|=bm>awL4yP)i!_I zph;lQzMo%6`V`-{6<`7`9-PQUketI!gf?;kdx4SBfqJMAN%30F53i!H1jkch^x()6 z$rIkWzACWcxmN((k7v2O_NaFgIyXE!Rq_zwwn5IG!KrkS-%!dy@)=+HyWgNl$gZ3s#KQlMa-Y!Tk2gY zz3WXO`#xxn?&&Ei-<6oDH%#6e38~!>8Tx`W- zubq4xzd(9)(U%y}$M9~x^FDu{I}^D-D$R591nmAsU+T|x_{F1IsZDxmLSO37|al3%rzHmi_&zfq!S&->(|@cb2`c`lIX&_`L4SaYqhh&AZ3&%ULgId&4*U7EO?pdnOihV!T|nSF}DB37=5(hiPNtO<+1X23c~?k%hPwf!z4G z*t%V5Ok`#H;FhAx0Jm3ahP^xwYlFX-_Qrq&8A0YnK9kQOOEr#%8wb(;oiP0Nk^3JG z!~fZwe+|RGG2agfGms)MlpsiofN_+faBz#s6oOLt?qT|6#{qx&R9SE@3CRB&eE&@H zJ-X%O&fbY=PgCve{he*!j)VCYO+WX>L-x%4moSdR`%EOVYx(UHzeC?{Q3vhWHwxT? zA)4*o;=3C?`im&MyP&)cPWBg#_brAkD3j!VZ`f_~pgje*4S?Z2?}Z`z&+S68_+5LP zf2`&mm*ICO4n%+Mt>g%}Z&+mD-{#4>y&(o;zeQnbFnrcK27{_g34?dzof7)LC<{(| zV@&7$#K6xn?IWap@qPRIkOq9jv>zj}4DI~>r%1dvX0Ja-V&LDsfqbkQ_;;55 zvFbey2K-$f>}$98=|bSHhE9(<wLRqlN+;SrX^nrg8E3K&s|Juc1W<}@X9zygg zMPhR}aJ7jWJY6S@AI`EVD-N$N$9WyvN{v`G^Fbg?IW4yCTAkO#lkeQ8Nn|#trPnF5 zd2FNmun8HaLtyu@l|!y_ug8~LIyRGs8{vJMSUh)yYX)9c_S0$D`x21KEZ)8iSJ2xaN6 zgVI^0A~{AYAJU2iS5LXRKHZ*4U$~5_0+f712*RK*XX@^_lU;WRbiCi%8qNKqhZcpt zTSZV;EMn4L6*mDQjvEQ^Ug&@RP+{0@s`ZC{b^3#snC%nF*ZcJx zTemEiIwiAYqk1Gyk0*5XOgiQCibNoYEL&4+T;{2fTd&TtY8t=1&d{TyPJJ=-)=b%u zbh0QxRuCn<9!Af~LOBfK(S5KuaKYsu^b-3H-!O>rp0#E@ zg{ljM7b;|1;A)5B%(Hi*o9}g4Z4%;{Rt9_Z{EZUULr^U$=MAJ<*~emL&m?59K%sx> zXQNMFymM^OIkK+jVYo^pn4VzeLE;!hW%*UK?F|ZCpJwf76!KzUkCPiFFcXtZ_ z?ithLggy$PXk4XRtoWCQ4R>kbpXPs5!TeaAyeKrvYEDSc#PBeHBUZ^ zV+`ysl!BirwR~C?X3xnCRyUlAqdV6|^H$tb_Xa0I^V+_bK$izBGcS9+75DaA#nBI#EKB}S zhxC8_T+#WP=X|T-{A~OWI1Pd}nhfunhB&#)?LZi^EASA|m!FphA%F7}(8hgteVn(y z9Qu~q*%&iQ?nRhbzEj!VtO(tEj-z*3XY%$g-W`yBl>&;hJ&Od!JDl3)rQ-eH#;M_C zPxWNzyC{kJDyReEZzT8jXxf&&H#xk!%yvpn?agJ_8yIG9%ck9_{%@U*SaSzw+2sSA zX|!j_#3E5OROysyo*aI5@X{pyC*$Jk>U}4+EOGReO)u6p5#M6Ve-@Wpm1VxGXa<=NOp{-B)LuUliRXe^#Ksv0~smXB&yPTp5g>OhzyBlO)6?HOw|Dk0 z&Nko10+&9S2U&l;KWodP{>R2G4flU18?$`>Y8i&J2P+Z&KK%XW_>a){=d)i!p9ufx zxQT3>6eV#Kgb)x!iS3U6dDc7Gx9tdQ4+_a!sD9&f6uR%fZFCTS+h>x*9&V`Z=Pwk- z-kS3ivWLp-i!d0uZ_dSUO)vB<_>fWW0`&AY!|yZ!?FHu%Vqw;w#b*J>isemqa!cAW2iuPw%HW5D;XNxI9p#`wD=KiakLLG169 z?)SRQ<=d{H!kaWWRD`JCK=`xVipKvO@@nO*S?0ulv(y+k%TrtztR?5hIHvQRve>-Q zxt*s8x?+EUXaTqtb@tx1>5Q~}-9IAMqH)1wI5MKG-bcjx;n>G2{)|}v<|=@F89%P# zPvf?~^=;EQ0Z6UJ%3TXg<@~^vuM46V%*Nnf0Vds9_-Gi@<00Nx7R3*2*~Y%#Qa(;; z*Gmp!Iw()TSaaP_93D5lc&V4_icvfw@)%X6?!|wHhnUu6%$z{@>YXqbcF&Tl)+I>_ z;DJau<{1Jnc|heRucDY~XJ@Tq;UQuqA{|~wp5R|p)Y<(pnOZkH*aO=-kL|{dJpLqtZ ziMM}K4CJW<>+x zbG%%D`7HEZMAYj9y-xJTR-gXK^#@eYv_eIW8`bo$e0ZlIv%B64wG8UxQ{PC_OH$i& z37vr_yAWy{-&!O(W1-fq_~p3>i*8Txk*|NEKGS-aS^c6*4=Ns!o-;?vg+Smk3BzZ5 z69BFk6?~@dvwJbN1tR)wK*QFsT)U@^kbtDk=}L&n;oOQGDnt*LA-B-lJOkpu3rIc# zoHOPWHedVo65PseA-E~hSL#q=>KV3VH`H5`zIx0NJo5OwluO=)6c|5Hs|@?NSO9-D z>0M6Xf&#v9#Kt36vKKDZii=_Tglf>t$h-gkU}P1ZyaCBeJ+OPt;|B7$;Dul#WVLpB61U zb!n5^U=#CR7FLyJAbzpy(BONqH zqJk+`b7?q4QA?cR8(ELUhJ_O=ef650 zx{KqT9xb*kG3IvS+{M+7g2aDgPvT~TuBx2i5e)qbsD~5`#bI^|ne1ABl}|cn_+cCi z+@X0)uGF~h?RueFRN4N(>Ps}7lK!GC*hMPbMK*xP^ic4AI7oPT3tA55dCzX>AV4}_ zSCK;p8P#Fqh`l^%?D-URusYvU4s?a%;M#Lu0w7XR@nujJyD-$dWsZN-8d_y*Ui;>L zJ~Ra(9iIjfi*MfiY~v zBJuzuq0S0mLSE3~Xi|T-;HVEwnn8r-1E$5K$`;G7UaH7N^849oPazdj_h_fZ=GM<|yA$P;LG=_Hmv6 z>N0`fxz2xenZWN{=fAql&n@1a-$#r9t=6e)U)3|0*PVcvOBp#6JM0M;ijIz;Y#Of8s=Uk zJANvO_!M4lL3wy`r}XHp9FwFkfY=+>+k(6M^7Z;)U*%-fh;oom+N-Rp>P+bL8Eqim zL7A=}oO^#uKO8#lj2aW*__>k*bm?KNVrA&wkN)Jhst8)-AzQ|W!aMrOf1C+BX$6kK z)(7tp&~1K%xka@X9pq_t1A8QBtofq|Q@u$NstU$Rl62`FQliS5M6)UceOrzd<@U*B z5ohK6ymsg-ZwsHv%yu{fO6MPpUM}M2hzc#CbEkg>6yX`>4I#+f;NWRE-3f`JFIC)F z#+lQ~i^{A7lNEC9Ks8_u>ubH-Z%%NKPCN>-D{&U|hCt%bY&r;mnEM63cJjibv4Z$P zUzGE+)uxxl8*7kHz*PplXqzHE}5vgy*C67+;Mm8Bp1qmD?PsGE#h%rwVqK z?+t|_a~M=z0x%bnT3_+Sf{k##Sz~fl<)?Ho2l{0t#tp%)ftgsjffV7{<^@W3yYiN+ zij52me}V46Fx`UPFoF9;Nf`9>?B|^SJFBWNQLsm+ zX~Hb~PChCm=i?JKi|mzVwH!J{G$@4%*GQw=wb5<+BSElsDpcNF=ko%ZKrJ2*XlY9Y z6`wJyhvHP`(3sY{6Wdw0jMclu8;VIf|ctkP4TuX#uet_%>dzN^m}FXTOw z42y@qp9?}cK)Kp%l_S=O8urKP|+9IMNW*c=51k}`xZ`|JQ(#5TvZOxJ}uBMn{Y5XpBpe$Mf0MSYd&(b+ceI1G*PRkE=c3Wy*g*^ONn& z2ZO|L9~q4%yL8I-m#r#uDj#1f=+e~L_7Tm>3@WQQ!A$2X;N222%Dlx4Bxfi@rDaJx zx!(^)$sWyYm4XttY%&iuKV%PiLI#JaDJ4zItv1EGD+yfjl2wrPs)77@C66S1qHl$Q zogOer_q?!SX!R8FrC@fa*A{=>7gWRy#=FgvLSIguNdY|19!gj#3%p5+JGgy^8cvf| zRUz+`)C6?T5{_&i3~dhk#sJNZ&M&KSKEssokPPPpEEx;mjk}TfQWkUM$g4L5hH&4Nqd9fk}TX-eF(%-b};N z9T9$MImyUfodLvlt)vYRM#P>nz|hb2CeXb=goJm{hrZ<`c1PbY9s|%`ewQM9q98(d zWw>qbB;B|7_A(5iYvfz2`jT; zL*ukCUPg!e&3Dpcw~zyOmcLQ$>@B|o6MMEK#ebz>m%n9%lDBFd{0aQs%hzo&7~6BpIQdTcrtd88FI6zGJ zUA%J-z4^Wqu;+y0UCv`WaT~lRwc)))8-KTd4npy+q_-;z{jJ1cOjM& zBqqXy#`EXxDqr?kn3`>rd3$#9rI3wV5E=c!FRllASP}&&sVzEd=Bb(R{zBVNY7MD> znl-9s>P|O&d>)-@L-Im)Y?oK*3fSGOi}iMUfaqf&sZao*^C`G;*SbCFoW}FNYP>rC zP#$J#95V&jGN=dwvqk?0|L5=#_>;#2P=ws$V+m!4kguppKJ^)++&5uk5KTReSjp-o&_{9so5v5`7itt zV9JPvw0dlU+fwD!t(1`Bh~eYR9z?d!%46`59#%-8=v5~Nt2ucu!2TzVG&=3^)<@L* zmtEM#*RhOi%Q|n{f1Fj?5e!Rv*IoY{=>I3@{fP3vJ?Hn)odO{Qr(lvm2#UmiF&sf~ z7{*}?K_H4iD4fC|9HU4S-Nr!3r<%^>E`)}@TeG+Dxff$&djdK}_LlAJov_}(Zk+Gi zu~htd!O&aUEr<7#pzX$hMthnXLf@s;U%+$rwnBY-5G3#5yUU`czb+Ue$^En~Y^TXy zUA{Y|zSUH>SK1Xwq4!pAz9-;+w`bvK-$>5hh2r2Fh$nBC)NOo}?rC%)-c?OE60qGU zc9pol&z`sYmM+?TQ$36qYO$|K*ZN0IZB>JcQ*N=a8A zTbPoIF<#}_GQR~$ur@4D-&VnBre}$O11b8KdTIKlIr5}^t&`^1+AzI;P;hUI^x-A{ zSz|KrFSl%+Vlp@v)w%bM?fUiDqOC5bJ9VUq)z3!rgRR-YMykIW%`?!)Dp=3c6{qqX zcVSG27hJ2NY{zCeAV!pF3(bqh(I30852q3?mM0e$JdZ&m^q*IF*JFHR7$Ehn_U-&5 zRllWj^i|{thR&kns~!!1+|2IDg-q;<#;Z~+PyoFJx)1f`R;v#vDr}+{&Ab1A{l2=C zLfy@Y4>}qSTT)_jkpv)Q+TnJXU4jRK9hHUZxQeMW3n;*o5BwUsXh{T>iW{RWjJ~Q%m2Wz0~ zhZY*dq!ABo2aGI#sj-OI^mA~$8!@&jl%r=d*PiHLB*u9hol7Yvx;ZA|D;EaBwB2rF z7vL0&rYPmWJw#SdiVb&dna4w62-+^o3BEz*{RH)|h|DO?PuS4RQT$lKw_x9gKiUGdUZBD|(5 zEzkJU0$|o3?&mobTkUlsUl0k}snI2V5JtBo)u;OE=>s^g#I;UluRoYM-lrFu5aF{y z>qrE!3UeC_cOa_UJ(nu12Qhzc{GmGQ?o2o8Bb7}J1|d;3f2}18J`OT~;F7MYNKZLE zLxp4(4&$YHT_3oMsYY990_T)D{p?M3s0i>e;L}NLfP?DJ*l0mZ zTsw{LNpjt~LXx5)5v91fq9PK{*TBizw6zJ`x$#PhXF966Vb;nns{+?2sFMO_*lR=> z3!UF8<${`hG$NxZS6Y8>$t!``9T+oH@_IN0kDyoV@njd$V?))d)kyV`^l^Y_5eIJnM;z~}KuNN1mhSu)-t*z{ z=a3GE-=K?nb8B#~qTKgnk)3Dngd2Ga*}sJ`;q=YrseLDR!(-^LjO_Q^`&$k;hTip+ zZ|99|B{6^TKA7)2x6s?P3XFCzN2dFO=-r67K{|}wiz)H9ZhibV>xkc#ErY_|hs@Ys zTYOBu(btVmfu8MEHNWA*Z34!z+|+X`%K4`J>&QFc!n8G6HR!AG@7H6%*Mn2W{p23I zEB`uQNaNc*mJbcbGY7ZQbcG!?9r!lM;5ss@mBFNO z)=7V}r|DFDpjMsPZ94$dtt0un*=0c&+&WY&p0c+I_REtzPdU54Pze5$fuAGXgU<5O zjk#VuY&o-iu+{JPk*VF3$*yE7_1=B)z$~WelVdUI0|r$I=wlfdX&m%}Wescis-G)Z z*RhksBt^@Y#=>91j-_!cL>J9;1gtB-yPw!eMSM{ND%gdbul zhQT;Q;3SFS8zlxYl%Oz@#0d;Sb{{~D#5O5dh{720>6YlmVK*|1fP1*vI3u2J|(Jl>WeDIT^t|DkwH3{Er^FA2v{6#EX#D8Gb*RLNg z8NgSYj2{Pm<&$n{!e2uortNOjyKyx4);-@t;;!wx0N)*)te?ct&B}edOn+=b@*n$% z!WUih_#O;E0`uQlR%;Fq{y69>F}HOt_>{l`zhJQaQ!*D^)oUz%4(1Z1kG9gSxP9Ez zQzc!_DM<1?>tzaHM1qH35`QqA<`*WoseAKn9v(8(f*%6{D_TwJ_XpW$jb50{q3P8S zEwS3Ii)-+FnIbTXvOr&>;}b7RnuuJEm|wA`s!$XWoN2+ZBfOk4$>KoxLbY&~qklgYiWN7mIYdX} zndhR=l;wwj<4pn$|{A+Sx z2N|7g9#|j*9>i`9^O?QZd?PQMdbS*_$e>y%n6uc}epr;}QX0z>$)*R(S;V7oiMGpD z<%537etv^-WS`twA^Nd3wxIPPO zzypV52M4KuyaElDwo#Ae7W-CeK2ecM>nh%`##3%4hwCNBWe$pVIW@9^m7RSrZmbT- z$M&8i_k6Adu;mfBvf{*Q=lpch=8Mm3uy|yLQlSoqFm>_pAb;cwk@|B=oJ&}k*B+SR zK3tAEL9)H{Nb5fHEI3xcRn5bvBrsc?`^k;=Bb9=GYM1ncCWe!U>4z7 z1jl$5?l^K&X+~MguI}OLP*9`aUGL@q_vR?wbL8Bd3UL~XuEj;JRg*gDToyoobVK>u z{^XzAlYtMqOMgSlGeJeE)0XKmTwJt_E_O}|&4wp_NnoWJ1&DLNs_GEu5Ba%Kvt%5K zw5{aT$?xNXZ3#=PvZr1PEr^m2s;JypaV14JW`iOs)GGkx)hf2ZK`%0YMEZ${%I&?C znin6HQVLecQH-&JjfOBxZfXAkKZs-2!)VL(dreQ9~8{8 z&=Xc4zMyv;KH{2}-A$*vYw0S^79-%k${)bl5&*%v-W29+FCb$0@|n>`*&IwtK)5l6AKc173$9FHM&VeholhD%6AT(Y!|<4{E%QzD7qK z5#@>&=Lvk3D|T`zj;x=WBc-ZznC53d>;zUS!+9e-n?*?NXVZ`#N^#MV;}gx4Wamd2SWYw zq+dZO{&NULfe1oU$c9D`3c^Sffk_y}C>%ow0z%X*m+J6WB6Zo{FC*U{Wld!|5U->%%Uxc>ognLTo;c&~`dzT@&2N-{Crh1wu zeyW&qC_BJ9bcepBpKCs~T>9$AlP&58Xkuvw!XKKNEtq|0^;Vujj!@*$MVZ+coh zjX<^u5aBr9Fq5sesM5svdcS|IFpG)W9cj!`|F4=kOny1bF#@Q6rl{JJ6Z zKj4C@!O*H>xwi8M48q`UbM;?I+ceoT$^K~C`)z2qFJ|$F*u>J$@$6V1p8GAV{dC?B zF%6_39Hb}|Mq!YIDTqWdltM8W`!t4qF>%M=dIuZl%HCUP8$;WhkKe{D8#e^snay-h zoPHX^_Ldf6m!g7@J*Mn|BiS2+x8wWV(Z11_kGTgIf0?@~!h4%9Mc*}w@V-LgZN^2# z|4pEKPIilm+wpA$7`(^H?Zo`8(zQ*pF;5uW8;&>Hxdr1bylqU7ivE@c7CQ}uzhz3* zOZB2I<8Z#!BOYe%r>feH^OLuu>6x><(${ZQAB{T%Ftx#dZwX+xh%eMD@~u6Wxx2YY z@kNpMJ$MIv4S3uh@Eo3EO?P(P+H*X1?<8&Zl*x9wl5DwG-E?+GuR@P!UHo>%P>aBXp@tXM>?@r>eMrpf=~U>lzO{ zf{)rdPr#zw9M}dg7DihIF5_}1w;rxOk3FlMPHdBBNoSTXwh)avn8^)GElb#y?DmC5 z;)8Mkm|6<0z|8p`t=z^ISnx(WZ8pm{dFo!T*Z87Yl;_%>KhZQKc7ud>mY#A!Zrq|d zQUE)$%Ln^9(Y=LI;_WnlWfrbKg;B(UI0YLW(kd}&k7xIAP6X5~>|qHM%CMB%AW2On z0Z(c}?uO$=(`)tbtEtImF#f1wBaUT!H|4 zszomMK%7amlo>G=xZ{JC;-QJ}1d6p-jBeGHp z)B7_ZeGXowd3}ZDc|d7WsW$gimmj#34_kXi4s zMKdQ1#XhFsT3ALCT&*w%4)XCtTb!XJXDnb|#T@1wxGb_3ohZ#%O;Dq4GT|}w;6n#J zJRy#wAK)W@;~X^l2E{j^8k4rK6ePLn_jx5PAyzjmb2)6EKEXZ@+igETUikC5fwQ8Z zGgwNxAc|3C$uojf@ls?|>q~)Y zN7UirWZQRdwnxWc07pJz$i)u@4L&Je38MXldzg)X35k;%5FU*jM*65(L0*8a(59{e zV;4+f`Ik5?{2gZj%{30tc0+F?_yu)+!N17UG_#r=1`t9IkpC3UkV}HxmEXQs9%p4u z$){c4Vt019S9bt?-p9pIVco9r^!yZZR2G6RIN{B0$=pPYM`c5P?mCYtYL^h3!NcJ= zrXoInNYC;8Ynh-xNaAHz{|SmZv7+GA0`RTH@@LrOr?9L zijD1$S<{>a=<=sY6VRf=%tcy5a&#Jtx`6tKjDA)8gCaOu7dFU~yq?*Pe8%kgO>o6{ zKS#fI=#)n3A8Gby%~`V7oEh$W#5UB5ZtKroDbfGIdA}t|e>LY1MQ_wbK0yS9KoH!C zC;Dl7$?jQ#?;R#swC`*0wxn+{mKfeST8!^dYu}I0_98dx^Fn4U-fPNm@{P}uz36N= zIDN}tLD>%Dwg=JM!Y@zuJ(pd-2O9zMmwCSjDu3U#Cw9t7eQPg~x%6C);)+EC|0z@} z0`r@l-L5L=ux>{Myi0|Viw`9;rU&~R{+;z zTz=<+I%}%wV&dE4PEf9qzuloqy7;;R%@N)AtWJ2*p`>vOcb}~BmhU8{K4eTM!=XXV z*?%FPhVC+^fVOT2cW=LsgC)ycx$j&DElsi3VAaY3(H(yJsi0Zd)u!(n>wajQW?z=N zm)cQFbg`}>%T%HLLp@&k);s+wVgtOrN~VEvs^G1LD^K!Re<2+3!hIqP{0pipl$=_K;@LaFva?iB?Z zDv)~)wqa=fW3*DEz4?8%&IEn|4HZ}uxaHvIi4$<_US(%eZ(z02 zKEoTjL+5J=wo+QD**SO8{${Sgg}6a2=qOK#Le30W67uQsc&Rc|P3*ColP0WGb!Jc7 z;*U^}f}B??Rvu4@H({qVIs*31P$`dO0_r3s%MG%pCv-TySXIJS#gS7M)_+a{%F{<8 zg#A|r_AYml4yd|vy_t#5kr{6U;I*(g`uJv9HUV93LaeocS)3xc$AT=HdrfT5$#3;nfuWwMfYM0AZ$M!zEJK7P>%0z%(WlGPx)_*gsC2Xv=BT(QU zR4RTt#|#`4&3+L0vk&u{qjwb9Fk!XGPPbhZ7{+d@=(>`XmEc*t*iav|&6{(s)TXoW z!#-UBT4Iwlb-aLt;C#HUx`fmBExO)YhEcNXOx3X)P+#WN6}& z7QlPPT@Ghtf26PX@PCRppvY*cG0m1mrxtzNxh4skZQd)!?J9CZ!ksW4cP>s`w2w7X zfR`|I!t-LGo|ZQc#z%{lCC1#J(N(5)j;Ft&9lbbQ#|5-H3%$j9-cvpYtm>&dQ-pv? zw+zzMpF}%x1)**n#lLJ;yCDzv9(&rFR~%9o8r8lp?WHiGz<=FtP&&Sy6BFiI(E+!z zDdplE;pVDzO6jU?7?gQyNU(2VdUUgI3ONm2P)xaL-TonedfVv8G=OY5com*K z!CxQTP}nzU9)I((=B{akk-OAm4J!*jnCd0_89Kwlr@m#yc<&Qu52A7Er0sPPo18&EieqEtGf9;ucmHX zDwt{1a4qvt3U0=^CG3Zi(6?3f%eKyoaQ^!M^1yCm{TqM&ug}cc2jBI58n?f3;Sm02 zt^JVJ{cqO)VgAqWjc>>4znEV50Yv_%@BI}>{&?R%lf%*oYp6cf>I{b~Ipw@B!$skFy8*kq6x2+aZe&{}fbaAGNgbwrpo9L09%>L3N`Wzf+ zBptuC)}<2FcoJ_p&vV6U%z#F(jsCL(3G`!YondqQ*I0jmE8p<6ZHIZ2#G8-gvSTH* z+LDVq#4BrGOMX+l;O3LlcMb2D9+BG1oRM^AN z`Hq!^u1Y3rbG(AQsh0so?c!f>vI)cB*|vl}P_&V^*<`4m40dW6kh zx?B-UiN&ds1q>?M*!8O?ZuYYHew868AcJNcJ$aDPS^-E*4bV)ZS*=NeUfD<$#ebVu zm|JpKdc*yCa_>OZ+JXWB8|T z9PVNISA0J`29EdL0w+J?%lOgRw}=0o(TDUg@=S(D!6u=Pr`b=7Kh))h&PIHa0*9RtGb@0sz>D&l#mU#F-d$F9iYvlq^sRbtHrqu z32Kh8R{(oYP=NzaCunzF=Tbt?q@5y}TuVdZaCHH>oDC73@(=(@c7JcOTP9oZc#X8K zs(r1In0R`7h&+<7oTpw@bx(6@X%GgEoN!PZth)?3$SV9(Mgfs=H|zjYK&-z}Ek@7v zi4%sUu*c>Wd%qliW+g1>A6YSVHIc#5A~U+zKlwJvF+|<}#;|h zS)wVYf;aSnZ8(Se*=f1GVP}8C)gYrts~@m?-Fx1QiZevt>Sy-^NH@`>QNGj#rI)r* zX0BHhvV%g)dp=uz5A82$U?8SL;+LZ3ENzzj@IX$LfdMCWa|hRy#p?B@ zZ#%x>85C1zCnX#CRNUdjOzkjtUyoqZ?4`nh`%<$MqMl|7j|$M4yj(!|jp~_eqg9oJ zTX%@Jd+)_zzJ_Jp=3$aW+3#DB`=xfCT3}P(O_;1=6H5?`HYR_BrdU^1KKO#_Q}Mj$ zFGP7DriYs`flakGrs|fe7X!H#$$)7lfUCAD(at5i1tvs6kQ#G{`R=dpY^Hg}>26SG z$|<43k|pNnjyn5mcYA0V!I`+D@&<6p8e3sW2XgsYiEd+g$?oF(D*E{TT1sj0m((GV z=6ckh1QF$phzKolh@su2ck6GUvUC8kk?J zBN=2DBRNCW~t zVPfd0{Q%QHs8JZp+P%F-OWejF@3ZdsH!-2XUG($T5X`DfMscehcDq4=Dn4 zPm*(3$U=YeCZKNrCGrTLP1wC36l7onL(2C+BhcD%!!<*K>zuS#)0JU_Q9W+tisDL=o$dSwyya;Vr@v_*WENKGA1X2*w8mFE0(`g4vm4VNRc-zQ_xmPg^TWOV*p9RB zAQ*%nlNblkBQcahAew*?0>k$MG=Wj@{tZgP2!wx-KP~!DpNdIDA8Ii>YC?%agli}5 zJJ5=#qY{)+$7~t;fl|@WHO1oWkoqS-+OSY|oW3*Yqe&(?G!hPhf$W%=z@kHsAAvps z3D_}i_jQ!+PaqIU4@?O|$Hd5?DjR_L0Sb>nl%1;|qbA7F{)oYU2?G10=!1Xb=MFz#U94o)vJ5Xmog~9`B>TEQ zfvozcBG5<8!2h8$#^td?O91{lGj=V7Cb;88V6ivfGhY#cKcb8Csr3LqK}JG!af?LU z0Z&~m$^H9wK~?329T-(#WM96wKlxVF(YSwud*`sWImj2FXZ-P4v9Iw5Z#u^=cvR(_ zuTy)YaZjqK)+&j|zpSbj`hEF7)^0xXFo49n{f-KuxIX%j1URtyKxN~v$_9ojx}*EM zRs2^$hVM%>z+XEK=o$pBG%M%{w*z+A&qvl7SlYjxvnkmz98RSE>9HY`f9V`58}xtj zDsHt}KRF}IBZuVd;PGDIJC;h6b`4-fRr-342&nA!Zb<+{Hn{dSL->w(H4i1_5{jaN zbrAi|n8A#{gH4tVQ>$Oh$u8OPSx=gB&o0{;&68U$0Pg&f9Q{QY^y@?lW60Gq0*+lG z`tC}y7g9^}OeS~C5T~TYMu|IQs@;Eak1^aL*jEGK(t2hkXWrUr)frT=lc9`g7Bm}G zg0$cE=F$bx>}X)ZBzYlHN<1@nIo;pS3?p(k0jQqf)rgfVFWKDLvSg8TbR?-zb9q|T zg^d@yD zBZ*GRWdP&0L4Awt&QK*{!JE36qlqg|9|2OI%IsH}^ev@wj}2U1lmP?Ac)fX)q98)P z5YQty?|WaV7TP_6`(kkmD$J#d=u#;H)Qa!*>6)crkfhHtC`M13>|lAqusS~=a9{3o z+u+#_1EW5Bu>{uGo#&-EqV<1KvT~r9Sx3lLGjg9zRZMRS_nYlLK3hNRId5_BI#7e^ zWK7RX$dvwoOZqv#HoCQuZMMxUKuUDFd7!265?lH2XSG3w2o>Hma+2~oChs3OPGI_#B%EqZ`v}vVajPl*iRW0$(b4_48l{)8XI8n7iSiQ*DG%f zD=)nIDoUS_>j{5gFRmMe3i24!){8Mfltc27ptdG&XI~;Cpm7nWyTUn+C`{5`I8HjO zA_E!AH>*>?x9^ENcaoo7mc+qj&4hMov}YpFMnz7bZvf}%G8eAN6^uo+8#rNwC!Rbz zk2aHquJjhTaefMhCar^3<_RWLdHNliaI zhw%Vz{eBG95BTpteCw}J@PD|MhSLWhe^avp0zbUm>FAISpon7-|j#ixzIlvwK z>Ebz(ZaaMYd94OQ4t)a%KU%A^&(Q4QqK6$Q5F!rCx+Fc|ApAjh=?Bgw9|r^CSGAfQ z?V zG|)%&>G0s&FZ}C-;Ojsg*!nFYc#At*ywMy*g}0K*pg=0I>}P?LWBVTyg2&vzA3&9L zar+(G?F>xacLD7YT-@RK-oN8Veyajv#C^oI)=GRe#;Ey9ME8TR;`IPbe5szLub?Wj zc)_xqqY21Vot3p5=t#$XMOW6Rf0cUqy}^4qzQ5W0tl7?X`_kYIe;(hy9Mj--j<_Ap z{-Iw9mVSV-k1Dz>J1(pWi5Ck0ZdTEK|NbJ3!I`xJPpbgmlZ)`o%2A_1p=ZJ$@Ytva zLS1Bzd47el7wEw{Wp$WOIkupF^ECe6&2LrzNgt0iopFM$fO>YDsxLM9f7%#aZByGQ zm6!9%XJ}%s^N!)(kCeRcdlpWXvQc{hC5Ur>GKC^|>UPcpn0q;c#AXebCb^EY*+SkJ zB0XtePihewX=7e+!Y=SM$JF{J6=EXXeA;eCUf@s2H~}HuW)O8ZpTbx^gR7aRp?Y(l zIb2s<7b3n>Jw{}HlOaX7f2Z+Qdc{kK@mwS->KqT43cR9Q_U_7LSda~rUID&u!a<0_ zs%#W2csmPa57+Wyk1MF)!aGn$&B8FZGy4f6HA>C-$l+%=0EO zWyFQ$4V30G(=eU_S$(^|DHAd!(&eNV#+L8c2bj?@A^#c)2B-RV#wyJkV#%54;T;UO zO5fNbc3Sl(GqX~xsIuIYu7hyoFww0}0+Vd0FI6-hd0ZqfC974)-% zJ`{Vl93$v-K;Et_?v%j zszLyGf5k<551F05xvKPN$$lKyyUr(XqQ9}l!>cy^U9?Xpoy;exZ_1{Mu9OkORkBm^ zWdrO*Vw+54>=XI84c1j+IfALOnxy#afU}o|EnkJUOfCOT+49a}+rek^!0?u^E^UK7T&lz#`7 z8EkVEi1`JLdfItKL zB0*+-{r7O+p$h%~67Ks?-1;ZD@5kHzQU8j>VVtIL9HSrzhba;xF^I%40)b!*$8i!x ze`$jF8So3!BSD76NAnCp{5Oh!#IHV3korjS)97a&4m~n!5%hCof%s?+k3WJbJFeUD z7mfTb@qd(YKW?B@^1He6(es@i*+2;XZ;bi{@I$i$s>SgEO0kc~_Musw9JT60hMGKJ z9`VUk?kDWGhClF?M2`|Q@+tR1*#Qpsf9H$C6>$5pRbpfNADeXkG3YxmAMkI2zAsAE-@2ZE4*HJb?xOw?^c~0M zoBIXy{bAPd-vNCG<^%p6&}TXt_qDJK{0sE`T;J-OycL&A;QNwSgG{^?TD}b&e}kzi z2`I{YiEFrG_*8o6Ybe)aXOXX-Wje`wWUK3ElzL?#tT26d>3HW<&Pdp-Rbz9`z8?UV zEvhw-=e(c55UMyOe~S3C({*A#OfQzb%ZwX$>0|hXycO`hXvbI0j@8_EQ}cRaHRle6 z9aYySw3VG8>f1jlR02$Xx%^q*e=26wi*Z(2F?~-(Y`4$3%b4domQCYE=dp#3#eC&G zLz|C5J&Wia=P90RXh!C)Z+k=DNWz!LI+tbzN<)^4%()e)H6QO6o{w74n9l(LOKYwH z&0-)mE)|0$6Ce|DCH-xvZR zcH;VJEd0Y9&Hv&)-{xli-Fy5YL~*!?u}GyQWEXW~N( zi$o47uIv+D533mHL(BL3{N)_EUf7nFNJ(`k&vpAXhJ-<4E=ufsD3M*bQ~ zZq8p1l}LQ>+5^HKUPXBdYIi#h9;(m=R9z&VN7TaKwEY8i<9-)_e?HU!3w6;xs({HC z0qBB(fjyvqJE#i$58_;|tvb?de3hZ*s&6AbUnhEiKaKP}*-PljgYU6xgarF8@TJBg z@tS579On!P4Ga%XVk%-Y);Dh> z-MT)OM3G+@Z@yl4e>a^5i#_8~uj2z9z4DuN1sxN=Ts^hnF~l%bAP#%l@i3jG>4J5) z*MytXY}M!aYKKn5vHEo!j**mg60TZ~l^qFk4>6-VfrI)qy8u^O8ty(!oJ-;!y3)BT zLVpLVgdnM^wt#Y3Bp{6O10GvA#$gaxXOLb;CIp(y2IEgae`DyGEwKC|yl*rrMe=>M z6V|d@^tNz&l;+P`;H`{sak3$l2#fDu38?w+{v}b83O)djpz^6>kqnJ0lIItko+WsQ1fK>-~sCKuy&KZSX3(lr*fX_ zVUe2zH%Nl0e>Ze0DUg_yK@}RWl-hXk)X0ig`Md%FX!ycQB|tv&A@6xt%@d)_OXdaj z;A2%NpIHRaK)Qp7TI#yQ8~x_lz^xMM&GWs}AwKH8YfvZrYF=o{dm{4bmQksmT;Q-D zA1fVhqs*R%>tcuCiiBn(h*NpZ##xML5wrnCqL#Zmf7z>{)PrzUqE|*5lq1RYNujS3 z-K@`!OI(3kR6Fi+VX6fPuvI-$};*%`R<5zhsOq^6n?EhFQF{>>d z-roDVhieJkA&1FK4x`tz8@@OgmEdC>3w=NDFR z5HZJfr-;SOH>%? zoh)<>Jn!~UpvGuERYe@tsRLN;>zsT2@&E`mF1yJzytga z6`z+-kBg^`T4z+dk{*@fMNuZW!bB7WSAu+Jp6W6d9`PiUq1{i08KT`r*1Wp_96G&p zJ~j2Z7|m>3Mk!~m5`9K0(QR&y9g2crU}Jh-dCG!k0cE?amdJ3jlr-*<0XX-Qf7&8x zq=+_O({ZRXrvGY{OfC;4Ck_!5aOm}93f z)9O`rNaQvPci0XLP4UVu@4NJ78EkOVJdu{zlD-qJbIr}{;m$X{n}Lkxc(vI-ITfF4De-`hC!rtFbD*$E?A*PbokCQQ~H6&t#|TX@pRAmwgJY%F(U8}mL_eNIz=OVshf8tW4;CzEA5 zTskK)6J)RJMo8k~{%y!P9nCX;pv|}c`*|EYKb}qa+qwNe|JQX$esnAOfBRQ`g_D1} z;(M4#pwQttLc$n?&^Q4>1cK4{jwLZ1KIUeQp==tZ5R}6AlW6Q`7;=YdhY%`!44dG{ z0rGalg`uAbn)K+h#y`G$@P`Rg_|NBPJ{s4D%Gal+bM)pQpY9*xBX5HoyY7r21)wp9!l|JxCTRxnVNm& z#}j%5CE-zaitz(LlF5H3=wDKF?xP2G|JJ7y)g5&YU-tCM@qoBmAz_es z4aM-#&VMvmQ)j^3eo(K0u1jR3daA}42c(f zPLz0;J9L{Pgoch`>~AX5T)e3^gYMW8(uChtruSpOcL(QxXEnh0%lXUIi2lqozWDp^ z;Mf)fK*4yPIZK?MGkoB(%DY|gs@#DSQ*bSD)xfH{8(v1sFY`PusCL$dt*-`{qE0*O zyh6YPJ|1%G_HgS)e{sWEU@|-hjkcd8vY5i-Y2=M~YUqf+WhX;N8AMgz>X^*0cUnuw z#+zbnVqtV+>%8$ZKZlxYv}t&*g`Te6=`zR)O=gN1i4^r=+rZydlm6lF&xQM*85q(G zxHy`)lPF@R8nayXT_X(_oi;ABywb%T+mHtf+NRB5YfA3pe}R1waJq#VZF!E5^bBw) zixG)_X{s<(`t^YnpcFhO`!3JWDaFOlX zKMZOAJe>WrQ1+8BhEgPc*pwbIi>6Qv-Gdmp$2FS7s6!|kqA?OCFdF)3LsLI(H;kxb zv<@XdLwp}qf9E|mP#=D|f9}(dB=Z-E=);aE{<(P)cF5in^bvaYXt>kP!+st=dJ2yW z-$!Wdut54W4bmTt>HRrjY-ioSGEWK*#jriZ68JIwvPUE0W1B?8AA42&$Y4^?aU&=> zO4=d(No9UiCqnuti|@w|ku&(nNgn!)pD?yRE3rq!f4?-#6Wl`*Y$LvfG2^5yJpJ76 zrkM;Bp@OzYN8~IN&Y$JMKJA9a|00wDLcus+g9rCDc>H8sMBCKm%LE?-e;pS&jsf2e z7A*He!JD%SK33WG>$r#=x}QEpF1%EuzZ3Lm#d^RN&GK6Cca=51DmrM%r=d~(X?Ng{ zg}D6y9$AR@^LzfTS2VtwEK&BH_h7(MXyfNL&Otl$mg2{N^nSaBd>vI% zD(-Q8n}i~K;-qoOr0E`qstqPpRFy^aM+$HYBo>x$M3%fo1{SU>~RIZMG)a8jY7IWM>m zS@GMManGtfZ)cQ@uP^Gvxi)j}ytq{+%7kY^-e3|Y&ykvlKvs3m!wR$;`F`8C=X{Rk ze`rg#H?xwMT5!j4XRxP}l3+aAiz0GzJdn3tQMj78n~^$44>b`k|| z*!Kb@#VaoaO|qE1GyLL~@O~klR*L10h;rtce~EYC=2@f7U(N`q)RuPHYEa#}f9Y1w zs(Yg|D@kg-W_tf=Q5RQ-+43sb)i5!X64RgNK;T>D_m7m<@f)_FygP5e&l_a=c^`-U z<-H-#ADRP!Z{30au;EXMI?D>v0ibkYo==uX$)}f`xbLR0Px#KQ>EiWv-jb2-jK$VE z1sUg9gRi+!PGlrTip=yl|F|bWf3xrdC{`tU<8nGdbC;^Cz6oYWu}{PNn&FUvD9`h3 zrzZI1vLUzi44(Sia;3|w2k%_^kzk|%Q%|*c7gd5FZpokCizPz$o)SBTye(7G5hk*n zZ>+XjD8V6khszKp^!#H|0VRCnXT^JJi>>t0qb8T`jO!dmh4DR%vZ%ZBe^diH%X%^x zvzMmidRbsh9J_PqPSSG%Y|ast{Hmm^IhgCfxMOow>hmL*Z~4})R4>ZS!ihIzvn$!q za@iY}`erkib;uI_Y5^AI24^M}r{3eTQ+h{IF;?X4zQ1=0djCMJ57;ng*Y4F8$FY0< zjCtWU^C+~@GS*~oK%$;fe^B)F&GR4TB=+8V!w5pI6VGN1J@Z4kv#}@gy1t>fT}UZW z!~K`n=G``8Js;6PM8la=xv)Q-y;j`3n+{bw$;j)4)1PFHc@-%PihtHRKJ}JO$6jHw z|K8|Y!7VJ)SiqUa*R@gZ&6*WmqtPd}ErO;eJLEpAxv;-;F}f`&e^uG;&t<3@zHsKv z^GD%*3z`zv0s9G&8m~X<9MfKx&>IISw#vCz+VPq@^m6H%#1jYdtU3&5Ck%!zq$^O< z!`(r}lr})mE}q*9E1@9IHwJB1)Mn3Ns}pg_AN5JB(EjfiekIGie1SC{vV$P08Pa!? z4qw!>4(G{lrP!UBe~zyoFw{T4`YWFL{>ty+6h&h6(UwQk6h%T54a3l3SOo3Z3MTe1 z1WLg0&$%G_F&V;+X1twp9jXY#0jT1S!3Fv83{If~@ubAJLE9h8waHO+h(7ie34Rns zsgEIi{83TeFU3DjkvmzW@k43#&;6nOF&c*Y@}RV3bLK6OVX z?iSpI)kpFie=t#bN<3x}Y{vcurufQs%J?gU@zvr}Q~2UI#2H^?RJkvX?ZD@t9WRH! z(Q#1asXx{fD%V!`Pmlanv3fm{)6DCFzG4S^rY;rHBd3nsV0&fIyNWouIJe^!CV8=Cg$jz%~W5{F^F11Gq< z3flCH?;JmtcbE6;8<9sDy|#BzvG65!R*cbev6cLrG1$pWu+E^;i`{n9DH_?P|=@H4A~uS7EAmHT_ZuNF5E<@8Jol^&i%wQ9KY zjT-_#f0Dx#t!`#@=I7E9&Bq|bI_=j934x()xuKt5J^QX*7q*4jjIokCFkG=FF~W&| z;&3gY)WHp(po>PDEd}8_Kd;*;$WWsZHtrCviJxl@2A_uN4(Dcm!vNS}On6IG%Qzts zJVZivZT^09%TrgRZ%psMe!CovC+El5HF;`Jn^4j{jEjiPgnjS z{{_(mfe;9aA<+JXCMldE4tab8g$~QM&#|92rVw8mgg@CR;!`0_sgM8p$Jq85^v{km z=V3{n9{Hl58ncB*CrXMP5h?wcn5Q4%0PG_ioqj}}_vmy~RFmWQ7gx4DE)l;n63U<> zf564*Cp;lX6*kI_+UTJ*7#)2sdlcKRk3Z?SLwf-FC{u^WEZWWw58Jj+Yx2>vf*i65 zdni2eVDx7Z@n6!#@SuzEHzOhAnjrmFD_*4t9C78eq<*D7_tTDK{l|`E$KCisJ@a3i zQ^K7h!cm>H3Bu>@F-<=3O>M6-YQ(EU@iGd+khB=f6|PNM*~ftudB$sSFD$^Y2=q_w(Zg_+C!>uhgVz76+Y1T{%%NCsTu3f7+OV zPCJhl{X|gI6@$s7@U7&<`ZY_M9PT?o<7QSx$oqrO%Y(Y4#C8_isWj~gLvd`FC%}z! zR9{YeIA1qhOyZW0P6^nCEmw48@LJL5NLNBkjN^jiFijh^fv3;?1+VrDTPFgjg4_%` z#M6=hW_&JxgBI`mvnEzF^}vJFe+a?%&OIH5*{`WYFa6 z){ChZ$Qwf|>L}ul*t1!tYCUt21GV*qD{7d7L#Q;!q&=)UXg*G5W#j>%NLR}!x&jgO zKJ}~Ix^Pnbk`0u(>G^hAjVcKdlU7-@2;+eee^pzj3RHydyM-C_Cj6?X88d4r3IOnq#_(%n1`(-x4FcWz z^f+Zv$(IGqHk9R(;Y)((h@td6w%TF<)48`ts=lK4TFF%)1j8F&r#1DKeVmbGep_GT za)zLS6{vJW8CJ}KtcVpzbEhTr76x>$mkZxD$^{&K0?2v^%uACvH3T)rI2<|4lMKvCTbtl73Lz_?JR7yKlBEjEW48 zfu}A>%%$CTsDgODc5n+Xu&v)7GhbUuzp&yHAU92UJ=LA*!XmwBL!l}9n zMfNm>TTgIiW;ne}VwAiUWq`3o-V?$W0zq#>FP*(#Z1r`o?|XPjE><`4^MW<4NtxxV zY?46LC(b7;IMwBgN97PLW8bJa7hpMAmKe=;JJ?w}jD(JMo)RRxiT ztg%e5vG7?U=4x`89x7I;gZX zhHsOQW(I`6f3MHpKV4p1aeWJbmQr|F^80)4(zb8q!ooeV?)+6p96smc)fCUJT?kFs zm=VvrF!4TT(9MUzlE|La{07R>jiRTUKb%N|!fsCL&`$91l#>`XJk6V&Pb%rX?w65! z-TRh z1i%PIe`6GdAt+6tBu?+2#-H-b9jqKi7u1JG!XM+qFE$wXF}f9o2eR3*5|kZ3{vt_F zL`{juK@_0po+{PxwR<4Dx2TRsEo7jvScp$)x%x3s^z ze=^`#OZz)3yYH+R_@kWO*I$&%va@z(7zO!r};IWNNii}wRmXG4>d)Je>3-6n2eN5B@M;zYDZ;a-6+hRb=ho&F%pPs z0U}9^U9OmK3DqMnb|iWZ6ZfJqXp!WO3QEWgN%9Uu>+4z+(4uy>@s4gFSp|Ll_d}Fz z{qF2~khndh=T-VeM((2%|BTcAx9j!%V@&$nSA7dY|GMJOK?g-36rxFlz+e)Ce-0mR z41-AIr%kfZ$D=HPK3=ZyLDr637I9S24h>HD$Y>?h(WsCdhO0jfI_XC$1dhMVryl*O zD0ST63oAQBE>9alZu*h%4!8rpyktui+sL+be+$ns6|~NNN^VXu19p5pr|nZ2$+vztoBJC(%`ACN)$DSf zV=lXP!4R?KZAcy``SB8%4Vlx>aufy9vo)%Kjww#nm{Xx_+GMK|&vWlqdA(v{r(PTH z-rUBpuOK97~it^N3ENI^p;DaT5@(MbI7xdJG;%VHn-oWw7O`*w(CO8*O zV)2y`!>&W@3D4^OhKQ{zo&L44bqT0X_ohrjWopcM>)tmjvbzRdxt?c99J;+<$Q~%g+^>#C38Uf}T%qSpibL6>!?OyBs?#a;D@{SjjsA*CkqO7ze z3xAUY!d)r;qB@RuO})gwmn8H8LQbDx_YUi zb2S>!olo9k#OIW8V^5s>)&RHI%xb>lD>Mx^XN(sPv|Ljgf8{H8kFz&4M{kuhCm3u6 z7Gci5Zoj2>)A`zx3zZ>p!2iZC_SnsUV>`o@ooY6Gs6cu1uxKlIZWASyc5Gw6_KTHI zo$-t!;Eazaa3SvH6FarFtAwv7A@u`2+iyS5+siVA723162NK|IR@ZlV?M{(b`?oA7 zu6zm@CBL1wf0*9dY8K)SmX$&eNzXFQ7k^tNXxk=1YVCY62qhB`3Fn-0d-bPFWu(zC zuE2xHC<^RY*EFB!s(wkiv*_*hP7Z^oUFWSzRPS=in|((j?WZ56)vFwaORs?<-Rdd^ zkm7aby?e55rd6Z?5-!;V#lrVaF=YjH>)d1>udzMRe+pKC=_zk1bDILR-lxv`OdUq>v@#Cj9hUeD_QpQvgTL~u9vZdnW`eb&d0;qYG zTu$bB2W5F{^gN6y*p*^*Pc%5EV;q554hd**>@*NNeh~d%@ z9B6bgf04o4)r;6!w0ek6Oyq2A@04ExHd8mF*B*k&LcVgtlTGxwLF zcxN`VOE&SJGJ=BO&5saOLJLcquX!<$CU2)Q{RRiiNS^Q4wKv#q5|}0cAf8y;DLs)R z>4WqfUK0Y-8u7WK9ke=orX^}oYkN(PS4|T0f5GiJi1whJiwRm*HmCt)kW^h8x1z5f zoX_a#5}?8r1d+MkKk+w85^|SFhQ$E&lvh;-Rr0QiqBC5aj1b-5iIGmIlP=GivBkAn z!l;%>X05wga~WN5MDrSr{|&5!{99P*udeztRzm)al`xFPpo6G=lHoLj?PQHW=%0$J zf5YjKbdI6V#1HW)1JZ}!#?Fl5@FS0bf0W#k18XEdCunqdOz~j!fw6WPltf4WQ}!`d zjN+rUY9~gAKnD2{&PWe+b%;Kw68$Tu9x6Gyu#P19XGCcSR|k}Xj^QNYBczU{hY)-m zACwLIXv(F?M@Sv|prlU$GeHmiki*(BfBh)GeFlxlbjO4L7FK%qca1$eVYl#A_os~S z|2J4k`u`PHlKu%+;%?}K)Th(d3*5fj%t06l4SKP?gr+Wu8@}z$9?KHFPUq=008JLn z51)-%=V3KlBMc|3ec!-qQs>T?oCfB;1|nrV#Q;3TO_pDUal#o3wD-^VrLcT}f8eT? zl$|O?!v|j1_`|GsR=S!4LICpw zAr@n-I!0+E{zIK46i>I!k;*(%hV31a1K-E}7TfaWO~4A}Z!M5;U5_vAe>287OV#7b z=;P;Y>#cX-k4vv1EYaq}=F0A=wygAl2#vG^>v+%Fu2^vM4oGjhvrvqcBB@XZu&hgS zxVu9_8|PeYTM zuXtNtle?|J;Ygih-fuI|ag~dp>yRTexAj5a%3=ObVWklSmEw8JY`jsBlia>K$CZ~j zMU4LeE3u%m4X6lA-6cb;L_O29SF=L*)G!)vgnRr}(o0Fiur>O!e}F_GI?gI4$x}jA z=Ggho+mFCjr2wdwAG2W(+sFxPoZCF?HQnVl6k^YUO*~^U-qy2+@gt5OVq&K4wCCnh z%KS-1x%CXNFkPb}WPthd=;ymjj+ZevPVGrebvw)p<{7+2U$fkpfUh&BRcW}{Oe{P-P#}(Si8NbG91q$w!0$FgpJG6`x#&($q2S*>J`IbGiyGOPn zl7^ZCD|5Zw-()~c$ZE&NbfM`iojmMGMWbrBmE*ciaW~fVRHrkh5XuvMiElf%nD7Z+ z(g_e(=^TT7e(p|*%a4yOqjQsFKrf{t_l5eFb5JCbG^y?3cZM$vPcDt&p z-|>B8xXLygf6$VJ=9*8MQ*Ewy&0uD(6z`8{Yzravc9s%g%=#^2-x6c3p9of+?Rw4x zLuy7ABiu8jTKfZxISitc*?w4GD#1Ag9`)yQA`a*Hb~-+jW+GsX52T00aLXtpZ-YgW zr@gbxR{+tV$6&bdx1tecv3&OQxY9e)Ltgv2LJgIkVYS&W=}s%rho!Br=R5b;K;x7Q z)#fsn7Z`p-axNd7JumHp^fErwc1K@1rFQ2VMeg0uv2QaPE{>yNl0;BR&0(A<>KH@_ zf5jJxs6Y1kR`u^C;vt^*c*aeryMVo_Sg{az1)wKJ2)B|Qe>NYQj@`1q%CV4vE=RP49e{G{VPacFn-eUj(cZDAz#2vU^Tx% zr&l2ED^tX?veaq9H%)|pKcz^n_9&Gie`39yc9j{av6ciV>YRpM=IDo&B@{&TA0QU- zA0gK7uKFWleNuiS@fD~LnnEy?Tw#kPFq&d86!}yxV$-c&fdv%DSfF^bhG@V+E8pf* zwq7PQ2e_520LFZZSnH*0Bn6}#Xb?uNFo$eztRH&ZAS;f7wkmqF$-+~BXDckOe=kM+ zYDNYqcITUF1p=THOzU7Ew~k_4o}C31H2}2aX56y5d!ab^m2IfFq8C#FJOf@`Tgz#| zfc2p%IGO&f4CAMWHOciD&yOtYc>9yq&i{49vj2aDSoZG_%X{aF*(QhK{JfgPF$;M^ z8FcrA(^IIJ*6RA` zK(@=Rymz%1p=4a$eCr-N4L>N^g=v+Hxfm;#Yi4b9ASHhMUBq%Z@0dPwi&c*gE5NJ; zwff+}sCHie8^od{5k9ko2(jw!r9_Wbf8It9PQ;b!50x$DNZveFS}8o4f4m49GDDp? zK3vW^`xY>r-fvx;j4Uxw2fOF<%c#)U&NCv(^aR$9>NvObwLZSq_=_|1vu-9qUsuGl z_lxX#JmjuF_>K7I{RoQYl6WYvUOVbF2VmOIn7>%*pe`Fttlb@!fkZlM#$w1A0f4jXFF#F!*w+%3Z!&E=L5cVe-4yjJaN5S%KQ`i zeWzv)dIr#M8aEaf=B3J}yW(YCpE{w2Q&{|*iUEC8hGfsskBTxU)o|{U%zf>L+=CX# zE_Qi23CVtw4wG0issE+34{bvGa?mHC^j!}mkA3MwpG}|wR$phs^UDm0o;?HPhes*6qk6)E#fi46P^1f5Pxi7ec;0H_vfDYge|5 z+z@`iU>~d%j=ga}jD!UA$;nLay)b>F^5D z`IZ2AlGS8lVhr^$DmwpleuzlK3-36#Fzy`6dOHp2e{m9>8-ZTgfNEU0I2^0A>|C3d z9~2aNh{Q)>exwejUgOUxaojUZ+X?I3>ftR@j%q4y7dKX0hV#|*{{2+M&suc9 zoB+Iw!>OCr9lTB?q$Nf(3Bv}yJI``%8C+5!f99#Fm7GgYx*h3L+PAWo8Z|$j$VXo` zyX7%SSb0|{^57K6W5Nh|xXcl%i+6QE+S3I>N-El^%vACeygyYbLb0+E>WBB8cOlo2;KjDxX+AR6`=vEQ&)j)J8xDitUz}Df zcEUmvOqnZ}k|IL1ImNI1e(qxh8nvc#;zR*+$cZVrWvt(Bm6CoE- zFbqr_7%I1%1=}dzn1yAT{FF7r+(T5Me-CnJp7HA|zTfQ~A7y+~ zZM+=G4ovV~SX0B|lU@)UxdKXXywDf)-9y6w3*h_Wm)==8VPLGm4?y~#L*7h?Ay5<1ir<}R& z_+ByMg2(naMhSK7HW~*#dF$o#e|B**vOnLpDgIy2@m=p9yz`Cvpg=|yS=rM1vn{xj zZ>jPVxB3g0edSlbyyQn$WfBbjVH8U%covav zaa0b(TZ=8S!ZsF0V;~V!;y^=TE^O90C1sofLQ_4b_9;9;U+=Z9i#lBt39kC)NXv}cdff|P~!KxJWpTam} z^M2IVZ$|N_lUl|&*GxHHB!KRsTSoN)wx*qxPg7bPrZWknD*SAa`!DKt zA?I?5EQiX@gKJ}ApEKEs4)fw(7`S8E((35`{s*Dh7Oo6*2_$X;E@ZJEy(!)eHWLoF z1TEM=;$3dV=u1JHmPecW48)dwCsJ8h)&|TXUOw+W{WYFKLyrR;0kBfvx&d42KYe#O z9c;n+mh)|GQ(+wy!(W?MU+egQ@14fFkvf*M-#3pFEv1hS|7JEnjA3}!9^B%8skTxT zs~NWwFpa(3XYSWD+QWn6Pd&E0Ra$;`^R%>*d)d zSxh|-xF1hMiBZ_3Jyc+LIq}7~IV7LMv+R)@QmdVf6dhoR|KqBWhz3Oq#fim@g9`ME zcE8JEpqk^Lj84#6@B7R74Et2SE7bFBod+F}7KK$-oE?BQqPHHKY1OQ(5P24%IfArM zB63DAur4^NOK1lj`|I0ZV)^9O%(}wNPA@QjNes1W^>5In6@th&^OV*#?s8H_85fJ0 zO8N*C{MFFz!OoKCp!n0#?vP6wr$tc1!<4f>&7Go%qmn4~H@RixVf>MMC%=40!f3_W z#h-pZiQer@s=fy(OBp3)+_>>u`e{82ivKlzb7sz&8Zs{WW6RX0-%Aqw$CrHW__O7M zi%s=*DFv#>zQqKV}f|CV7UdDJ37xMdp08oYiNL-^nD; z)u-D~@I7=4X%9rYnMwQbh`$NGd2VOVC};1)+msf4iRUMpUU*sY+P=Jy;{=%g(cH8D z{*NXi+Vdlv?MymzhK11U$!EfKsWWbV{I#y+8wWijaiT6?{H8CbBo4ree0!{}<;MjQ zMQmTGz~3IffhcG9Hcc*D ze2<0uA3aDPta}1XIiobuyRrds1f0S6tjcANBLb1X7-Lr%!yaXpgB1duB|ZntAi4d- zVgUpkuI^eRleN#`W}EL`z-1u#{#N%ET9O)Xomi(GRV0Y zagVXH9J!sqE%~DLnPcJhYiArJAJnvFb>VJww=pEeHh08_fZ&+JdMk@@Fgww7DJ#B9 z!1Hiqi_6cC!GFf<6%$%WuMmi{#~=@qdaTHYDUHsY4GG$U+l@{j-b1O@=4)M$k@?Ys z&a^O9oedGq_=i2}_gozyEZ(1j9`~XH`B!RanZGRG57!$*vH#Egb}P2{689Ipy=E6wM?VcUCEO6cBbbcf3i z2VwP0`ez$?$M-Mu0(Fu5z)I^h*^AR+YOUX>9qT79)puwq^&?n9Wd9!0fQm!TGJO!w zOujj7V9$_Z>%B=91N}&75Kb0qjtJeo{jIqwzPjVE-J~&8{J{hGZ_@49w`9uWqwI}t z8K#)?het$pbyEX(wu8JWU!C=2AHKuXlV-6^xvA3j{$dB}OVUqn4x@{Wf(=T{w7jB` zH7~uf9=PP2(ftm%RkI!tB;)!B&{gggBQ9CW?g2;s^#^E;vLIbrHx(0s*9q7v4B57F zv>bzpBGHNcy`D-ZV{WKC&WoJZFB83z-$Acr$7&b>0AufHgl06_%QyZ+dG{EHgg`); zwz%$J#&jfG6~{5--Ev<$FzT0TJt~Q7YnR2|O%DInf933p0>(u&X8^iD_J7{TQFg`~ zZbW4$uU}xlCkE*uY$#U+NSd*|YNpLt6a0eYlrQv5-?p#ZJj3B_W!WLb6CxG3iY{}H zbyz~7X)UOQyU^}Q%D&iK+P5h&(EZIq@_};0OaGqulve(R^v>6cHG*uF_*U636Y{9> ztX${EuF}>evC|f7*D|f?II69;B>Cjsad`>m4fI8{?ph#F8?Ce_Luw&O+}HMXUoq0} zfh1hAH2G91<2l7reI%e(#`xvboK?Iw|I99@xL-d;4ST-}@6I(f7_>o?pq4MA}vjJzuj>ijsq1;8C z8Zi)Gwpy2XuNiCoLE7hu^%6PVU(-|3F$5hilovOq)g4i!i%YO@bJzFK82qqM+8bR% z-Q=ymwyplEsVs z__dP)kY-rge)IOAR%rw7y1*`rQ(E=**+sm`M>QK^A`b>h)yoA#l`(LTmksyN9Wv=z z!uDT2G*x_-imySk9y6xIXh-?8vmMEE!GZ~bNCM7e_L?s&ui|h!Gwaxn%0vb7QL7G*)OHU-Z810OJKm^dvv#*O;%dW z-=VnnOgzsro?_}=+$Ha&zN4=cY7P+}l6 zo+Aksq@TNG<&##NMC9{TpIWHue>2}s|*&kC9@&qk_cWgs=N_t zJk~zqRqMJf<}dl$O>XWx4e=L026zla%16+Aa%kfaAp~0nVM_&{GHf1p`+Rz_+qUj| zdKzjdbD0bFX(M)E`6Es_qKlV=R^DUI4B9MYuJrAq(h#4Q#d};vDKGBDn86x4ROA=f zIZ41(1&?eOgU#C#cidOgO>BQjFfC;fMz-ac&1b1iVFKM~`f`*1G~I}ho>Onj6pZrL z_&Bk?;N2yC{yD5+&`r_@#M1RD8pEWuzdC;O|IIC+bpI>v#xwEI+j%m=sfCw;6*oiYhwocS*T*Eglf-t~lyDkPKEp(TrhSsk zcB-|s>U;Sw;GU$PH}CAo6~ns^XrtfzWaVQI-t^gd_#LQ@{*tiv74bOqBqe+ZiBXr> ze7#wWgI;E=AKymcAD?QRwozOU8m_uFHAwaH5xy#jx2hKyr4LWKUv)Pd=NY3SS59qi z(O=JETJtAVjC&Ur&YzJU-az_MLwB7%8KTxi0f-V0-NaENKquZ3r6`}i+X*(oz;XQw zjt}Em$B&?Mmse#}Vu+x=A1WPFd6t6XfvIt+;Y>#uYugqTIZEV=hcAkd6>X&pd9kS0L*Eun zeJ$Zi+?7-{E7lw>AymYx0zJG?B<05v&-fYr)*h8zuE|r%XBrn|ayC0%xW->{bALRs zGjYB1s}Q-lY`k+4D7v$0?H6&CbNnsS*l8Oz^*V`dvevYTIA&Pl!~}3L*_^tJM3G4C`GN$}5Z_xwY&oUz zO>0`0q>4gXH3FUV;&or>fPOUHb#%N?J1j0r+JoNZaT^WBjq#@&x20RohuyD+x(e}ufBiU zAO@^OPbCNNl!ojsMfriwf<|%p#{6ZpdlLb7Z`PbUuXZ@MqeGV09|kGgc_^({N_2fQ zGpRXemxtJYH;)AV%ezxpSTf{WdC`G9<$u!OCE^rlw&_!s$|dWuOl@9O$wUIuoa%Ei z+5H~%CtZn!!mBZ&B*#y{*0#N(lm$b|S<0KR(@keyi?nnQ@7&Ko7#t z+~K^Vx?ZI7oxLwG2~IhEg<4+966Q7Lu=C7RP9zR2mKTU%5f4>+{&X|lTRA?^S66(0 z+G#Myl6FXXe9SfhGV8Nem6d(HA!WRiv)W-o;!H&n%16k~_wPcV?g#ync%CN+ecM6i zPq8=$`(y48Ic3k=uBQ6zFp|TWpDZK}W8D+!?c*99FY4lQ>R9A0%iNn)JyyOqXGRfm zwR|f{BFw${r8%CmT^X(SD&ttkDmD^0P2ElSZ=T}2#|N`wFVJv;?e|NTZ%3aM6nthb zV9HbBkI`8|1QnTNzpq;acJ*=RS(VlFG;@HXVB;EK!VOR=@ioX7(!$K;ohscx#G= zr7|;;tYrDZPZd-i64Y;8xR4Zvw{JQsxG}Vu>7UCzYLqPp61gNzd36&=SSn2{yUH5) zMnEA?q=AnihSvE@E&;Z+MKbzd-!CeNz@I2&!9y;sk*z%9i0GZYVZHWW*xRH*Dl(4k zr+b?UN_ts-Op>&x!9jl*(^OfWwITYVpa~BOUB4Gali&i$V=_}{r-poQA>?Lg9Y)p- zD#ppyuC*vEvS;-FuF27-JRDnYU&kS|#LPhGG{%R{~@hrDOO=(Vm0k_t5R({E4rwI)fgN6@y#zMwwS*o)_Ek^)(V zoHb1f`Ugk4_=DYMScwz+Gb>Q38iH#b=JD(j>t*aJWOFTi_3Pq(xar>VR2^l;853I_fGZU zm@fFL^zje$!Cj1T@7eAUli>r}rPRSAf=Oq2x8#=>A&5m+k0FMSopb&u^&p=<8U^lG zsdBtgZ3JN-ivIwgnIbD7*yEeuV^|Z*u^}>Tfdpkb@K46#5{ZohngtM}d^z|&&m|Bi zP`5w3S;{&&Av7nYtE%+B^b?6Ku2c_q;*gH=-)9n{XR0tfr#f?JHR4X z6U2n_MP0i?wf5-Z@(5yua96QZGVeFied);3EKqp$Msmpe#qG%m@Rd@vl<<4e?)jmc z@iG%fHa-NoD1pqYGog1gufJHtM>msmD?K2fC=ZuOiHJ4-pk&-$=k6%+)W31oJlvm3 zZq7QbSuyLJAT3%74x%|T=yWVc!}b0%u>+#@OMDM#w!54DxhSN4{9q|?ggW>-0V8Ik z@eavc!U=!N8AQpC%XeADdIhZbKS+FC^?>;6h|?NurErs#f)D1QH)$4?Aos}-*% zUc<0p)sr(V-O-uBz}n>I!5XD*%POv-?-_KyRldDc9^Y3?nmf22<>Op#gq|ao9%6ya zZ-%_=?=;)~CI5R^7r@*J_$eUwO;6DB(~#weX)fX4lq^@OzB5BbV&zr(jo9c=0Ovwz zI&K?G|dH zn#rt6tvTMtiB^ryuv^jAX94Pms{;%`zo_&hZD}Rn$u#Q>b)J;R4>vk1ol?$VyI(qe ztYBKfR`d!>wkmV2#gH933d?i6?|{A%KZ>Z$8(Ek^P=;e{X$Sh%xMy$9PFy6KZ9<^$ z<4imhX||$vPh1KyEtehO>Q?lSvfeJvBym&RWI0LUNWE6gl%#DJSuXy?GVrwS7n^HMgmhSDp;MP|JtO??1AX zU5r}j*mtO_8Ag}FA8we?Uv>Gbz`q}Ir%EU1L&41$r=FCW#HP@NzJ|iiENt_6MeYnx zK{pXs_D+k)Yx8waz(nQ;I|&*FIV(^WkBY@!2Xna4)b%GYC=C0w%ynjHwA!;+w(OBxveW5Y_GV1VtAIbm-Q@qqzn>(7oavPZ>xo? z<2os=Zor}|=6G%8Jl;ZDbnYklQZcSAI{%ekkuKYNXU3SgLudOBsABb%i)SE`(=+`Q z-G9Z?A z5;i%gr{+!(eTLFn{8M)cz0m1grv zWZdW}A55G->&3jY{qgV-%&s>dhdel6ac{w*KTgGPUH94OIGB1&-B+5t=V$l77X^Zo zwEmabho28O?qc~o#8O=xFQxU^@bBhl>Q}?}k)^(n%#0Zverg^&6zK~CIZgdAo^$%{ z8rze5%iN(lWEJT##l4%Y81fZTuOq%Q8=@PgGJKZLaA5DK!@Sr~_bIeX&u`4Ai!-~H z%fs7~nrr%ggTJs!5!GwzaGJCptlqt0JnR@=Z&TgeMduM`DVmit$7AAoNAV zL<%8bwCO`&Gkt<4@SZ#aw7TFIAMqm2KszYS_HP>&VcY`NY}#lI&cY9qHv4boYhzyd zUsL_VV$%*yD(j`z^w>|%s+v?b`vpn{*^KNlTF?&M)l!9AUD-FXTP1GKc(wAo=_Ibb zJ)YYx)RWr&>Q+}LjQ&t(ADUS4=OVXT#VW;rMUS)iZJ-53YGwFz9O&Kg<*x7Re&E5# zmAzyiOV|ACs6z+jc`3owI{%EfhD#i1_N2;!y29f0iCFSFewHsGwpi1i7mbT| z$@XTXz{ER)U%dY6O#3RFm%Zuu5F+0Mj>w>2yj1~o15A57P?f8K3F#K}K<)n?{5RFh zsXh&e3bz_@kHpIv6UEqAZ zCDDIHWU3KE*fXn6#==NFF8$!9;zM2cL|FA+X?TU{_>BclykqX<_O#qg9m}qCAG4KJ zTBU}P7b4rfQ!QZ<=2D^E0<~J^20;bt=at(PFCdG!ma+&4CsW1Hb5-V2pCbe_CQnV> z?n?S6#AZ}|=VEx0_db!nXzG*tt=MLJhsmJrm#^z@AJ4c@l1>KYvGyVBkJBzz`^Y^J zxD5sy^P2YSmcioJc4=;ci(A}xjE}VS2!8v_%tO*&LzMHZF4quMSfsmUMG_3^n05nP z_jkAB44dXaHXdB{Z>=<=^>4wz0$Nc9b&!Z2sp3i<#&XK*g|4Vzmz zSub;|+D}elts(4pI+2ti#~juSdfTKdkmYapEDC;@3o`|r$-J~uFNrc5NHCD0f0B(! zPFSDB2oF*dp$)95<3kd3|M%^x+KF*nSMeK})1(Nz`9AoPjBhakIxm$(c2M za~WXC2EwWQwfpx>2se^w`Sm)=w2pRy(KFvBIt<1Z{|p*dX@3NcW*JCty3a;LL|#pWErvz(~m(s!fZaF?>}E+`+Zy|^Af<#uprn~R2S`N*@VwDc>`&TzL6 zOdc&h02h-NJ-GZ$qjF@alNP2P?&G~H8D?p$O`Qq(MzOJiYhr5;l|%s zeg_)%4jNTFOc0V-#lR4D_BH9L0)~4rMM%`C-x;`5+`hWRn~k9zay`OMF(9s+=p*p( zZxgW~fxZ5Q0& z)!{fts&0QFs&X)78mVXLkNJB-k^42si!&ua9BqtyuJGQtWD$U`Xk>==ZXQQd0(}UH zA^*9<%U7-1YqFO8dLG$ah0AX;Ppto#oZF}JdB|}MESHUMY+v;-v245y4b`Y?nK;e8 zLAtJ{i?<&*U-M_P^*yv-ShLPQ{)zFzApPc<*i~J$D`O9HAo#-Rjp+N-`{h~`(EYEx z-ddv%u3xqOzW%MB=+`4Y*&XVo3Et!udWUt>7>kRRnYh&=y@SY6K;u#kqUxK!P_ML{*9Q~Gv| zDAMG(jlH+80sJYox`!|&c9BOn5p;3BmZj2m&_(T+0;0RWS2H9@Z`!#n{y14E5BS;`3VOvu{{P@$!)h#++sAL|u4s*r|*ZzQSOOiFSe)`$@_9_gcIJns6&2 zg9M2WQX!TFUBgKOSlGf-yTyDBBy008xa_&)s6Bta`b~-U?^Vy3IECe+vKXkR(e>n% zGMp+f=k4J(552(1Gdq&Q7v>)F(c}EV-%Y%bs1V=$Zw_qL~wKfK&z$&suu z98n&Hb=|Iy)P(4Fh`fF?X%uqIA#!KR4@^8AYQrCV

  • H{9BJwl<(e#{K`wHG0A6; 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 G)Bgbo^e3GF diff --git a/Misc/NEWS.d/next/Library/2023-11-29-10-51-41.gh-issue-112516.rFKUKN.rst b/Misc/NEWS.d/next/Library/2023-11-29-10-51-41.gh-issue-112516.rFKUKN.rst new file mode 100644 index 00000000000000..530cf992dcd77a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-29-10-51-41.gh-issue-112516.rFKUKN.rst @@ -0,0 +1 @@ +Update the bundled copy of pip to version 23.3.1. From 2ed20d3bd84fdcf511235cc473a241c5e8278a91 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 4 Dec 2023 19:35:46 +0000 Subject: [PATCH 166/228] gh-74690: Avoid a costly type check where possible in `_ProtocolMeta.__subclasscheck__` (#112717) --- Lib/test/test_typing.py | 19 +++++++++++++--- Lib/typing.py | 22 ++++++++++++++++--- ...3-12-04-16-45-11.gh-issue-74690.pQYP5U.rst | 2 ++ 3 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-12-04-16-45-11.gh-issue-74690.pQYP5U.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 3572df7737f652..2d10c39840ddf3 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -3533,13 +3533,26 @@ def __subclasshook__(cls, other): def test_issubclass_fails_correctly(self): @runtime_checkable - class P(Protocol): + class NonCallableMembers(Protocol): x = 1 + class NotRuntimeCheckable(Protocol): + def callable_member(self) -> int: ... + + @runtime_checkable + class RuntimeCheckable(Protocol): + def callable_member(self) -> int: ... + class C: pass - with self.assertRaisesRegex(TypeError, r"issubclass\(\) arg 1 must be a class"): - issubclass(C(), P) + # These three all exercise different code paths, + # but should result in the same error message: + for protocol in NonCallableMembers, NotRuntimeCheckable, RuntimeCheckable: + with self.subTest(proto_name=protocol.__name__): + with self.assertRaisesRegex( + TypeError, r"issubclass\(\) arg 1 must be a class" + ): + issubclass(C(), protocol) def test_defining_generic_protocols(self): T = TypeVar('T') diff --git a/Lib/typing.py b/Lib/typing.py index aa64ed93f76fbf..61b88a560e9dc5 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1790,6 +1790,23 @@ def _pickle_pskwargs(pskwargs): _abc_subclasscheck = ABCMeta.__subclasscheck__ +def _type_check_issubclass_arg_1(arg): + """Raise TypeError if `arg` is not an instance of `type` + in `issubclass(arg, )`. + + In most cases, this is verified by type.__subclasscheck__. + Checking it again unnecessarily would slow down issubclass() checks, + so, we don't perform this check unless we absolutely have to. + + For various error paths, however, + we want to ensure that *this* error message is shown to the user + where relevant, rather than a typing.py-specific error message. + """ + if not isinstance(arg, type): + # Same error message as for issubclass(1, int). + raise TypeError('issubclass() arg 1 must be a class') + + class _ProtocolMeta(ABCMeta): # This metaclass is somewhat unfortunate, # but is necessary for several reasons... @@ -1829,13 +1846,11 @@ def __subclasscheck__(cls, other): getattr(cls, '_is_protocol', False) and not _allow_reckless_class_checks() ): - if not isinstance(other, type): - # Same error message as for issubclass(1, int). - raise TypeError('issubclass() arg 1 must be a class') if ( not cls.__callable_proto_members_only__ and cls.__dict__.get("__subclasshook__") is _proto_hook ): + _type_check_issubclass_arg_1(other) non_method_attrs = sorted( attr for attr in cls.__protocol_attrs__ if not callable(getattr(cls, attr, None)) @@ -1845,6 +1860,7 @@ def __subclasscheck__(cls, other): f" Non-method members: {str(non_method_attrs)[1:-1]}." ) if not getattr(cls, '_is_runtime_protocol', False): + _type_check_issubclass_arg_1(other) raise TypeError( "Instance and class checks can only be used with " "@runtime_checkable protocols" diff --git a/Misc/NEWS.d/next/Library/2023-12-04-16-45-11.gh-issue-74690.pQYP5U.rst b/Misc/NEWS.d/next/Library/2023-12-04-16-45-11.gh-issue-74690.pQYP5U.rst new file mode 100644 index 00000000000000..8102f02e941c29 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-04-16-45-11.gh-issue-74690.pQYP5U.rst @@ -0,0 +1,2 @@ +Speedup :func:`issubclass` checks against simple :func:`runtime-checkable +protocols ` by around 6%. Patch by Alex Waygood. From a1551b48eebb4a68fda031b5ee9e5cbde8d924dd Mon Sep 17 00:00:00 2001 From: Kamil Turek Date: Mon, 4 Dec 2023 20:42:01 +0100 Subject: [PATCH 167/228] gh-103363: Add follow_symlinks argument to `pathlib.Path.owner()` and `group()` (#107962) --- Doc/library/pathlib.rst | 20 ++++- Doc/whatsnew/3.13.rst | 8 +- Lib/pathlib.py | 14 ++-- Lib/test/test_pathlib.py | 73 ++++++++++++++++--- ...-08-14-21-10-52.gh-issue-103363.u64_QI.rst | 2 + 5 files changed, 93 insertions(+), 24 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-08-14-21-10-52.gh-issue-103363.u64_QI.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 7ecfd120db8d15..62d4ed5e3f46b9 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -1017,15 +1017,21 @@ call fails (for example because the path doesn't exist). future Python release, patterns with this ending will match both files and directories. Add a trailing slash to match only directories. -.. method:: Path.group() +.. method:: Path.group(*, follow_symlinks=True) - Return the name of the group owning the file. :exc:`KeyError` is raised + Return the name of the group owning the file. :exc:`KeyError` is raised if the file's gid isn't found in the system database. + This method normally follows symlinks; to get the group of the symlink, add + the argument ``follow_symlinks=False``. + .. versionchanged:: 3.13 Raises :exc:`UnsupportedOperation` if the :mod:`grp` module is not available. In previous versions, :exc:`NotImplementedError` was raised. + .. versionchanged:: 3.13 + The *follow_symlinks* parameter was added. + .. method:: Path.is_dir(*, follow_symlinks=True) @@ -1291,15 +1297,21 @@ call fails (for example because the path doesn't exist). '#!/usr/bin/env python3\n' -.. method:: Path.owner() +.. method:: Path.owner(*, follow_symlinks=True) - Return the name of the user owning the file. :exc:`KeyError` is raised + Return the name of the user owning the file. :exc:`KeyError` is raised if the file's uid isn't found in the system database. + This method normally follows symlinks; to get the owner of the symlink, add + the argument ``follow_symlinks=False``. + .. versionchanged:: 3.13 Raises :exc:`UnsupportedOperation` if the :mod:`pwd` module is not available. In previous versions, :exc:`NotImplementedError` was raised. + .. versionchanged:: 3.13 + The *follow_symlinks* parameter was added. + .. method:: Path.read_bytes() diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index be890ff314dfa4..534813f3659c9d 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -270,9 +270,11 @@ pathlib (Contributed by Barney Gale in :gh:`73435`.) * Add *follow_symlinks* keyword-only argument to :meth:`pathlib.Path.glob`, - :meth:`~pathlib.Path.rglob`, :meth:`~pathlib.Path.is_file`, and - :meth:`~pathlib.Path.is_dir`. - (Contributed by Barney Gale in :gh:`77609` and :gh:`105793`.) + :meth:`~pathlib.Path.rglob`, :meth:`~pathlib.Path.is_file`, + :meth:`~pathlib.Path.is_dir`, :meth:`~pathlib.Path.owner`, + :meth:`~pathlib.Path.group`. + (Contributed by Barney Gale in :gh:`77609` and :gh:`105793`, and + Kamil Turek in :gh:`107962`). pdb --- diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 81f75cd47ed087..b728a0b3dfdb6c 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -1319,13 +1319,13 @@ def rmdir(self): """ self._unsupported("rmdir") - def owner(self): + def owner(self, *, follow_symlinks=True): """ Return the login name of the file owner. """ self._unsupported("owner") - def group(self): + def group(self, *, follow_symlinks=True): """ Return the group name of the file gid. """ @@ -1440,18 +1440,20 @@ def resolve(self, strict=False): return self.with_segments(os.path.realpath(self, strict=strict)) if pwd: - def owner(self): + def owner(self, *, follow_symlinks=True): """ Return the login name of the file owner. """ - return pwd.getpwuid(self.stat().st_uid).pw_name + uid = self.stat(follow_symlinks=follow_symlinks).st_uid + return pwd.getpwuid(uid).pw_name if grp: - def group(self): + def group(self, *, follow_symlinks=True): """ Return the group name of the file gid. """ - return grp.getgrgid(self.stat().st_gid).gr_name + gid = self.stat(follow_symlinks=follow_symlinks).st_gid + return grp.getgrgid(gid).gr_name if hasattr(os, "readlink"): def readlink(self): diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index ccaef070974ffd..1b10d6c2f0cb19 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -41,6 +41,9 @@ def test_is_notimplemented(self): only_posix = unittest.skipIf(os.name == 'nt', 'test requires a POSIX-compatible system') +root_in_posix = False +if hasattr(os, 'geteuid'): + root_in_posix = (os.geteuid() == 0) # # Tests for the pure classes. @@ -2975,27 +2978,75 @@ def test_chmod_follow_symlinks_true(self): # XXX also need a test for lchmod. - @unittest.skipUnless(pwd, "the pwd module is needed for this test") - def test_owner(self): - p = self.cls(BASE) / 'fileA' - uid = p.stat().st_uid + def _get_pw_name_or_skip_test(self, uid): try: - name = pwd.getpwuid(uid).pw_name + return pwd.getpwuid(uid).pw_name except KeyError: self.skipTest( "user %d doesn't have an entry in the system database" % uid) - self.assertEqual(name, p.owner()) - @unittest.skipUnless(grp, "the grp module is needed for this test") - def test_group(self): + @unittest.skipUnless(pwd, "the pwd module is needed for this test") + def test_owner(self): p = self.cls(BASE) / 'fileA' - gid = p.stat().st_gid + expected_uid = p.stat().st_uid + expected_name = self._get_pw_name_or_skip_test(expected_uid) + + self.assertEqual(expected_name, p.owner()) + + @unittest.skipUnless(pwd, "the pwd module is needed for this test") + @unittest.skipUnless(root_in_posix, "test needs root privilege") + def test_owner_no_follow_symlinks(self): + all_users = [u.pw_uid for u in pwd.getpwall()] + if len(all_users) < 2: + self.skipTest("test needs more than one user") + + target = self.cls(BASE) / 'fileA' + link = self.cls(BASE) / 'linkA' + + uid_1, uid_2 = all_users[:2] + os.chown(target, uid_1, -1) + os.chown(link, uid_2, -1, follow_symlinks=False) + + expected_uid = link.stat(follow_symlinks=False).st_uid + expected_name = self._get_pw_name_or_skip_test(expected_uid) + + self.assertEqual(expected_uid, uid_2) + self.assertEqual(expected_name, link.owner(follow_symlinks=False)) + + def _get_gr_name_or_skip_test(self, gid): try: - name = grp.getgrgid(gid).gr_name + return grp.getgrgid(gid).gr_name except KeyError: self.skipTest( "group %d doesn't have an entry in the system database" % gid) - self.assertEqual(name, p.group()) + + @unittest.skipUnless(grp, "the grp module is needed for this test") + def test_group(self): + p = self.cls(BASE) / 'fileA' + expected_gid = p.stat().st_gid + expected_name = self._get_gr_name_or_skip_test(expected_gid) + + self.assertEqual(expected_name, p.group()) + + @unittest.skipUnless(grp, "the grp module is needed for this test") + @unittest.skipUnless(root_in_posix, "test needs root privilege") + def test_group_no_follow_symlinks(self): + all_groups = [g.gr_gid for g in grp.getgrall()] + if len(all_groups) < 2: + self.skipTest("test needs more than one group") + + target = self.cls(BASE) / 'fileA' + link = self.cls(BASE) / 'linkA' + + gid_1, gid_2 = all_groups[:2] + os.chown(target, -1, gid_1) + os.chown(link, -1, gid_2, follow_symlinks=False) + + expected_gid = link.stat(follow_symlinks=False).st_gid + expected_name = self._get_pw_name_or_skip_test(expected_gid) + + self.assertEqual(expected_gid, gid_2) + self.assertEqual(expected_name, link.group(follow_symlinks=False)) def test_unlink(self): p = self.cls(BASE) / 'fileA' diff --git a/Misc/NEWS.d/next/Library/2023-08-14-21-10-52.gh-issue-103363.u64_QI.rst b/Misc/NEWS.d/next/Library/2023-08-14-21-10-52.gh-issue-103363.u64_QI.rst new file mode 100644 index 00000000000000..d4a27d624eb5e6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-14-21-10-52.gh-issue-103363.u64_QI.rst @@ -0,0 +1,2 @@ +Add *follow_symlinks* keyword-only argument to :meth:`pathlib.Path.owner` +and :meth:`~pathlib.Path.group`, defaulting to ``True``. From 4eddb4c9d9452482c9af7fa9eec223d12b5a9f33 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Mon, 4 Dec 2023 12:04:05 -0800 Subject: [PATCH 168/228] gh-105967: Work around a macOS bug, limit zlib C library crc32 API calls to 1gig (#112615) Work around a macOS bug, limit zlib crc32 calls to 1GiB. Without this, `zlib.crc32` and `binascii.crc32` could produce incorrect results on multi-gigabyte inputs depending on the macOS version's Apple supplied zlib implementation. --- ...3-12-01-19-02-21.gh-issue-105967.Puq5Cn.rst | 4 ++++ Modules/binascii.c | 18 +++++++++++++----- Modules/zlibmodule.c | 18 +++++++++++++----- 3 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-12-01-19-02-21.gh-issue-105967.Puq5Cn.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-01-19-02-21.gh-issue-105967.Puq5Cn.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-01-19-02-21.gh-issue-105967.Puq5Cn.rst new file mode 100644 index 00000000000000..c69511218e3e16 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-01-19-02-21.gh-issue-105967.Puq5Cn.rst @@ -0,0 +1,4 @@ +Workaround a bug in Apple's macOS platform zlib library where +:func:`zlib.crc32` and :func:`binascii.crc32` could produce incorrect results +on multi-gigabyte inputs. Including when using :mod:`zipfile` on zips +containing large data. diff --git a/Modules/binascii.c b/Modules/binascii.c index 17970aa5e9456c..86493241a1fb7e 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -770,12 +770,20 @@ binascii_crc32_impl(PyObject *module, Py_buffer *data, unsigned int crc) Py_BEGIN_ALLOW_THREADS /* Avoid truncation of length for very large buffers. crc32() takes - length as an unsigned int, which may be narrower than Py_ssize_t. */ - while ((size_t)len > UINT_MAX) { - crc = crc32(crc, buf, UINT_MAX); - buf += (size_t) UINT_MAX; - len -= (size_t) UINT_MAX; + length as an unsigned int, which may be narrower than Py_ssize_t. + We further limit size due to bugs in Apple's macOS zlib. + See https://github.com/python/cpython/issues/105967 + */ +#define ZLIB_CRC_CHUNK_SIZE 0x40000000 +#if ZLIB_CRC_CHUNK_SIZE > INT_MAX +# error "unsupported less than 32-bit platform?" +#endif + while ((size_t)len > ZLIB_CRC_CHUNK_SIZE) { + crc = crc32(crc, buf, ZLIB_CRC_CHUNK_SIZE); + buf += (size_t) ZLIB_CRC_CHUNK_SIZE; + len -= (size_t) ZLIB_CRC_CHUNK_SIZE; } +#undef ZLIB_CRC_CHUNK_SIZE crc = crc32(crc, buf, (unsigned int)len); Py_END_ALLOW_THREADS } else { diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c index 9b76afa0e56f76..fe9a6d8d4150ab 100644 --- a/Modules/zlibmodule.c +++ b/Modules/zlibmodule.c @@ -1896,12 +1896,20 @@ zlib_crc32_impl(PyObject *module, Py_buffer *data, unsigned int value) Py_BEGIN_ALLOW_THREADS /* Avoid truncation of length for very large buffers. crc32() takes - length as an unsigned int, which may be narrower than Py_ssize_t. */ - while ((size_t)len > UINT_MAX) { - value = crc32(value, buf, UINT_MAX); - buf += (size_t) UINT_MAX; - len -= (size_t) UINT_MAX; + length as an unsigned int, which may be narrower than Py_ssize_t. + We further limit size due to bugs in Apple's macOS zlib. + See https://github.com/python/cpython/issues/105967. + */ +#define ZLIB_CRC_CHUNK_SIZE 0x40000000 +#if ZLIB_CRC_CHUNK_SIZE > INT_MAX +# error "unsupported less than 32-bit platform?" +#endif + while ((size_t)len > ZLIB_CRC_CHUNK_SIZE) { + value = crc32(value, buf, ZLIB_CRC_CHUNK_SIZE); + buf += (size_t) ZLIB_CRC_CHUNK_SIZE; + len -= (size_t) ZLIB_CRC_CHUNK_SIZE; } +#undef ZLIB_CRC_CHUNK_SIZE value = crc32(value, buf, (unsigned int)len); Py_END_ALLOW_THREADS } else { From a8ce149628c9eaafb8c38fbf25fbd1ed483d2902 Mon Sep 17 00:00:00 2001 From: Amioplk Date: Mon, 4 Dec 2023 21:52:06 +0100 Subject: [PATCH 169/228] gh-112671: Fixing typo in the Macro Docs (GH-112715) Replace Py_T_STRING_INLINE with Py_T_STRING_INPLACE --- Doc/c-api/structures.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index 25cb4ed40f63e7..528813c255c0a5 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -592,7 +592,7 @@ Macro name C type Python type (*): Zero-terminated, UTF8-encoded C string. With :c:macro:`!Py_T_STRING` the C representation is a pointer; - with :c:macro:`!Py_T_STRING_INLINE` the string is stored directly + with :c:macro:`!Py_T_STRING_INPLACE` the string is stored directly in the structure. (**): String of length 1. Only ASCII is accepted. From c5fa8a54dbdf564d482e2e3857aa3efa61edd329 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 4 Dec 2023 23:40:06 +0100 Subject: [PATCH 170/228] gh-112535: Add test on _Py_ThreadId() (#112709) Add also test.support.Py_GIL_DISABLED constant. --- Lib/test/support/__init__.py | 3 +- Lib/test/test_capi/test_misc.py | 55 +++++++++++++++++++++++++ Lib/test/test_cppext/__init__.py | 3 +- Lib/test/test_importlib/test_windows.py | 4 +- Lib/test/test_sys.py | 4 +- Modules/_testinternalcapi.c | 14 +++++++ 6 files changed, 75 insertions(+), 8 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 318a0599a75acd..c22d73c231b46e 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -796,7 +796,8 @@ def check_cflags_pgo(): return any(option in cflags_nodist for option in pgo_options) -if sysconfig.get_config_var('Py_GIL_DISABLED'): +Py_GIL_DISABLED = bool(sysconfig.get_config_var('Py_GIL_DISABLED')) +if Py_GIL_DISABLED: _header = 'PHBBInP' else: _header = 'nP' diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 6cbf5d22203804..3d86ae37190475 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2854,5 +2854,60 @@ def testfunc(n, m): self.assertIn("_FOR_ITER_TIER_TWO", uops) +@unittest.skipUnless(support.Py_GIL_DISABLED, 'need Py_GIL_DISABLED') +class TestPyThreadId(unittest.TestCase): + def test_py_thread_id(self): + # gh-112535: Test _Py_ThreadId(): make sure that thread identifiers + # in a few threads are unique + py_thread_id = _testinternalcapi.py_thread_id + short_sleep = 0.010 + + class GetThreadId(threading.Thread): + def __init__(self): + super().__init__() + self.get_lock = threading.Lock() + self.get_lock.acquire() + self.started_lock = threading.Event() + self.py_tid = None + + def run(self): + self.started_lock.set() + self.get_lock.acquire() + self.py_tid = py_thread_id() + time.sleep(short_sleep) + self.py_tid2 = py_thread_id() + + nthread = 5 + threads = [GetThreadId() for _ in range(nthread)] + + # first make run sure that all threads are running + for thread in threads: + thread.start() + for thread in threads: + thread.started_lock.wait() + + # call _Py_ThreadId() in the main thread + py_thread_ids = [py_thread_id()] + + # now call _Py_ThreadId() in each thread + for thread in threads: + thread.get_lock.release() + + # call _Py_ThreadId() in each thread and wait until threads complete + for thread in threads: + thread.join() + py_thread_ids.append(thread.py_tid) + # _PyThread_Id() should not change for a given thread. + # For example, it should remain the same after a short sleep. + self.assertEqual(thread.py_tid2, thread.py_tid) + + # make sure that all _Py_ThreadId() are unique + for tid in py_thread_ids: + self.assertIsInstance(tid, int) + self.assertGreater(tid, 0) + self.assertEqual(len(set(py_thread_ids)), len(py_thread_ids), + py_thread_ids) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_cppext/__init__.py b/Lib/test/test_cppext/__init__.py index 299a16ada2e32e..c6039bd17b0662 100644 --- a/Lib/test/test_cppext/__init__.py +++ b/Lib/test/test_cppext/__init__.py @@ -2,7 +2,6 @@ # compatible with C++ and does not emit C++ compiler warnings. import os.path import shutil -import sys import unittest import subprocess import sysconfig @@ -15,7 +14,7 @@ # gh-110119: pip does not currently support 't' in the ABI flag use by # --disable-gil builds. Once it does, we can remove this skip. -@unittest.skipIf(sysconfig.get_config_var('Py_GIL_DISABLED') == 1, +@unittest.skipIf(support.Py_GIL_DISABLED, 'test does not work with --disable-gil') @support.requires_subprocess() class TestCPPExt(unittest.TestCase): diff --git a/Lib/test/test_importlib/test_windows.py b/Lib/test/test_importlib/test_windows.py index d25133240b1afd..8a9a8fffcd10d4 100644 --- a/Lib/test/test_importlib/test_windows.py +++ b/Lib/test/test_importlib/test_windows.py @@ -4,8 +4,8 @@ import os import re import sys -import sysconfig import unittest +from test import support from test.support import import_helper from contextlib import contextmanager from test.test_importlib.util import temp_module @@ -112,7 +112,7 @@ def test_module_not_found(self): class WindowsExtensionSuffixTests: def test_tagged_suffix(self): suffixes = self.machinery.EXTENSION_SUFFIXES - abi_flags = "t" if sysconfig.get_config_var("Py_GIL_DISABLED") else "" + abi_flags = "t" if support.Py_GIL_DISABLED else "" ver = sys.version_info platform = re.sub('[^a-zA-Z0-9]', '_', get_platform()) expected_tag = f".cp{ver.major}{ver.minor}{abi_flags}-{platform}.pyd" diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 8c2c1a40f74bf2..db5ba16c4d9739 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1224,9 +1224,7 @@ def test_pystats(self): @test.support.cpython_only @unittest.skipUnless(hasattr(sys, 'abiflags'), 'need sys.abiflags') def test_disable_gil_abi(self): - abi_threaded = 't' in sys.abiflags - py_gil_disabled = (sysconfig.get_config_var('Py_GIL_DISABLED') == 1) - self.assertEqual(py_gil_disabled, abi_threaded) + self.assertEqual('t' in sys.abiflags, support.Py_GIL_DISABLED) @test.support.cpython_only diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 4607a3faf17f74..ba7653f2d9c7aa 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -1625,6 +1625,17 @@ get_type_module_name(PyObject *self, PyObject *type) } +#ifdef Py_GIL_DISABLED +static PyObject * +get_py_thread_id(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + uintptr_t tid = _Py_ThreadId(); + Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(tid)); + return PyLong_FromUnsignedLongLong(tid); +} +#endif + + static PyMethodDef module_functions[] = { {"get_configs", get_configs, METH_NOARGS}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, @@ -1688,6 +1699,9 @@ static PyMethodDef module_functions[] = { {"restore_crossinterp_data", restore_crossinterp_data, METH_VARARGS}, _TESTINTERNALCAPI_TEST_LONG_NUMBITS_METHODDEF {"get_type_module_name", get_type_module_name, METH_O}, +#ifdef Py_GIL_DISABLED + {"py_thread_id", get_py_thread_id, METH_NOARGS}, +#endif {NULL, NULL} /* sentinel */ }; From 9fe7655c6ce0b8e9adc229daf681b6d30e6b1610 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Mon, 4 Dec 2023 15:08:19 -0800 Subject: [PATCH 171/228] gh-112334: Restore subprocess's use of `vfork()` & fix `extra_groups=[]` behavior (#112617) Restore `subprocess`'s intended use of `vfork()` by default for performance on Linux; also fixes the behavior of `extra_groups=[]` which was unintentionally broken in 3.12.0: Fixed a performance regression in 3.12's :mod:`subprocess` on Linux where it would no longer use the fast-path ``vfork()`` system call when it could have due to a logic bug, instead falling back to the safe but slower ``fork()``. Also fixed a security bug introduced in 3.12.0. If a value of ``extra_groups=[]`` was passed to :mod:`subprocess.Popen` or related APIs, the underlying ``setgroups(0, NULL)`` system call to clear the groups list would not be made in the child process prior to ``exec()``. The security issue was identified via code inspection in the process of fixing the first bug. Thanks to @vain for the detailed report and analysis in the initial bug on Github. Co-authored-by: Serhiy Storchaka --- Lib/test/test_subprocess.py | 38 +++++++++---------- ...-12-01-21-05-46.gh-issue-112334.DmNXKh.rst | 11 ++++++ Modules/_posixsubprocess.c | 12 +++++- 3 files changed, 38 insertions(+), 23 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-12-01-21-05-46.gh-issue-112334.DmNXKh.rst diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index fe1a3675fced65..319bc0d2638563 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -2066,8 +2066,14 @@ def test_group_error(self): def test_extra_groups(self): gid = os.getegid() group_list = [65534 if gid != 65534 else 65533] + self._test_extra_groups_impl(gid=gid, group_list=group_list) + + @unittest.skipUnless(hasattr(os, 'setgroups'), 'no setgroups() on platform') + def test_extra_groups_empty_list(self): + self._test_extra_groups_impl(gid=os.getegid(), group_list=[]) + + def _test_extra_groups_impl(self, *, gid, group_list): name_group = _get_test_grp_name() - perm_error = False if grp is not None: group_list.append(name_group) @@ -2077,11 +2083,8 @@ def test_extra_groups(self): [sys.executable, "-c", "import os, sys, json; json.dump(os.getgroups(), sys.stdout)"], extra_groups=group_list) - except OSError as ex: - if ex.errno != errno.EPERM: - raise - perm_error = True - + except PermissionError: + self.skipTest("setgroup() EPERM; this test may require root.") else: parent_groups = os.getgroups() child_groups = json.loads(output) @@ -2092,12 +2095,15 @@ def test_extra_groups(self): else: desired_gids = group_list - if perm_error: - self.assertEqual(set(child_groups), set(parent_groups)) - else: - self.assertEqual(set(desired_gids), set(child_groups)) + self.assertEqual(set(desired_gids), set(child_groups)) - # make sure we bomb on negative values + if grp is None: + with self.assertRaises(ValueError): + subprocess.check_call(ZERO_RETURN_CMD, + extra_groups=[name_group]) + + # No skip necessary, this test won't make it to a setgroup() call. + def test_extra_groups_invalid_gid_t_values(self): with self.assertRaises(ValueError): subprocess.check_call(ZERO_RETURN_CMD, extra_groups=[-1]) @@ -2106,16 +2112,6 @@ def test_extra_groups(self): cwd=os.curdir, env=os.environ, extra_groups=[2**64]) - if grp is None: - with self.assertRaises(ValueError): - subprocess.check_call(ZERO_RETURN_CMD, - extra_groups=[name_group]) - - @unittest.skipIf(hasattr(os, 'setgroups'), 'setgroups() available on platform') - def test_extra_groups_error(self): - with self.assertRaises(ValueError): - subprocess.check_call(ZERO_RETURN_CMD, extra_groups=[]) - @unittest.skipIf(mswindows or not hasattr(os, 'umask'), 'POSIX umask() is not available.') def test_umask(self): diff --git a/Misc/NEWS.d/next/Library/2023-12-01-21-05-46.gh-issue-112334.DmNXKh.rst b/Misc/NEWS.d/next/Library/2023-12-01-21-05-46.gh-issue-112334.DmNXKh.rst new file mode 100644 index 00000000000000..3a53a8bf84230f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-01-21-05-46.gh-issue-112334.DmNXKh.rst @@ -0,0 +1,11 @@ +Fixed a performance regression in 3.12's :mod:`subprocess` on Linux where it +would no longer use the fast-path ``vfork()`` system call when it could have +due to a logic bug, instead falling back to the safe but slower ``fork()``. + +Also fixed a second 3.12.0 potential security bug. If a value of +``extra_groups=[]`` was passed to :mod:`subprocess.Popen` or related APIs, +the underlying ``setgroups(0, NULL)`` system call to clear the groups list +would not be made in the child process prior to ``exec()``. + +This was identified via code inspection in the process of fixing the first +bug. diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index 2898eedc3e3a8f..d0dd8f064e0395 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -767,8 +767,10 @@ child_exec(char *const exec_array[], #endif #ifdef HAVE_SETGROUPS - if (extra_group_size > 0) + if (extra_group_size >= 0) { + assert((extra_group_size == 0) == (extra_groups == NULL)); POSIX_CALL(setgroups(extra_group_size, extra_groups)); + } #endif /* HAVE_SETGROUPS */ #ifdef HAVE_SETREGID @@ -1022,7 +1024,6 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args, pid_t pid = -1; int need_to_reenable_gc = 0; char *const *argv = NULL, *const *envp = NULL; - Py_ssize_t extra_group_size = 0; int need_after_fork = 0; int saved_errno = 0; int *c_fds_to_keep = NULL; @@ -1103,6 +1104,13 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args, cwd = PyBytes_AsString(cwd_obj2); } + // Special initial value meaning that subprocess API was called with + // extra_groups=None leading to _posixsubprocess.fork_exec(gids=None). + // We use this to differentiate between code desiring a setgroups(0, NULL) + // call vs no call at all. The fast vfork() code path could be used when + // there is no setgroups call. + Py_ssize_t extra_group_size = -2; + if (extra_groups_packed != Py_None) { #ifdef HAVE_SETGROUPS if (!PyList_Check(extra_groups_packed)) { From 304a1b3f3a8ed9a734ef1d098cafccb6725162db Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Mon, 4 Dec 2023 23:21:39 +0000 Subject: [PATCH 172/228] GH-112727: Speed up `pathlib.Path.absolute()` (#112728) Use `_from_parsed_parts()` to create a pre-joined/pre-parsed path, rather than passing multiple arguments to `with_segments()` Co-authored-by: Alex Waygood --- Lib/pathlib.py | 20 +++++++++++++------ ...-12-04-21-30-34.gh-issue-112727.jpgNRB.rst | 1 + 2 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-12-04-21-30-34.gh-issue-112727.jpgNRB.rst diff --git a/Lib/pathlib.py b/Lib/pathlib.py index b728a0b3dfdb6c..c48cff307083a8 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -1415,21 +1415,29 @@ def absolute(self): """ if self.is_absolute(): return self - elif self.drive: + if self.root: + drive = os.path.splitroot(os.getcwd())[0] + return self._from_parsed_parts(drive, self.root, self._tail) + if self.drive: # There is a CWD on each drive-letter drive. cwd = os.path.abspath(self.drive) else: cwd = os.getcwd() + if not self._tail: # Fast path for "empty" paths, e.g. Path("."), Path("") or Path(). # We pass only one argument to with_segments() to avoid the cost # of joining, and we exploit the fact that getcwd() returns a # fully-normalized string by storing it in _str. This is used to # implement Path.cwd(). - if not self.root and not self._tail: - result = self.with_segments(cwd) - result._str = cwd - return result - return self.with_segments(cwd, self) + result = self.with_segments(cwd) + result._str = cwd + return result + drive, root, rel = os.path.splitroot(cwd) + if not rel: + return self._from_parsed_parts(drive, root, self._tail) + tail = rel.split(self.pathmod.sep) + tail.extend(self._tail) + return self._from_parsed_parts(drive, root, tail) def resolve(self, strict=False): """ diff --git a/Misc/NEWS.d/next/Library/2023-12-04-21-30-34.gh-issue-112727.jpgNRB.rst b/Misc/NEWS.d/next/Library/2023-12-04-21-30-34.gh-issue-112727.jpgNRB.rst new file mode 100644 index 00000000000000..bbe7aae5732d9a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-04-21-30-34.gh-issue-112727.jpgNRB.rst @@ -0,0 +1 @@ +Speed up :meth:`pathlib.Path.absolute`. Patch by Barney Gale. From dc824c5dc120ffed84bafd23f95e95a99678ed6a Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 5 Dec 2023 12:23:17 +0800 Subject: [PATCH 173/228] gh-112736: Refactor del-safe symbol handling in subprocess (#112738) Refactor delete-safe symbol handling in subprocess. Only module globals are force-cleared during interpreter finalization, using a class reference instead of individually listing the constants everywhere is simpler. --- Lib/subprocess.py | 48 +++++++++---------- ...-12-05-01-19-28.gh-issue-112736.rdHDrU.rst | 1 + 2 files changed, 25 insertions(+), 24 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-12-05-01-19-28.gh-issue-112736.rdHDrU.rst diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 6df5dd551ea67e..d6edd1a9807d1b 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -74,8 +74,8 @@ else: _mswindows = True -# wasm32-emscripten and wasm32-wasi do not support processes -_can_fork_exec = sys.platform not in {"emscripten", "wasi"} +# some platforms do not support subprocesses +_can_fork_exec = sys.platform not in {"emscripten", "wasi", "ios", "tvos", "watchos"} if _mswindows: import _winapi @@ -103,18 +103,22 @@ if _can_fork_exec: from _posixsubprocess import fork_exec as _fork_exec # used in methods that are called by __del__ - _waitpid = os.waitpid - _waitstatus_to_exitcode = os.waitstatus_to_exitcode - _WIFSTOPPED = os.WIFSTOPPED - _WSTOPSIG = os.WSTOPSIG - _WNOHANG = os.WNOHANG + class _del_safe: + waitpid = os.waitpid + waitstatus_to_exitcode = os.waitstatus_to_exitcode + WIFSTOPPED = os.WIFSTOPPED + WSTOPSIG = os.WSTOPSIG + WNOHANG = os.WNOHANG + ECHILD = errno.ECHILD else: - _fork_exec = None - _waitpid = None - _waitstatus_to_exitcode = None - _WIFSTOPPED = None - _WSTOPSIG = None - _WNOHANG = None + class _del_safe: + waitpid = None + waitstatus_to_exitcode = None + WIFSTOPPED = None + WSTOPSIG = None + WNOHANG = None + ECHILD = errno.ECHILD + import select import selectors @@ -1951,20 +1955,16 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, raise child_exception_type(err_msg) - def _handle_exitstatus(self, sts, - _waitstatus_to_exitcode=_waitstatus_to_exitcode, - _WIFSTOPPED=_WIFSTOPPED, - _WSTOPSIG=_WSTOPSIG): + def _handle_exitstatus(self, sts, _del_safe=_del_safe): """All callers to this function MUST hold self._waitpid_lock.""" # This method is called (indirectly) by __del__, so it cannot # refer to anything outside of its local scope. - if _WIFSTOPPED(sts): - self.returncode = -_WSTOPSIG(sts) + if _del_safe.WIFSTOPPED(sts): + self.returncode = -_del_safe.WSTOPSIG(sts) else: - self.returncode = _waitstatus_to_exitcode(sts) + self.returncode = _del_safe.waitstatus_to_exitcode(sts) - def _internal_poll(self, _deadstate=None, _waitpid=_waitpid, - _WNOHANG=_WNOHANG, _ECHILD=errno.ECHILD): + def _internal_poll(self, _deadstate=None, _del_safe=_del_safe): """Check if child process has terminated. Returns returncode attribute. @@ -1980,13 +1980,13 @@ def _internal_poll(self, _deadstate=None, _waitpid=_waitpid, try: if self.returncode is not None: return self.returncode # Another thread waited. - pid, sts = _waitpid(self.pid, _WNOHANG) + pid, sts = _del_safe.waitpid(self.pid, _del_safe.WNOHANG) if pid == self.pid: self._handle_exitstatus(sts) except OSError as e: if _deadstate is not None: self.returncode = _deadstate - elif e.errno == _ECHILD: + elif e.errno == _del_safe.ECHILD: # This happens if SIGCLD is set to be ignored or # waiting for child processes has otherwise been # disabled for our process. This child is dead, we diff --git a/Misc/NEWS.d/next/Library/2023-12-05-01-19-28.gh-issue-112736.rdHDrU.rst b/Misc/NEWS.d/next/Library/2023-12-05-01-19-28.gh-issue-112736.rdHDrU.rst new file mode 100644 index 00000000000000..6c09e622923af8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-05-01-19-28.gh-issue-112736.rdHDrU.rst @@ -0,0 +1 @@ +The use of del-safe symbols in ``subprocess`` was refactored to allow for use in cross-platform build environments. From aa5bee30abb28d73a838399f4c3a8bcdc5108fe3 Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Tue, 5 Dec 2023 16:24:56 +0900 Subject: [PATCH 174/228] gh-102130: Support tab completion in cmd for Libedit. (GH-107748) --- Co-authored-by: Tian Gao --- Doc/library/cmd.rst | 10 +++++++ Lib/cmd.py | 10 ++++++- Lib/test/test_cmd.py | 30 +++++++++++++++++++ Misc/ACKS | 1 + ...-08-07-21-11-24.gh-issue-102130._UyI5i.rst | 1 + 5 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst diff --git a/Doc/library/cmd.rst b/Doc/library/cmd.rst index fd5df96dfd0b3d..1318ffe5a48d53 100644 --- a/Doc/library/cmd.rst +++ b/Doc/library/cmd.rst @@ -26,6 +26,13 @@ interface. key; it defaults to :kbd:`Tab`. If *completekey* is not :const:`None` and :mod:`readline` is available, command completion is done automatically. + The default, ``'tab'``, is treated specially, so that it refers to the + :kbd:`Tab` key on every :data:`readline.backend`. + Specifically, if :data:`readline.backend` is ``editline``, + ``Cmd`` will use ``'^I'`` instead of ``'tab'``. + Note that other values are not treated this way, and might only work + with a specific backend. + The optional arguments *stdin* and *stdout* specify the input and output file objects that the Cmd instance or subclass instance will use for input and output. If not specified, they will default to :data:`sys.stdin` and @@ -35,6 +42,9 @@ interface. :attr:`use_rawinput` attribute to ``False``, otherwise *stdin* will be ignored. + .. versionchanged:: 3.13 + ``completekey='tab'`` is replaced by ``'^I'`` for ``editline``. + .. _cmd-objects: diff --git a/Lib/cmd.py b/Lib/cmd.py index e933b8dbc1470a..2e358d6cd5a02d 100644 --- a/Lib/cmd.py +++ b/Lib/cmd.py @@ -108,7 +108,15 @@ def cmdloop(self, intro=None): import readline self.old_completer = readline.get_completer() readline.set_completer(self.complete) - readline.parse_and_bind(self.completekey+": complete") + if readline.backend == "editline": + if self.completekey == 'tab': + # libedit uses "^I" instead of "tab" + command_string = "bind ^I rl_complete" + else: + command_string = f"bind {self.completekey} rl_complete" + else: + command_string = f"{self.completekey}: complete" + readline.parse_and_bind(command_string) except ImportError: pass try: diff --git a/Lib/test/test_cmd.py b/Lib/test/test_cmd.py index 951336fa08542d..46ec82b704963d 100644 --- a/Lib/test/test_cmd.py +++ b/Lib/test/test_cmd.py @@ -9,7 +9,10 @@ import doctest import unittest import io +import textwrap from test import support +from test.support.import_helper import import_module +from test.support.pty_helper import run_pty class samplecmdclass(cmd.Cmd): """ @@ -259,6 +262,33 @@ class CmdPrintExceptionClass(cmd.Cmd): def default(self, line): print(sys.exc_info()[:2]) + +@support.requires_subprocess() +class CmdTestReadline(unittest.TestCase): + def setUpClass(): + # Ensure that the readline module is loaded + # If this fails, the test is skipped because SkipTest will be raised + readline = import_module('readline') + + def test_basic_completion(self): + script = textwrap.dedent(""" + import cmd + class simplecmd(cmd.Cmd): + def do_tab_completion_test(self, args): + print('tab completion success') + return True + + simplecmd().cmdloop() + """) + + # 't' and complete 'ab_completion_test' to 'tab_completion_test' + input = b"t\t\n" + + output = run_pty(script, input) + + self.assertIn(b'ab_completion_test', output) + self.assertIn(b'tab completion success', output) + def load_tests(loader, tests, pattern): tests.addTest(doctest.DocTestSuite()) return tests diff --git a/Misc/ACKS b/Misc/ACKS index 1c67d96ed3a528..12335c911ae42a 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -788,6 +788,7 @@ Thomas Holmes Craig Holmquist Philip Homburg Naofumi Honda +Constantin Hong Weipeng Hong Jeffrey Honig Rob Hooft diff --git a/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst b/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst new file mode 100644 index 00000000000000..f582ad5df39e84 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst @@ -0,0 +1 @@ +Support tab completion in :mod:`cmd` for ``editline``. From 9f92b31339945da55559747c420e170c968e9e2b Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 5 Dec 2023 10:34:13 +0300 Subject: [PATCH 175/228] Minor refactoring of Object/abstract.c (UNARY_FUNC macro and more cases for BINARY_FUNC) (GH-112145) * Use BINARY_FUNC macro for some remaining ops * Add UNARY_FUNC macro to define unary PyNumber_* functions --- Objects/abstract.c | 115 ++++++++++----------------------------------- 1 file changed, 25 insertions(+), 90 deletions(-) diff --git a/Objects/abstract.c b/Objects/abstract.c index 43842fbdd6aedd..1ec5c5b8c3dc2f 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -1180,29 +1180,10 @@ PyNumber_Multiply(PyObject *v, PyObject *w) return result; } -PyObject * -PyNumber_MatrixMultiply(PyObject *v, PyObject *w) -{ - return binary_op(v, w, NB_SLOT(nb_matrix_multiply), "@"); -} - -PyObject * -PyNumber_FloorDivide(PyObject *v, PyObject *w) -{ - return binary_op(v, w, NB_SLOT(nb_floor_divide), "//"); -} - -PyObject * -PyNumber_TrueDivide(PyObject *v, PyObject *w) -{ - return binary_op(v, w, NB_SLOT(nb_true_divide), "/"); -} - -PyObject * -PyNumber_Remainder(PyObject *v, PyObject *w) -{ - return binary_op(v, w, NB_SLOT(nb_remainder), "%"); -} +BINARY_FUNC(PyNumber_MatrixMultiply, nb_matrix_multiply, "@") +BINARY_FUNC(PyNumber_FloorDivide, nb_floor_divide, "//") +BINARY_FUNC(PyNumber_TrueDivide, nb_true_divide, "/") +BINARY_FUNC(PyNumber_Remainder, nb_remainder, "%") PyObject * PyNumber_Power(PyObject *v, PyObject *w, PyObject *z) @@ -1379,73 +1360,27 @@ _PyNumber_InPlacePowerNoMod(PyObject *lhs, PyObject *rhs) /* Unary operators and functions */ -PyObject * -PyNumber_Negative(PyObject *o) -{ - if (o == NULL) { - return null_error(); - } - - PyNumberMethods *m = Py_TYPE(o)->tp_as_number; - if (m && m->nb_negative) { - PyObject *res = (*m->nb_negative)(o); - assert(_Py_CheckSlotResult(o, "__neg__", res != NULL)); - return res; - } - - return type_error("bad operand type for unary -: '%.200s'", o); -} - -PyObject * -PyNumber_Positive(PyObject *o) -{ - if (o == NULL) { - return null_error(); - } - - PyNumberMethods *m = Py_TYPE(o)->tp_as_number; - if (m && m->nb_positive) { - PyObject *res = (*m->nb_positive)(o); - assert(_Py_CheckSlotResult(o, "__pos__", res != NULL)); - return res; - } - - return type_error("bad operand type for unary +: '%.200s'", o); -} - -PyObject * -PyNumber_Invert(PyObject *o) -{ - if (o == NULL) { - return null_error(); - } - - PyNumberMethods *m = Py_TYPE(o)->tp_as_number; - if (m && m->nb_invert) { - PyObject *res = (*m->nb_invert)(o); - assert(_Py_CheckSlotResult(o, "__invert__", res != NULL)); - return res; - } - - return type_error("bad operand type for unary ~: '%.200s'", o); -} - -PyObject * -PyNumber_Absolute(PyObject *o) -{ - if (o == NULL) { - return null_error(); - } - - PyNumberMethods *m = Py_TYPE(o)->tp_as_number; - if (m && m->nb_absolute) { - PyObject *res = m->nb_absolute(o); - assert(_Py_CheckSlotResult(o, "__abs__", res != NULL)); - return res; - } - - return type_error("bad operand type for abs(): '%.200s'", o); -} +#define UNARY_FUNC(func, op, meth_name, descr) \ + PyObject * \ + func(PyObject *o) { \ + if (o == NULL) { \ + return null_error(); \ + } \ + \ + PyNumberMethods *m = Py_TYPE(o)->tp_as_number; \ + if (m && m->op) { \ + PyObject *res = (*m->op)(o); \ + assert(_Py_CheckSlotResult(o, #meth_name, res != NULL)); \ + return res; \ + } \ + \ + return type_error("bad operand type for "descr": '%.200s'", o); \ + } + +UNARY_FUNC(PyNumber_Negative, nb_negative, __neg__, "unary -") +UNARY_FUNC(PyNumber_Positive, nb_positive, __pow__, "unary +") +UNARY_FUNC(PyNumber_Invert, nb_invert, __invert__, "unary ~") +UNARY_FUNC(PyNumber_Absolute, nb_absolute, __abs__, "abs()") int From 81ee0260912dc4b55410f3c6ad755b5c4da82f4a Mon Sep 17 00:00:00 2001 From: pan324 <103143968+pan324@users.noreply.github.com> Date: Tue, 5 Dec 2023 09:11:44 +0100 Subject: [PATCH 176/228] gh-82300: Add track parameter to multiprocessing.shared_memory (#110778) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a track parameter to shared memory to allow resource tracking via the side-launched resource tracker process to be disabled on platforms that use it (POSIX). This allows people who do not want automated cleanup at process exit because they are using the shared memory with processes not participating in Python's resource tracking to use the shared_memory API. Co-authored-by: Łukasz Langa Co-authored-by: Guido van Rossum Co-authored-by: Antoine Pitrou Co-authored-by: Gregory P. Smith --- Doc/library/multiprocessing.shared_memory.rst | 51 ++++++++++++------ Lib/multiprocessing/shared_memory.py | 24 ++++++--- Lib/test/_test_multiprocessing.py | 53 +++++++++++++++++++ ...3-10-12-18-19-47.gh-issue-82300.P8-O38.rst | 1 + 4 files changed, 106 insertions(+), 23 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-12-18-19-47.gh-issue-82300.P8-O38.rst diff --git a/Doc/library/multiprocessing.shared_memory.rst b/Doc/library/multiprocessing.shared_memory.rst index f453e6403d932d..671130d9b29fc0 100644 --- a/Doc/library/multiprocessing.shared_memory.rst +++ b/Doc/library/multiprocessing.shared_memory.rst @@ -36,7 +36,7 @@ or other communications requiring the serialization/deserialization and copying of data. -.. class:: SharedMemory(name=None, create=False, size=0) +.. class:: SharedMemory(name=None, create=False, size=0, *, track=True) Creates a new shared memory block or attaches to an existing shared memory block. Each shared memory block is assigned a unique name. @@ -64,26 +64,45 @@ copying of data. memory block may be larger or equal to the size requested. When attaching to an existing shared memory block, the ``size`` parameter is ignored. + *track*, when enabled, registers the shared memory block with a resource + tracker process on platforms where the OS does not do this automatically. + The resource tracker ensures proper cleanup of the shared memory even + if all other processes with access to the memory exit without doing so. + Python processes created from a common ancestor using :mod:`multiprocessing` + facilities share a single resource tracker process, and the lifetime of + shared memory segments is handled automatically among these processes. + Python processes created in any other way will receive their own + resource tracker when accessing shared memory with *track* enabled. + This will cause the shared memory to be deleted by the resource tracker + of the first process that terminates. + To avoid this issue, users of :mod:`subprocess` or standalone Python + processes should set *track* to ``False`` when there is already another + process in place that does the bookkeeping. + *track* is ignored on Windows, which has its own tracking and + automatically deletes shared memory when all handles to it have been closed. + + .. versionchanged:: 3.13 Added *track* parameter. + .. method:: close() - Closes access to the shared memory from this instance. In order to - ensure proper cleanup of resources, all instances should call - ``close()`` once the instance is no longer needed. Note that calling - ``close()`` does not cause the shared memory block itself to be - destroyed. + Closes the file descriptor/handle to the shared memory from this + instance. :meth:`close()` should be called once access to the shared + memory block from this instance is no longer needed. Depending + on operating system, the underlying memory may or may not be freed + even if all handles to it have been closed. To ensure proper cleanup, + use the :meth:`unlink()` method. .. method:: unlink() - Requests that the underlying shared memory block be destroyed. In - order to ensure proper cleanup of resources, ``unlink()`` should be - called once (and only once) across all processes which have need - for the shared memory block. After requesting its destruction, a - shared memory block may or may not be immediately destroyed and - this behavior may differ across platforms. Attempts to access data - inside the shared memory block after ``unlink()`` has been called may - result in memory access errors. Note: the last process relinquishing - its hold on a shared memory block may call ``unlink()`` and - :meth:`close()` in either order. + Deletes the underlying shared memory block. This should be called only + once per shared memory block regardless of the number of handles to it, + even in other processes. + :meth:`unlink()` and :meth:`close()` can be called in any order, but + trying to access data inside a shared memory block after :meth:`unlink()` + may result in memory access errors, depending on platform. + + This method has no effect on Windows, where the only way to delete a + shared memory block is to close all handles. .. attribute:: buf diff --git a/Lib/multiprocessing/shared_memory.py b/Lib/multiprocessing/shared_memory.py index 9a1e5aa17b87a2..67e70fdc27cf31 100644 --- a/Lib/multiprocessing/shared_memory.py +++ b/Lib/multiprocessing/shared_memory.py @@ -71,8 +71,9 @@ class SharedMemory: _flags = os.O_RDWR _mode = 0o600 _prepend_leading_slash = True if _USE_POSIX else False + _track = True - def __init__(self, name=None, create=False, size=0): + def __init__(self, name=None, create=False, size=0, *, track=True): if not size >= 0: raise ValueError("'size' must be a positive integer") if create: @@ -82,6 +83,7 @@ def __init__(self, name=None, create=False, size=0): if name is None and not self._flags & os.O_EXCL: raise ValueError("'name' can only be None if create=True") + self._track = track if _USE_POSIX: # POSIX Shared Memory @@ -116,8 +118,8 @@ def __init__(self, name=None, create=False, size=0): except OSError: self.unlink() raise - - resource_tracker.register(self._name, "shared_memory") + if self._track: + resource_tracker.register(self._name, "shared_memory") else: @@ -236,12 +238,20 @@ def close(self): def unlink(self): """Requests that the underlying shared memory block be destroyed. - In order to ensure proper cleanup of resources, unlink should be - called once (and only once) across all processes which have access - to the shared memory block.""" + Unlink should be called once (and only once) across all handles + which have access to the shared memory block, even if these + handles belong to different processes. Closing and unlinking may + happen in any order, but trying to access data inside a shared + memory block after unlinking may result in memory errors, + depending on platform. + + This method has no effect on Windows, where the only way to + delete a shared memory block is to close all handles.""" + if _USE_POSIX and self._name: _posixshmem.shm_unlink(self._name) - resource_tracker.unregister(self._name, "shared_memory") + if self._track: + resource_tracker.unregister(self._name, "shared_memory") _encoding = "utf8" diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index ec003d8dc4314d..a94eb6c0ae4b8e 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -4455,6 +4455,59 @@ def test_shared_memory_cleaned_after_process_termination(self): "resource_tracker: There appear to be 1 leaked " "shared_memory objects to clean up at shutdown", err) + @unittest.skipIf(os.name != "posix", "resource_tracker is posix only") + def test_shared_memory_untracking(self): + # gh-82300: When a separate Python process accesses shared memory + # with track=False, it must not cause the memory to be deleted + # when terminating. + cmd = '''if 1: + import sys + from multiprocessing.shared_memory import SharedMemory + mem = SharedMemory(create=False, name=sys.argv[1], track=False) + mem.close() + ''' + mem = shared_memory.SharedMemory(create=True, size=10) + # The resource tracker shares pipes with the subprocess, and so + # err existing means that the tracker process has terminated now. + try: + rc, out, err = script_helper.assert_python_ok("-c", cmd, mem.name) + self.assertNotIn(b"resource_tracker", err) + self.assertEqual(rc, 0) + mem2 = shared_memory.SharedMemory(create=False, name=mem.name) + mem2.close() + finally: + try: + mem.unlink() + except OSError: + pass + mem.close() + + @unittest.skipIf(os.name != "posix", "resource_tracker is posix only") + def test_shared_memory_tracking(self): + # gh-82300: When a separate Python process accesses shared memory + # with track=True, it must cause the memory to be deleted when + # terminating. + cmd = '''if 1: + import sys + from multiprocessing.shared_memory import SharedMemory + mem = SharedMemory(create=False, name=sys.argv[1], track=True) + mem.close() + ''' + mem = shared_memory.SharedMemory(create=True, size=10) + try: + rc, out, err = script_helper.assert_python_ok("-c", cmd, mem.name) + self.assertEqual(rc, 0) + self.assertIn( + b"resource_tracker: There appear to be 1 leaked " + b"shared_memory objects to clean up at shutdown", err) + finally: + try: + mem.unlink() + except OSError: + pass + resource_tracker.unregister(mem._name, "shared_memory") + mem.close() + # # Test to verify that `Finalize` works. # diff --git a/Misc/NEWS.d/next/Library/2023-10-12-18-19-47.gh-issue-82300.P8-O38.rst b/Misc/NEWS.d/next/Library/2023-10-12-18-19-47.gh-issue-82300.P8-O38.rst new file mode 100644 index 00000000000000..d7e6b225489b99 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-12-18-19-47.gh-issue-82300.P8-O38.rst @@ -0,0 +1 @@ +Add ``track`` parameter to :class:`multiprocessing.shared_memory.SharedMemory` that allows using shared memory blocks without having to register with the POSIX resource tracker that automatically releases them upon process exit. From d824512059eabbe9d45daeb24d1e2070ad13dd87 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Tue, 5 Dec 2023 09:03:32 +0000 Subject: [PATCH 177/228] gh-112535: Update _Py_ThreadId() to support PowerPC (gh-112624) --- Include/object.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Include/object.h b/Include/object.h index 81f777ad21f2f9..dfeb43bda7d841 100644 --- a/Include/object.h +++ b/Include/object.h @@ -261,6 +261,22 @@ _Py_ThreadId(void) __asm__ ("mrs %0, tpidrro_el0" : "=r" (tid)); #elif defined(__aarch64__) __asm__ ("mrs %0, tpidr_el0" : "=r" (tid)); +#elif defined(__powerpc64__) + #if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer) + tid = (uintptr_t)__builtin_thread_pointer(); + #else + register uintptr_t tp __asm__ ("r13"); + __asm__("" : "=r" (tp)); + tid = tp; + #endif +#elif defined(__powerpc__) + #if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer) + tid = (uintptr_t)__builtin_thread_pointer(); + #else + register uintptr_t tp __asm__ ("r2"); + __asm__ ("" : "=r" (tp)); + tid = tp; + #endif #else # error "define _Py_ThreadId for this platform" #endif From b31232ddf7f219ca8ff9e8d0401c02eb0b6ffec3 Mon Sep 17 00:00:00 2001 From: Rune Tynan Date: Tue, 5 Dec 2023 04:21:09 -0500 Subject: [PATCH 178/228] gh-62897: Update PyUnicode C API parameter names (GH-12680) Standardize PyUnicode C API parameter names across the documentation. Co-authored-by: Serhiy Storchaka --- Doc/c-api/unicode.rst | 182 +++++++++++++++++++++--------------------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index e654412965a727..5541eaa521803b 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -75,19 +75,19 @@ Python: The following APIs are C macros and static inlined functions for fast checks and access to internal read-only data of Unicode objects: -.. c:function:: int PyUnicode_Check(PyObject *o) +.. c:function:: int PyUnicode_Check(PyObject *obj) - Return true if the object *o* is a Unicode object or an instance of a Unicode + Return true if the object *obj* is a Unicode object or an instance of a Unicode subtype. This function always succeeds. -.. c:function:: int PyUnicode_CheckExact(PyObject *o) +.. c:function:: int PyUnicode_CheckExact(PyObject *obj) - Return true if the object *o* is a Unicode object, but not an instance of a + Return true if the object *obj* is a Unicode object, but not an instance of a subtype. This function always succeeds. -.. c:function:: int PyUnicode_READY(PyObject *o) +.. c:function:: int PyUnicode_READY(PyObject *unicode) Returns ``0``. This API is kept only for backward compatibility. @@ -97,17 +97,17 @@ access to internal read-only data of Unicode objects: This API does nothing since Python 3.12. -.. c:function:: Py_ssize_t PyUnicode_GET_LENGTH(PyObject *o) +.. c:function:: Py_ssize_t PyUnicode_GET_LENGTH(PyObject *unicode) - Return the length of the Unicode string, in code points. *o* has to be a + Return the length of the Unicode string, in code points. *unicode* has to be a Unicode object in the "canonical" representation (not checked). .. versionadded:: 3.3 -.. c:function:: Py_UCS1* PyUnicode_1BYTE_DATA(PyObject *o) - Py_UCS2* PyUnicode_2BYTE_DATA(PyObject *o) - Py_UCS4* PyUnicode_4BYTE_DATA(PyObject *o) +.. c:function:: Py_UCS1* PyUnicode_1BYTE_DATA(PyObject *unicode) + Py_UCS2* PyUnicode_2BYTE_DATA(PyObject *unicode) + Py_UCS4* PyUnicode_4BYTE_DATA(PyObject *unicode) Return a pointer to the canonical representation cast to UCS1, UCS2 or UCS4 integer types for direct character access. No checks are performed if the @@ -129,18 +129,18 @@ access to internal read-only data of Unicode objects: ``PyUnicode_WCHAR_KIND`` has been removed. -.. c:function:: int PyUnicode_KIND(PyObject *o) +.. c:function:: int PyUnicode_KIND(PyObject *unicode) Return one of the PyUnicode kind constants (see above) that indicate how many - bytes per character this Unicode object uses to store its data. *o* has to + bytes per character this Unicode object uses to store its data. *unicode* has to be a Unicode object in the "canonical" representation (not checked). .. versionadded:: 3.3 -.. c:function:: void* PyUnicode_DATA(PyObject *o) +.. c:function:: void* PyUnicode_DATA(PyObject *unicode) - Return a void pointer to the raw Unicode buffer. *o* has to be a Unicode + Return a void pointer to the raw Unicode buffer. *unicode* has to be a Unicode object in the "canonical" representation (not checked). .. versionadded:: 3.3 @@ -168,25 +168,25 @@ access to internal read-only data of Unicode objects: .. versionadded:: 3.3 -.. c:function:: Py_UCS4 PyUnicode_READ_CHAR(PyObject *o, Py_ssize_t index) +.. c:function:: Py_UCS4 PyUnicode_READ_CHAR(PyObject *unicode, Py_ssize_t index) - Read a character from a Unicode object *o*, which must be in the "canonical" + Read a character from a Unicode object *unicode*, which must be in the "canonical" representation. This is less efficient than :c:func:`PyUnicode_READ` if you do multiple consecutive reads. .. versionadded:: 3.3 -.. c:function:: Py_UCS4 PyUnicode_MAX_CHAR_VALUE(PyObject *o) +.. c:function:: Py_UCS4 PyUnicode_MAX_CHAR_VALUE(PyObject *unicode) Return the maximum code point that is suitable for creating another string - based on *o*, which must be in the "canonical" representation. This is + based on *unicode*, which must be in the "canonical" representation. This is always an approximation but more efficient than iterating over the string. .. versionadded:: 3.3 -.. c:function:: int PyUnicode_IsIdentifier(PyObject *o) +.. c:function:: int PyUnicode_IsIdentifier(PyObject *unicode) Return ``1`` if the string is a valid identifier according to the language definition, section :ref:`identifiers`. Return ``0`` otherwise. @@ -358,9 +358,9 @@ APIs: .. versionadded:: 3.3 -.. c:function:: PyObject* PyUnicode_FromStringAndSize(const char *u, Py_ssize_t size) +.. c:function:: PyObject* PyUnicode_FromStringAndSize(const char *str, Py_ssize_t size) - Create a Unicode object from the char buffer *u*. The bytes will be + Create a Unicode object from the char buffer *str*. The bytes will be interpreted as being UTF-8 encoded. The buffer is copied into the new object. The return value might be a shared object, i.e. modification of the data is @@ -369,16 +369,16 @@ APIs: This function raises :exc:`SystemError` when: * *size* < 0, - * *u* is ``NULL`` and *size* > 0 + * *str* is ``NULL`` and *size* > 0 .. versionchanged:: 3.12 - *u* == ``NULL`` with *size* > 0 is not allowed anymore. + *str* == ``NULL`` with *size* > 0 is not allowed anymore. -.. c:function:: PyObject *PyUnicode_FromString(const char *u) +.. c:function:: PyObject *PyUnicode_FromString(const char *str) Create a Unicode object from a UTF-8 encoded null-terminated char buffer - *u*. + *str*. .. c:function:: PyObject* PyUnicode_FromFormat(const char *format, ...) @@ -646,29 +646,29 @@ APIs: .. versionadded:: 3.3 -.. c:function:: PyObject* PyUnicode_Substring(PyObject *str, Py_ssize_t start, \ +.. c:function:: PyObject* PyUnicode_Substring(PyObject *unicode, Py_ssize_t start, \ Py_ssize_t end) - Return a substring of *str*, from character index *start* (included) to + Return a substring of *unicode*, from character index *start* (included) to character index *end* (excluded). Negative indices are not supported. .. versionadded:: 3.3 -.. c:function:: Py_UCS4* PyUnicode_AsUCS4(PyObject *u, Py_UCS4 *buffer, \ +.. c:function:: Py_UCS4* PyUnicode_AsUCS4(PyObject *unicode, Py_UCS4 *buffer, \ Py_ssize_t buflen, int copy_null) - Copy the string *u* into a UCS4 buffer, including a null character, if + Copy the string *unicode* into a UCS4 buffer, including a null character, if *copy_null* is set. Returns ``NULL`` and sets an exception on error (in particular, a :exc:`SystemError` if *buflen* is smaller than the length of - *u*). *buffer* is returned on success. + *unicode*). *buffer* is returned on success. .. versionadded:: 3.3 -.. c:function:: Py_UCS4* PyUnicode_AsUCS4Copy(PyObject *u) +.. c:function:: Py_UCS4* PyUnicode_AsUCS4Copy(PyObject *unicode) - Copy the string *u* into a new UCS4 buffer that is allocated using + Copy the string *unicode* into a new UCS4 buffer that is allocated using :c:func:`PyMem_Malloc`. If this fails, ``NULL`` is returned with a :exc:`MemoryError` set. The returned buffer always has an extra null code point appended. @@ -683,7 +683,7 @@ The current locale encoding can be used to decode text from the operating system. .. c:function:: PyObject* PyUnicode_DecodeLocaleAndSize(const char *str, \ - Py_ssize_t len, \ + Py_ssize_t length, \ const char *errors) Decode a string from UTF-8 on Android and VxWorks, or from the current @@ -788,7 +788,7 @@ conversion function: Accepts a :term:`path-like object`. -.. c:function:: PyObject* PyUnicode_DecodeFSDefaultAndSize(const char *s, Py_ssize_t size) +.. c:function:: PyObject* PyUnicode_DecodeFSDefaultAndSize(const char *str, Py_ssize_t size) Decode a string from the :term:`filesystem encoding and error handler`. @@ -804,7 +804,7 @@ conversion function: handler>` is now used. -.. c:function:: PyObject* PyUnicode_DecodeFSDefault(const char *s) +.. c:function:: PyObject* PyUnicode_DecodeFSDefault(const char *str) Decode a null-terminated string from the :term:`filesystem encoding and error handler`. @@ -841,17 +841,17 @@ wchar_t Support :c:type:`wchar_t` support for platforms which support it: -.. c:function:: PyObject* PyUnicode_FromWideChar(const wchar_t *w, Py_ssize_t size) +.. c:function:: PyObject* PyUnicode_FromWideChar(const wchar_t *wstr, Py_ssize_t size) - Create a Unicode object from the :c:type:`wchar_t` buffer *w* of the given *size*. + Create a Unicode object from the :c:type:`wchar_t` buffer *wstr* of the given *size*. Passing ``-1`` as the *size* indicates that the function must itself compute the length, - using wcslen. + using :c:func:`!wcslen`. Return ``NULL`` on failure. -.. c:function:: Py_ssize_t PyUnicode_AsWideChar(PyObject *unicode, wchar_t *w, Py_ssize_t size) +.. c:function:: Py_ssize_t PyUnicode_AsWideChar(PyObject *unicode, wchar_t *wstr, Py_ssize_t size) - Copy the Unicode object contents into the :c:type:`wchar_t` buffer *w*. At most + Copy the Unicode object contents into the :c:type:`wchar_t` buffer *wstr*. At most *size* :c:type:`wchar_t` characters are copied (excluding a possibly trailing null termination character). Return the number of :c:type:`wchar_t` characters copied or ``-1`` in case of an error. Note that the resulting :c:expr:`wchar_t*` @@ -915,10 +915,10 @@ Generic Codecs These are the generic codec APIs: -.. c:function:: PyObject* PyUnicode_Decode(const char *s, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_Decode(const char *str, Py_ssize_t size, \ const char *encoding, const char *errors) - Create a Unicode object by decoding *size* bytes of the encoded string *s*. + Create a Unicode object by decoding *size* bytes of the encoded string *str*. *encoding* and *errors* have the same meaning as the parameters of the same name in the :func:`str` built-in function. The codec to be used is looked up using the Python codec registry. Return ``NULL`` if an exception was raised by @@ -941,13 +941,13 @@ UTF-8 Codecs These are the UTF-8 codec APIs: -.. c:function:: PyObject* PyUnicode_DecodeUTF8(const char *s, Py_ssize_t size, const char *errors) +.. c:function:: PyObject* PyUnicode_DecodeUTF8(const char *str, Py_ssize_t size, const char *errors) Create a Unicode object by decoding *size* bytes of the UTF-8 encoded string - *s*. Return ``NULL`` if an exception was raised by the codec. + *str*. Return ``NULL`` if an exception was raised by the codec. -.. c:function:: PyObject* PyUnicode_DecodeUTF8Stateful(const char *s, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_DecodeUTF8Stateful(const char *str, Py_ssize_t size, \ const char *errors, Py_ssize_t *consumed) If *consumed* is ``NULL``, behave like :c:func:`PyUnicode_DecodeUTF8`. If @@ -1004,7 +1004,7 @@ UTF-32 Codecs These are the UTF-32 codec APIs: -.. c:function:: PyObject* PyUnicode_DecodeUTF32(const char *s, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_DecodeUTF32(const char *str, Py_ssize_t size, \ const char *errors, int *byteorder) Decode *size* bytes from a UTF-32 encoded buffer string and return the @@ -1031,7 +1031,7 @@ These are the UTF-32 codec APIs: Return ``NULL`` if an exception was raised by the codec. -.. c:function:: PyObject* PyUnicode_DecodeUTF32Stateful(const char *s, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_DecodeUTF32Stateful(const char *str, Py_ssize_t size, \ const char *errors, int *byteorder, Py_ssize_t *consumed) If *consumed* is ``NULL``, behave like :c:func:`PyUnicode_DecodeUTF32`. If @@ -1054,7 +1054,7 @@ UTF-16 Codecs These are the UTF-16 codec APIs: -.. c:function:: PyObject* PyUnicode_DecodeUTF16(const char *s, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_DecodeUTF16(const char *str, Py_ssize_t size, \ const char *errors, int *byteorder) Decode *size* bytes from a UTF-16 encoded buffer string and return the @@ -1082,7 +1082,7 @@ These are the UTF-16 codec APIs: Return ``NULL`` if an exception was raised by the codec. -.. c:function:: PyObject* PyUnicode_DecodeUTF16Stateful(const char *s, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_DecodeUTF16Stateful(const char *str, Py_ssize_t size, \ const char *errors, int *byteorder, Py_ssize_t *consumed) If *consumed* is ``NULL``, behave like :c:func:`PyUnicode_DecodeUTF16`. If @@ -1105,13 +1105,13 @@ UTF-7 Codecs These are the UTF-7 codec APIs: -.. c:function:: PyObject* PyUnicode_DecodeUTF7(const char *s, Py_ssize_t size, const char *errors) +.. c:function:: PyObject* PyUnicode_DecodeUTF7(const char *str, Py_ssize_t size, const char *errors) Create a Unicode object by decoding *size* bytes of the UTF-7 encoded string - *s*. Return ``NULL`` if an exception was raised by the codec. + *str*. Return ``NULL`` if an exception was raised by the codec. -.. c:function:: PyObject* PyUnicode_DecodeUTF7Stateful(const char *s, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_DecodeUTF7Stateful(const char *str, Py_ssize_t size, \ const char *errors, Py_ssize_t *consumed) If *consumed* is ``NULL``, behave like :c:func:`PyUnicode_DecodeUTF7`. If @@ -1126,11 +1126,11 @@ Unicode-Escape Codecs These are the "Unicode Escape" codec APIs: -.. c:function:: PyObject* PyUnicode_DecodeUnicodeEscape(const char *s, \ +.. c:function:: PyObject* PyUnicode_DecodeUnicodeEscape(const char *str, \ Py_ssize_t size, const char *errors) Create a Unicode object by decoding *size* bytes of the Unicode-Escape encoded - string *s*. Return ``NULL`` if an exception was raised by the codec. + string *str*. Return ``NULL`` if an exception was raised by the codec. .. c:function:: PyObject* PyUnicode_AsUnicodeEscapeString(PyObject *unicode) @@ -1146,11 +1146,11 @@ Raw-Unicode-Escape Codecs These are the "Raw Unicode Escape" codec APIs: -.. c:function:: PyObject* PyUnicode_DecodeRawUnicodeEscape(const char *s, \ +.. c:function:: PyObject* PyUnicode_DecodeRawUnicodeEscape(const char *str, \ Py_ssize_t size, const char *errors) Create a Unicode object by decoding *size* bytes of the Raw-Unicode-Escape - encoded string *s*. Return ``NULL`` if an exception was raised by the codec. + encoded string *str*. Return ``NULL`` if an exception was raised by the codec. .. c:function:: PyObject* PyUnicode_AsRawUnicodeEscapeString(PyObject *unicode) @@ -1167,10 +1167,10 @@ These are the Latin-1 codec APIs: Latin-1 corresponds to the first 256 Unicode ordinals and only these are accepted by the codecs during encoding. -.. c:function:: PyObject* PyUnicode_DecodeLatin1(const char *s, Py_ssize_t size, const char *errors) +.. c:function:: PyObject* PyUnicode_DecodeLatin1(const char *str, Py_ssize_t size, const char *errors) Create a Unicode object by decoding *size* bytes of the Latin-1 encoded string - *s*. Return ``NULL`` if an exception was raised by the codec. + *str*. Return ``NULL`` if an exception was raised by the codec. .. c:function:: PyObject* PyUnicode_AsLatin1String(PyObject *unicode) @@ -1187,10 +1187,10 @@ These are the ASCII codec APIs. Only 7-bit ASCII data is accepted. All other codes generate errors. -.. c:function:: PyObject* PyUnicode_DecodeASCII(const char *s, Py_ssize_t size, const char *errors) +.. c:function:: PyObject* PyUnicode_DecodeASCII(const char *str, Py_ssize_t size, const char *errors) Create a Unicode object by decoding *size* bytes of the ASCII encoded string - *s*. Return ``NULL`` if an exception was raised by the codec. + *str*. Return ``NULL`` if an exception was raised by the codec. .. c:function:: PyObject* PyUnicode_AsASCIIString(PyObject *unicode) @@ -1211,10 +1211,10 @@ decode characters. The mapping objects provided must support the These are the mapping codec APIs: -.. c:function:: PyObject* PyUnicode_DecodeCharmap(const char *data, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_DecodeCharmap(const char *str, Py_ssize_t length, \ PyObject *mapping, const char *errors) - Create a Unicode object by decoding *size* bytes of the encoded string *s* + Create a Unicode object by decoding *size* bytes of the encoded string *str* using the given *mapping* object. Return ``NULL`` if an exception was raised by the codec. @@ -1241,7 +1241,7 @@ These are the mapping codec APIs: The following codec API is special in that maps Unicode to Unicode. -.. c:function:: PyObject* PyUnicode_Translate(PyObject *str, PyObject *table, const char *errors) +.. c:function:: PyObject* PyUnicode_Translate(PyObject *unicode, PyObject *table, const char *errors) Translate a string by applying a character mapping table to it and return the resulting Unicode object. Return ``NULL`` if an exception was raised by the @@ -1266,13 +1266,13 @@ use the Win32 MBCS converters to implement the conversions. Note that MBCS (or DBCS) is a class of encodings, not just one. The target encoding is defined by the user settings on the machine running the codec. -.. c:function:: PyObject* PyUnicode_DecodeMBCS(const char *s, Py_ssize_t size, const char *errors) +.. c:function:: PyObject* PyUnicode_DecodeMBCS(const char *str, Py_ssize_t size, const char *errors) - Create a Unicode object by decoding *size* bytes of the MBCS encoded string *s*. + Create a Unicode object by decoding *size* bytes of the MBCS encoded string *str*. Return ``NULL`` if an exception was raised by the codec. -.. c:function:: PyObject* PyUnicode_DecodeMBCSStateful(const char *s, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_DecodeMBCSStateful(const char *str, Py_ssize_t size, \ const char *errors, Py_ssize_t *consumed) If *consumed* is ``NULL``, behave like :c:func:`PyUnicode_DecodeMBCS`. If @@ -1318,7 +1318,7 @@ They all return ``NULL`` or ``-1`` if an exception occurs. Concat two strings giving a new Unicode string. -.. c:function:: PyObject* PyUnicode_Split(PyObject *s, PyObject *sep, Py_ssize_t maxsplit) +.. c:function:: PyObject* PyUnicode_Split(PyObject *unicode, PyObject *sep, Py_ssize_t maxsplit) Split a string giving a list of Unicode strings. If *sep* is ``NULL``, splitting will be done at all whitespace substrings. Otherwise, splits occur at the given @@ -1326,10 +1326,10 @@ They all return ``NULL`` or ``-1`` if an exception occurs. set. Separators are not included in the resulting list. -.. c:function:: PyObject* PyUnicode_Splitlines(PyObject *s, int keepend) +.. c:function:: PyObject* PyUnicode_Splitlines(PyObject *unicode, int keepends) Split a Unicode string at line breaks, returning a list of Unicode strings. - CRLF is considered to be one line break. If *keepend* is ``0``, the line break + CRLF is considered to be one line break. If *keepends* is ``0``, the Line break characters are not included in the resulting strings. @@ -1339,28 +1339,28 @@ They all return ``NULL`` or ``-1`` if an exception occurs. Unicode string. -.. c:function:: Py_ssize_t PyUnicode_Tailmatch(PyObject *str, PyObject *substr, \ +.. c:function:: Py_ssize_t PyUnicode_Tailmatch(PyObject *unicode, PyObject *substr, \ Py_ssize_t start, Py_ssize_t end, int direction) - Return ``1`` if *substr* matches ``str[start:end]`` at the given tail end + Return ``1`` if *substr* matches ``unicode[start:end]`` at the given tail end (*direction* == ``-1`` means to do a prefix match, *direction* == ``1`` a suffix match), ``0`` otherwise. Return ``-1`` if an error occurred. -.. c:function:: Py_ssize_t PyUnicode_Find(PyObject *str, PyObject *substr, \ +.. c:function:: Py_ssize_t PyUnicode_Find(PyObject *unicode, PyObject *substr, \ Py_ssize_t start, Py_ssize_t end, int direction) - Return the first position of *substr* in ``str[start:end]`` using the given + Return the first position of *substr* in ``unicode[start:end]`` using the given *direction* (*direction* == ``1`` means to do a forward search, *direction* == ``-1`` a backward search). The return value is the index of the first match; a value of ``-1`` indicates that no match was found, and ``-2`` indicates that an error occurred and an exception has been set. -.. c:function:: Py_ssize_t PyUnicode_FindChar(PyObject *str, Py_UCS4 ch, \ +.. c:function:: Py_ssize_t PyUnicode_FindChar(PyObject *unicode, Py_UCS4 ch, \ Py_ssize_t start, Py_ssize_t end, int direction) - Return the first position of the character *ch* in ``str[start:end]`` using + Return the first position of the character *ch* in ``unicode[start:end]`` using the given *direction* (*direction* == ``1`` means to do a forward search, *direction* == ``-1`` a backward search). The return value is the index of the first match; a value of ``-1`` indicates that no match was found, and ``-2`` @@ -1369,20 +1369,20 @@ They all return ``NULL`` or ``-1`` if an exception occurs. .. versionadded:: 3.3 .. versionchanged:: 3.7 - *start* and *end* are now adjusted to behave like ``str[start:end]``. + *start* and *end* are now adjusted to behave like ``unicode[start:end]``. -.. c:function:: Py_ssize_t PyUnicode_Count(PyObject *str, PyObject *substr, \ +.. c:function:: Py_ssize_t PyUnicode_Count(PyObject *unicode, PyObject *substr, \ Py_ssize_t start, Py_ssize_t end) Return the number of non-overlapping occurrences of *substr* in - ``str[start:end]``. Return ``-1`` if an error occurred. + ``unicode[start:end]``. Return ``-1`` if an error occurred. -.. c:function:: PyObject* PyUnicode_Replace(PyObject *str, PyObject *substr, \ +.. c:function:: PyObject* PyUnicode_Replace(PyObject *unicode, PyObject *substr, \ PyObject *replstr, Py_ssize_t maxcount) - Replace at most *maxcount* occurrences of *substr* in *str* with *replstr* and + Replace at most *maxcount* occurrences of *substr* in *unicode* with *replstr* and return the resulting Unicode object. *maxcount* == ``-1`` means replace all occurrences. @@ -1418,9 +1418,9 @@ They all return ``NULL`` or ``-1`` if an exception occurs. .. versionadded:: 3.13 -.. c:function:: int PyUnicode_CompareWithASCIIString(PyObject *uni, const char *string) +.. c:function:: int PyUnicode_CompareWithASCIIString(PyObject *unicode, const char *string) - Compare a Unicode object, *uni*, with *string* and return ``-1``, ``0``, ``1`` for less + Compare a Unicode object, *unicode*, with *string* and return ``-1``, ``0``, ``1`` for less than, equal, and greater than, respectively. It is best to pass only ASCII-encoded strings, but the function interprets the input string as ISO-8859-1 if it contains non-ASCII characters. @@ -1428,7 +1428,7 @@ They all return ``NULL`` or ``-1`` if an exception occurs. This function does not raise exceptions. -.. c:function:: PyObject* PyUnicode_RichCompare(PyObject *left, PyObject *right, int op) +.. c:function:: PyObject* PyUnicode_RichCompare(PyObject *left, PyObject *right, int op) Rich compare two Unicode strings and return one of the following: @@ -1446,29 +1446,29 @@ They all return ``NULL`` or ``-1`` if an exception occurs. ``format % args``. -.. c:function:: int PyUnicode_Contains(PyObject *container, PyObject *element) +.. c:function:: int PyUnicode_Contains(PyObject *unicode, PyObject *substr) - Check whether *element* is contained in *container* and return true or false + Check whether *substr* is contained in *unicode* and return true or false accordingly. - *element* has to coerce to a one element Unicode string. ``-1`` is returned + *substr* has to coerce to a one element Unicode string. ``-1`` is returned if there was an error. -.. c:function:: void PyUnicode_InternInPlace(PyObject **string) +.. c:function:: void PyUnicode_InternInPlace(PyObject **p_unicode) - Intern the argument *\*string* in place. The argument must be the address of a + Intern the argument :c:expr:`*p_unicode` in place. The argument must be the address of a pointer variable pointing to a Python Unicode string object. If there is an - existing interned string that is the same as *\*string*, it sets *\*string* to + existing interned string that is the same as :c:expr:`*p_unicode`, it sets :c:expr:`*p_unicode` to it (releasing the reference to the old string object and creating a new :term:`strong reference` to the interned string object), otherwise it leaves - *\*string* alone and interns it (creating a new :term:`strong reference`). + :c:expr:`*p_unicode` alone and interns it (creating a new :term:`strong reference`). (Clarification: even though there is a lot of talk about references, think of this function as reference-neutral; you own the object after the call if and only if you owned it before the call.) -.. c:function:: PyObject* PyUnicode_InternFromString(const char *v) +.. c:function:: PyObject* PyUnicode_InternFromString(const char *str) A combination of :c:func:`PyUnicode_FromString` and :c:func:`PyUnicode_InternInPlace`, returning either a new Unicode string From 268415bbb32b1fafccae3d542c43d487b6f0f48d Mon Sep 17 00:00:00 2001 From: Jeffrey Kintscher <49998481+websurfer5@users.noreply.github.com> Date: Tue, 5 Dec 2023 01:33:51 -0800 Subject: [PATCH 179/228] gh-81441: shutil.rmtree() FileNotFoundError race condition (GH-14064) Ignore missing files and directories while enumerating directory entries in shutil.rmtree(). Co-authored-by: Serhiy Storchaka --- Doc/library/shutil.rst | 4 ++ Lib/shutil.py | 45 +++++++++++---- Lib/test/test_shutil.py | 57 +++++++++++++++++++ .../2019-06-14-22-37-32.bpo-37260.oecdIf.rst | 2 + 4 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2019-06-14-22-37-32.bpo-37260.oecdIf.rst diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index d1949d698f5614..d30d289710b129 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -343,6 +343,10 @@ Directory and files operations .. versionchanged:: 3.12 Added the *onexc* parameter, deprecated *onerror*. + .. versionchanged:: 3.13 + :func:`!rmtree` now ignores :exc:`FileNotFoundError` exceptions for all + but the top-level path. + .. attribute:: rmtree.avoids_symlink_attacks Indicates whether the current platform and implementation provides a diff --git a/Lib/shutil.py b/Lib/shutil.py index dd93872e83c9e2..93b00d73a0fd46 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -590,23 +590,21 @@ def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, dirs_exist_ok=dirs_exist_ok) if hasattr(os.stat_result, 'st_file_attributes'): - def _rmtree_islink(path): - try: - st = os.lstat(path) - return (stat.S_ISLNK(st.st_mode) or - (st.st_file_attributes & stat.FILE_ATTRIBUTE_REPARSE_POINT - and st.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT)) - except OSError: - return False + def _rmtree_islink(st): + return (stat.S_ISLNK(st.st_mode) or + (st.st_file_attributes & stat.FILE_ATTRIBUTE_REPARSE_POINT + and st.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT)) else: - def _rmtree_islink(path): - return os.path.islink(path) + def _rmtree_islink(st): + return stat.S_ISLNK(st.st_mode) # version vulnerable to race conditions def _rmtree_unsafe(path, onexc): try: with os.scandir(path) as scandir_it: entries = list(scandir_it) + except FileNotFoundError: + return except OSError as err: onexc(os.scandir, path, err) entries = [] @@ -614,6 +612,8 @@ def _rmtree_unsafe(path, onexc): fullname = entry.path try: is_dir = entry.is_dir(follow_symlinks=False) + except FileNotFoundError: + continue except OSError: is_dir = False @@ -624,6 +624,8 @@ def _rmtree_unsafe(path, onexc): # a directory with a symlink after the call to # os.scandir or entry.is_dir above. raise OSError("Cannot call rmtree on a symbolic link") + except FileNotFoundError: + continue except OSError as err: onexc(os.path.islink, fullname, err) continue @@ -631,10 +633,14 @@ def _rmtree_unsafe(path, onexc): else: try: os.unlink(fullname) + except FileNotFoundError: + continue except OSError as err: onexc(os.unlink, fullname, err) try: os.rmdir(path) + except FileNotFoundError: + pass except OSError as err: onexc(os.rmdir, path, err) @@ -643,6 +649,8 @@ def _rmtree_safe_fd(topfd, path, onexc): try: with os.scandir(topfd) as scandir_it: entries = list(scandir_it) + except FileNotFoundError: + return except OSError as err: err.filename = path onexc(os.scandir, path, err) @@ -651,6 +659,8 @@ def _rmtree_safe_fd(topfd, path, onexc): fullname = os.path.join(path, entry.name) try: is_dir = entry.is_dir(follow_symlinks=False) + except FileNotFoundError: + continue except OSError: is_dir = False else: @@ -658,6 +668,8 @@ def _rmtree_safe_fd(topfd, path, onexc): try: orig_st = entry.stat(follow_symlinks=False) is_dir = stat.S_ISDIR(orig_st.st_mode) + except FileNotFoundError: + continue except OSError as err: onexc(os.lstat, fullname, err) continue @@ -665,6 +677,8 @@ def _rmtree_safe_fd(topfd, path, onexc): try: dirfd = os.open(entry.name, os.O_RDONLY, dir_fd=topfd) dirfd_closed = False + except FileNotFoundError: + continue except OSError as err: onexc(os.open, fullname, err) else: @@ -675,6 +689,8 @@ def _rmtree_safe_fd(topfd, path, onexc): os.close(dirfd) dirfd_closed = True os.rmdir(entry.name, dir_fd=topfd) + except FileNotFoundError: + continue except OSError as err: onexc(os.rmdir, fullname, err) else: @@ -692,6 +708,8 @@ def _rmtree_safe_fd(topfd, path, onexc): else: try: os.unlink(entry.name, dir_fd=topfd) + except FileNotFoundError: + continue except OSError as err: onexc(os.unlink, fullname, err) @@ -781,7 +799,12 @@ def onexc(*args): if dir_fd is not None: raise NotImplementedError("dir_fd unavailable on this platform") try: - if _rmtree_islink(path): + st = os.lstat(path) + except OSError as err: + onexc(os.lstat, path, err) + return + try: + if _rmtree_islink(st): # symlinks to directories are forbidden, see bug #1669 raise OSError("Cannot call rmtree on a symbolic link") except OSError as err: diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index ae6c6814fcc3ec..7ea2496230da47 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -633,6 +633,63 @@ def test_rmtree_on_junction(self): finally: shutil.rmtree(TESTFN, ignore_errors=True) + @unittest.skipIf(sys.platform[:6] == 'cygwin', + "This test can't be run on Cygwin (issue #1071513).") + @os_helper.skip_if_dac_override + @os_helper.skip_unless_working_chmod + def test_rmtree_deleted_race_condition(self): + # bpo-37260 + # + # Test that a file or a directory deleted after it is enumerated + # by scandir() but before unlink() or rmdr() is called doesn't + # generate any errors. + def _onexc(fn, path, exc): + assert fn in (os.rmdir, os.unlink) + if not isinstance(exc, PermissionError): + raise + # Make the parent and the children writeable. + for p, mode in zip(paths, old_modes): + os.chmod(p, mode) + # Remove other dirs except one. + keep = next(p for p in dirs if p != path) + for p in dirs: + if p != keep: + os.rmdir(p) + # Remove other files except one. + keep = next(p for p in files if p != path) + for p in files: + if p != keep: + os.unlink(p) + + os.mkdir(TESTFN) + paths = [TESTFN] + [os.path.join(TESTFN, f'child{i}') + for i in range(6)] + dirs = paths[1::2] + files = paths[2::2] + for path in dirs: + os.mkdir(path) + for path in files: + write_file(path, '') + + old_modes = [os.stat(path).st_mode for path in paths] + + # Make the parent and the children non-writeable. + new_mode = stat.S_IREAD|stat.S_IEXEC + for path in reversed(paths): + os.chmod(path, new_mode) + + try: + shutil.rmtree(TESTFN, onexc=_onexc) + except: + # Test failed, so cleanup artifacts. + for path, mode in zip(paths, old_modes): + try: + os.chmod(path, mode) + except OSError: + pass + shutil.rmtree(TESTFN) + raise + class TestCopyTree(BaseTest, unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2019-06-14-22-37-32.bpo-37260.oecdIf.rst b/Misc/NEWS.d/next/Library/2019-06-14-22-37-32.bpo-37260.oecdIf.rst new file mode 100644 index 00000000000000..a5f2c5e8e18919 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-14-22-37-32.bpo-37260.oecdIf.rst @@ -0,0 +1,2 @@ +Fixed a race condition in :func:`shutil.rmtree` in which directory entries removed by another process or thread while ``shutil.rmtree()`` is running can cause it to raise FileNotFoundError. Patch by Jeffrey Kintscher. + From 2f20cafdbfc39925f9374f36f9d53bac365ed32a Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 5 Dec 2023 09:59:52 +0000 Subject: [PATCH 180/228] gh-101100: Fix many easily solvable Sphinx nitpicks in the datamodel docs (#112737) --- Doc/library/exceptions.rst | 8 +++-- Doc/reference/datamodel.rst | 58 ++++++++++++++++++++----------------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index cd85df8723a76b..b67215b8b3a362 100644 --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -429,9 +429,11 @@ The following exceptions are the exceptions that are usually raised. :meth:`~iterator.__next__` method to signal that there are no further items produced by the iterator. - The exception object has a single attribute :attr:`value`, which is - given as an argument when constructing the exception, and defaults - to :const:`None`. + .. attribute:: StopIteration.value + + The exception object has a single attribute :attr:`!value`, which is + given as an argument when constructing the exception, and defaults + to :const:`None`. When a :term:`generator` or :term:`coroutine` function returns, a new :exc:`StopIteration` instance is diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 29298b79ef06dd..06e61393fccc24 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -88,7 +88,7 @@ Some objects contain references to "external" resources such as open files or windows. It is understood that these resources are freed when the object is garbage-collected, but since garbage collection is not guaranteed to happen, such objects also provide an explicit way to release the external resource, -usually a :meth:`close` method. Programs are strongly recommended to explicitly +usually a :meth:`!close` method. Programs are strongly recommended to explicitly close such objects. The ':keyword:`try`...\ :keyword:`finally`' statement and the ':keyword:`with`' statement provide convenient ways to do this. @@ -681,8 +681,8 @@ underlying the class method. When an instance method object is called, the underlying function (:attr:`__func__`) is called, inserting the class instance (:attr:`__self__`) in front of the argument list. For instance, when -:class:`C` is a class which contains a definition for a function -:meth:`f`, and ``x`` is an instance of :class:`C`, calling ``x.f(1)`` is +:class:`!C` is a class which contains a definition for a function +:meth:`!f`, and ``x`` is an instance of :class:`!C`, calling ``x.f(1)`` is equivalent to calling ``C.f(x, 1)``. When an instance method object is derived from a class method object, the @@ -795,7 +795,7 @@ Classes Classes are callable. These objects normally act as factories for new instances of themselves, but variations are possible for class types that override :meth:`~object.__new__`. The arguments of the call are passed to -:meth:`__new__` and, in the typical case, to :meth:`~object.__init__` to +:meth:`!__new__` and, in the typical case, to :meth:`~object.__init__` to initialize the new instance. @@ -899,9 +899,9 @@ https://www.python.org/download/releases/2.3/mro/. pair: object; dictionary pair: class; attribute -When a class attribute reference (for class :class:`C`, say) would yield a +When a class attribute reference (for class :class:`!C`, say) would yield a class method object, it is transformed into an instance method object whose -:attr:`__self__` attribute is :class:`C`. When it would yield a static +:attr:`__self__` attribute is :class:`!C`. When it would yield a static method object, it is transformed into the object wrapped by the static method object. See section :ref:`descriptors` for another way in which attributes retrieved from a class may differ from those actually contained in its @@ -1903,13 +1903,17 @@ class' :attr:`~object.__dict__`. Called to delete the attribute on an instance *instance* of the owner class. +Instances of descriptors may also have the :attr:`!__objclass__` attribute +present: -The attribute :attr:`__objclass__` is interpreted by the :mod:`inspect` module -as specifying the class where this object was defined (setting this -appropriately can assist in runtime introspection of dynamic class attributes). -For callables, it may indicate that an instance of the given type (or a -subclass) is expected or required as the first positional argument (for example, -CPython sets this attribute for unbound methods that are implemented in C). +.. attribute:: object.__objclass__ + + The attribute :attr:`!__objclass__` is interpreted by the :mod:`inspect` module + as specifying the class where this object was defined (setting this + appropriately can assist in runtime introspection of dynamic class attributes). + For callables, it may indicate that an instance of the given type (or a + subclass) is expected or required as the first positional argument (for example, + CPython sets this attribute for unbound methods that are implemented in C). .. _descriptor-invocation: @@ -1990,13 +1994,14 @@ For instance bindings, the precedence of descriptor invocation depends on which descriptor methods are defined. A descriptor can define any combination of :meth:`~object.__get__`, :meth:`~object.__set__` and :meth:`~object.__delete__`. If it does not -define :meth:`__get__`, then accessing the attribute will return the descriptor +define :meth:`!__get__`, then accessing the attribute will return the descriptor object itself unless there is a value in the object's instance dictionary. If -the descriptor defines :meth:`__set__` and/or :meth:`__delete__`, it is a data +the descriptor defines :meth:`!__set__` and/or :meth:`!__delete__`, it is a data descriptor; if it defines neither, it is a non-data descriptor. Normally, data -descriptors define both :meth:`__get__` and :meth:`__set__`, while non-data -descriptors have just the :meth:`__get__` method. Data descriptors with -:meth:`__get__` and :meth:`__set__` (and/or :meth:`__delete__`) defined always override a redefinition in an +descriptors define both :meth:`!__get__` and :meth:`!__set__`, while non-data +descriptors have just the :meth:`!__get__` method. Data descriptors with +:meth:`!__get__` and :meth:`!__set__` (and/or :meth:`!__delete__`) defined +always override a redefinition in an instance dictionary. In contrast, non-data descriptors can be overridden by instances. @@ -2573,16 +2578,17 @@ either to emulate a sequence or to emulate a mapping; the difference is that for a sequence, the allowable keys should be the integers *k* for which ``0 <= k < N`` where *N* is the length of the sequence, or :class:`slice` objects, which define a range of items. It is also recommended that mappings provide the methods -:meth:`keys`, :meth:`values`, :meth:`items`, :meth:`get`, :meth:`clear`, -:meth:`setdefault`, :meth:`pop`, :meth:`popitem`, :meth:`!copy`, and -:meth:`update` behaving similar to those for Python's standard :class:`dictionary ` +:meth:`!keys`, :meth:`!values`, :meth:`!items`, :meth:`!get`, :meth:`!clear`, +:meth:`!setdefault`, :meth:`!pop`, :meth:`!popitem`, :meth:`!copy`, and +:meth:`!update` behaving similar to those for Python's standard :class:`dictionary ` objects. The :mod:`collections.abc` module provides a :class:`~collections.abc.MutableMapping` :term:`abstract base class` to help create those methods from a base set of -:meth:`~object.__getitem__`, :meth:`~object.__setitem__`, :meth:`~object.__delitem__`, and :meth:`keys`. -Mutable sequences should provide methods :meth:`append`, :meth:`count`, -:meth:`index`, :meth:`extend`, :meth:`insert`, :meth:`pop`, :meth:`remove`, -:meth:`reverse` and :meth:`sort`, like Python standard :class:`list` +:meth:`~object.__getitem__`, :meth:`~object.__setitem__`, +:meth:`~object.__delitem__`, and :meth:`!keys`. +Mutable sequences should provide methods :meth:`!append`, :meth:`!count`, +:meth:`!index`, :meth:`!extend`, :meth:`!insert`, :meth:`!pop`, :meth:`!remove`, +:meth:`!reverse` and :meth:`!sort`, like Python standard :class:`list` objects. Finally, sequence types should implement addition (meaning concatenation) and multiplication (meaning repetition) by defining the methods @@ -2595,7 +2601,7 @@ 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 :meth:`~object.__iter__` method to allow efficient iteration -through the container; for mappings, :meth:`__iter__` should iterate +through the container; for mappings, :meth:`!__iter__` should iterate through the object's keys; for sequences, it should iterate through the values. .. method:: object.__len__(self) @@ -3174,7 +3180,7 @@ generators, coroutines do not directly support iteration. to the :meth:`~generator.send` method of the iterator that caused the coroutine to suspend. The result (return value, :exc:`StopIteration`, or other exception) is the same as when - iterating over the :meth:`__await__` return value, described above. + iterating over the :meth:`!__await__` return value, described above. .. method:: coroutine.throw(value) coroutine.throw(type[, value[, traceback]]) From 5aa317e4ca619c3735e1d67b507f01a8e49a4c49 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Tue, 5 Dec 2023 10:44:19 +0000 Subject: [PATCH 181/228] gh-112535: Add comment for ppc32/64 registers (gh-112746) --- Include/object.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Include/object.h b/Include/object.h index dfeb43bda7d841..85abd30b5ad7d6 100644 --- a/Include/object.h +++ b/Include/object.h @@ -265,6 +265,7 @@ _Py_ThreadId(void) #if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer) tid = (uintptr_t)__builtin_thread_pointer(); #else + // r13 is reserved for use as system thread ID by the Power 64-bit ABI. register uintptr_t tp __asm__ ("r13"); __asm__("" : "=r" (tp)); tid = tp; @@ -273,6 +274,7 @@ _Py_ThreadId(void) #if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer) tid = (uintptr_t)__builtin_thread_pointer(); #else + // r2 is reserved for use as system thread ID by the Power 32-bit ABI. register uintptr_t tp __asm__ ("r2"); __asm__ ("" : "=r" (tp)); tid = tp; From 8cdfee1bb902fd1e38d79170b751ef13a0907262 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Tue, 5 Dec 2023 21:30:59 +0800 Subject: [PATCH 182/228] bpo-43153: Don't mask `PermissionError` with `NotADirectoryError` during tempdirectory cleanup (GH-29940) Co-authored-by: andrei kulakov Co-authored-by: Serhiy Storchaka --- Lib/tempfile.py | 28 +++++++++++++++++-- Lib/test/test_tempfile.py | 11 ++++++++ .../2021-12-06-22-10-53.bpo-43153.J7mjSy.rst | 4 +++ 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2021-12-06-22-10-53.bpo-43153.J7mjSy.rst diff --git a/Lib/tempfile.py b/Lib/tempfile.py index 2b4f4313247128..55403ad1faf46d 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -41,6 +41,7 @@ import io as _io import os as _os import shutil as _shutil +import stat as _stat import errno as _errno from random import Random as _Random import sys as _sys @@ -889,8 +890,31 @@ def resetperms(path): try: _os.unlink(path) - # PermissionError is raised on FreeBSD for directories - except (IsADirectoryError, PermissionError): + except IsADirectoryError: + cls._rmtree(path, ignore_errors=ignore_errors) + except PermissionError: + # The PermissionError handler was originally added for + # FreeBSD in directories, but it seems that it is raised + # on Windows too. + # bpo-43153: Calling _rmtree again may + # raise NotADirectoryError and mask the PermissionError. + # So we must re-raise the current PermissionError if + # path is not a directory. + try: + st = _os.lstat(path) + except OSError: + if ignore_errors: + return + raise + if (_stat.S_ISLNK(st.st_mode) or + not _stat.S_ISDIR(st.st_mode) or + (hasattr(st, 'st_file_attributes') and + st.st_file_attributes & _stat.FILE_ATTRIBUTE_REPARSE_POINT and + st.st_reparse_tag == _stat.IO_REPARSE_TAG_MOUNT_POINT) + ): + if ignore_errors: + return + raise cls._rmtree(path, ignore_errors=ignore_errors) except FileNotFoundError: pass diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index 1673507e2f7c91..f4aef887799ed4 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -1641,6 +1641,17 @@ def test_explicit_cleanup_ignore_errors(self): temp_path.exists(), f"TemporaryDirectory {temp_path!s} exists after cleanup") + @unittest.skipUnless(os.name == "nt", "Only on Windows.") + def test_explicit_cleanup_correct_error(self): + with tempfile.TemporaryDirectory() as working_dir: + temp_dir = self.do_create(dir=working_dir) + with open(os.path.join(temp_dir.name, "example.txt"), 'wb'): + # Previously raised NotADirectoryError on some OSes + # (e.g. Windows). See bpo-43153. + with self.assertRaises(PermissionError): + temp_dir.cleanup() + + @os_helper.skip_unless_symlink def test_cleanup_with_symlink_to_a_directory(self): # cleanup() should not follow symlinks to directories (issue #12464) diff --git a/Misc/NEWS.d/next/Library/2021-12-06-22-10-53.bpo-43153.J7mjSy.rst b/Misc/NEWS.d/next/Library/2021-12-06-22-10-53.bpo-43153.J7mjSy.rst new file mode 100644 index 00000000000000..7800e0a4869adf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-12-06-22-10-53.bpo-43153.J7mjSy.rst @@ -0,0 +1,4 @@ +On Windows, ``tempfile.TemporaryDirectory`` previously masked a +``PermissionError`` with ``NotADirectoryError`` during directory cleanup. It +now correctly raises ``PermissionError`` if errors are not ignored. Patch by +Andrei Kulakov and Ken Jin. From e7e1116a781434763c309b55a31204a98237f7b4 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Tue, 5 Dec 2023 05:52:28 -0900 Subject: [PATCH 183/228] gh-105323: Remove `WITH_APPLE_EDITLINE` to use the same declaration for all editline (gh-112513) --- Modules/readline.c | 8 ++------ configure | 17 ----------------- configure.ac | 10 ---------- pyconfig.h.in | 3 --- 4 files changed, 2 insertions(+), 36 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c index afbb7f8f0ec18f..e29051c37f8827 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -1043,10 +1043,8 @@ on_hook(PyObject *func) static int #if defined(_RL_FUNCTION_TYPEDEF) on_startup_hook(void) -#elif defined(WITH_APPLE_EDITLINE) -on_startup_hook(const char *Py_UNUSED(text), int Py_UNUSED(state)) #else -on_startup_hook(void) +on_startup_hook(const char *Py_UNUSED(text), int Py_UNUSED(state)) #endif { int r; @@ -1065,10 +1063,8 @@ on_startup_hook(void) static int #if defined(_RL_FUNCTION_TYPEDEF) on_pre_input_hook(void) -#elif defined(WITH_APPLE_EDITLINE) -on_pre_input_hook(const char *Py_UNUSED(text), int Py_UNUSED(state)) #else -on_pre_input_hook(void) +on_pre_input_hook(const char *Py_UNUSED(text), int Py_UNUSED(state)) #endif { int r; diff --git a/configure b/configure index 319009537f461c..8894122a11e86e 100755 --- a/configure +++ b/configure @@ -23971,7 +23971,6 @@ fi - # Check whether --with-readline was given. if test ${with_readline+y} then : @@ -23994,22 +23993,6 @@ else $as_nop fi -# gh-105323: Need to handle the macOS editline as an alias of readline. -case $ac_sys_system/$ac_sys_release in #( - Darwin/*) : - ac_fn_c_check_type "$LINENO" "Function" "ac_cv_type_Function" "#include -" -if test "x$ac_cv_type_Function" = xyes -then : - printf "%s\n" "#define WITH_APPLE_EDITLINE 1" >>confdefs.h - -fi - ;; #( - *) : - - ;; -esac - if test "x$with_readline" = xreadline then : diff --git a/configure.ac b/configure.ac index b78472e04846b7..1512e6d9e8c42a 100644 --- a/configure.ac +++ b/configure.ac @@ -5914,7 +5914,6 @@ dnl library (tinfo ncursesw ncurses termcap). We now assume that libreadline dnl or readline.pc provide correct linker information. AH_TEMPLATE([WITH_EDITLINE], [Define to build the readline module against libedit.]) -AH_TEMPLATE([WITH_APPLE_EDITLINE], [Define to build the readline module against Apple BSD editline.]) AC_ARG_WITH( [readline], @@ -5931,15 +5930,6 @@ AC_ARG_WITH( [with_readline=readline] ) -# gh-105323: Need to handle the macOS editline as an alias of readline. -AS_CASE([$ac_sys_system/$ac_sys_release], - [Darwin/*], [AC_CHECK_TYPE([Function], - [AC_DEFINE([WITH_APPLE_EDITLINE])], - [], - [@%:@include ])], - [] -) - AS_VAR_IF([with_readline], [readline], [ PKG_CHECK_MODULES([LIBREADLINE], [readline], [ LIBREADLINE=readline diff --git a/pyconfig.h.in b/pyconfig.h.in index bf708926e22c43..2978fa2c17301f 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1800,9 +1800,6 @@ /* Define if WINDOW in curses.h offers a field _flags. */ #undef WINDOW_HAS_FLAGS -/* Define to build the readline module against Apple BSD editline. */ -#undef WITH_APPLE_EDITLINE - /* Define if you want build the _decimal module using a coroutine-local rather than a thread-local context */ #undef WITH_DECIMAL_CONTEXTVAR From bc68f4a4abcfbea60bb1db1ccadb07613561931c Mon Sep 17 00:00:00 2001 From: Diego Russo Date: Tue, 5 Dec 2023 15:07:50 +0000 Subject: [PATCH 184/228] gh-110190: Fix ctypes structs with array on Arm (#112604) Set MAX_STRUCT_SIZE to 32 in stgdict.c when on Arm platforms. This because on Arm platforms structs with at most 4 elements of any floating point type values can be passed through registers. If the type is double the maximum size of the struct is 32 bytes. On x86-64 Linux, it's maximum 16 bytes hence we need to differentiate. --- Lib/test/test_ctypes/test_structures.py | 123 +++++++++++++++++- ...-12-01-18-05-09.gh-issue-110190.5bf-c9.rst | 1 + Modules/_ctypes/_ctypes_test.c | 36 +++++ Modules/_ctypes/stgdict.c | 53 +++++--- 4 files changed, 193 insertions(+), 20 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-12-01-18-05-09.gh-issue-110190.5bf-c9.rst diff --git a/Lib/test/test_ctypes/test_structures.py b/Lib/test/test_ctypes/test_structures.py index f05ee5e491a41e..771205ffb32ecc 100644 --- a/Lib/test/test_ctypes/test_structures.py +++ b/Lib/test/test_ctypes/test_structures.py @@ -2,7 +2,7 @@ import struct import sys import unittest -from ctypes import (CDLL, Structure, Union, POINTER, sizeof, byref, alignment, +from ctypes import (CDLL, Array, Structure, Union, POINTER, sizeof, byref, alignment, c_void_p, c_char, c_wchar, c_byte, c_ubyte, c_uint8, c_uint16, c_uint32, c_short, c_ushort, c_int, c_uint, @@ -494,12 +494,59 @@ class Test3B(Test3A): ('more_data', c_float * 2), ] + class Test3C1(Structure): + _fields_ = [ + ("data", c_double * 4) + ] + + class DataType4(Array): + _type_ = c_double + _length_ = 4 + + class Test3C2(Structure): + _fields_ = [ + ("data", DataType4) + ] + + class Test3C3(Structure): + _fields_ = [ + ("x", c_double), + ("y", c_double), + ("z", c_double), + ("t", c_double) + ] + + class Test3D1(Structure): + _fields_ = [ + ("data", c_double * 5) + ] + + class DataType5(Array): + _type_ = c_double + _length_ = 5 + + class Test3D2(Structure): + _fields_ = [ + ("data", DataType5) + ] + + class Test3D3(Structure): + _fields_ = [ + ("x", c_double), + ("y", c_double), + ("z", c_double), + ("t", c_double), + ("u", c_double) + ] + + # Load the shared library + dll = CDLL(_ctypes_test.__file__) + s = Test2() expected = 0 for i in range(16): s.data[i] = i expected += i - dll = CDLL(_ctypes_test.__file__) func = dll._testfunc_array_in_struct1 func.restype = c_int func.argtypes = (Test2,) @@ -540,6 +587,78 @@ class Test3B(Test3A): self.assertAlmostEqual(s.more_data[0], -3.0, places=6) self.assertAlmostEqual(s.more_data[1], -2.0, places=6) + # Tests for struct Test3C + expected = (1.0, 2.0, 3.0, 4.0) + func = dll._testfunc_array_in_struct_set_defaults_3C + func.restype = Test3C1 + result = func() + # check the default values have been set properly + self.assertEqual( + (result.data[0], + result.data[1], + result.data[2], + result.data[3]), + expected + ) + + func = dll._testfunc_array_in_struct_set_defaults_3C + func.restype = Test3C2 + result = func() + # check the default values have been set properly + self.assertEqual( + (result.data[0], + result.data[1], + result.data[2], + result.data[3]), + expected + ) + + func = dll._testfunc_array_in_struct_set_defaults_3C + func.restype = Test3C3 + result = func() + # check the default values have been set properly + self.assertEqual((result.x, result.y, result.z, result.t), expected) + + # Tests for struct Test3D + expected = (1.0, 2.0, 3.0, 4.0, 5.0) + func = dll._testfunc_array_in_struct_set_defaults_3D + func.restype = Test3D1 + result = func() + # check the default values have been set properly + self.assertEqual( + (result.data[0], + result.data[1], + result.data[2], + result.data[3], + result.data[4]), + expected + ) + + func = dll._testfunc_array_in_struct_set_defaults_3D + func.restype = Test3D2 + result = func() + # check the default values have been set properly + self.assertEqual( + (result.data[0], + result.data[1], + result.data[2], + result.data[3], + result.data[4]), + expected + ) + + func = dll._testfunc_array_in_struct_set_defaults_3D + func.restype = Test3D3 + result = func() + # check the default values have been set properly + self.assertEqual( + (result.x, + result.y, + result.z, + result.t, + result.u), + expected) + def test_38368(self): class U(Union): _fields_ = [ diff --git a/Misc/NEWS.d/next/Library/2023-12-01-18-05-09.gh-issue-110190.5bf-c9.rst b/Misc/NEWS.d/next/Library/2023-12-01-18-05-09.gh-issue-110190.5bf-c9.rst new file mode 100644 index 00000000000000..730b9d49119805 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-01-18-05-09.gh-issue-110190.5bf-c9.rst @@ -0,0 +1 @@ +Fix ctypes structs with array on Arm platform by setting ``MAX_STRUCT_SIZE`` to 32 in stgdict. Patch by Diego Russo. diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c index d33e6fc7586d28..fc9fc131f6249a 100644 --- a/Modules/_ctypes/_ctypes_test.c +++ b/Modules/_ctypes/_ctypes_test.c @@ -150,6 +150,42 @@ _testfunc_array_in_struct2a(Test3B in) return result; } +/* + * See gh-110190. structs containing arrays of up to four floating point types + * (max 32 bytes) are passed in registers on Arm. + */ + +typedef struct { + double data[4]; +} Test3C; + +EXPORT(Test3C) +_testfunc_array_in_struct_set_defaults_3C(void) +{ + Test3C s; + s.data[0] = 1.0; + s.data[1] = 2.0; + s.data[2] = 3.0; + s.data[3] = 4.0; + return s; +} + +typedef struct { + double data[5]; +} Test3D; + +EXPORT(Test3D) +_testfunc_array_in_struct_set_defaults_3D(void) +{ + Test3D s; + s.data[0] = 1.0; + s.data[1] = 2.0; + s.data[2] = 3.0; + s.data[3] = 4.0; + s.data[4] = 5.0; + return s; +} + typedef union { long a_long; struct { diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 6fbcf77a115371..04dd9bae32cd5e 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -697,29 +697,43 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct stgdict->align = total_align; stgdict->length = len; /* ADD ffi_ofs? */ -#define MAX_STRUCT_SIZE 16 +/* + * On Arm platforms, structs with at most 4 elements of any floating point + * type values can be passed through registers. If the type is double the + * maximum size of the struct is 32 bytes. + * By Arm platforms it is meant both 32 and 64-bit. +*/ +#if defined(__aarch64__) || defined(__arm__) +# define MAX_STRUCT_SIZE 32 +#else +# define MAX_STRUCT_SIZE 16 +#endif if (arrays_seen && (size <= MAX_STRUCT_SIZE)) { /* - * See bpo-22273. Arrays are normally treated as pointers, which is - * fine when an array name is being passed as parameter, but not when - * passing structures by value that contain arrays. On 64-bit Linux, - * small structures passed by value are passed in registers, and in - * order to do this, libffi needs to know the true type of the array - * members of structs. Treating them as pointers breaks things. + * See bpo-22273 and gh-110190. Arrays are normally treated as + * pointers, which is fine when an array name is being passed as + * parameter, but not when passing structures by value that contain + * arrays. + * On x86_64 Linux and Arm platforms, small structures passed by + * value are passed in registers, and in order to do this, libffi needs + * to know the true type of the array members of structs. Treating them + * as pointers breaks things. * - * By small structures, we mean ones that are 16 bytes or less. In that - * case, there can't be more than 16 elements after unrolling arrays, - * as we (will) disallow bitfields. So we can collect the true ffi_type - * values in a fixed-size local array on the stack and, if any arrays - * were seen, replace the ffi_type_pointer.elements with a more - * accurate set, to allow libffi to marshal them into registers - * correctly. It means one more loop over the fields, but if we got - * here, the structure is small, so there aren't too many of those. + * By small structures, we mean ones that are 16 bytes or less on + * x86-64 and 32 bytes or less on Arm. In that case, there can't be + * more than 16 or 32 elements after unrolling arrays, as we (will) + * disallow bitfields. So we can collect the true ffi_type values in + * a fixed-size local array on the stack and, if any arrays were seen, + * replace the ffi_type_pointer.elements with a more accurate set, + * to allow libffi to marshal them into registers correctly. + * It means one more loop over the fields, but if we got here, + * the structure is small, so there aren't too many of those. * - * Although the passing in registers is specific to 64-bit Linux, the - * array-in-struct vs. pointer problem is general. But we restrict the - * type transformation to small structs nonetheless. + * Although the passing in registers is specific to x86_64 Linux + * and Arm platforms, the array-in-struct vs. pointer problem is + * general. But we restrict the type transformation to small structs + * nonetheless. * * Note that although a union may be small in terms of memory usage, it * could contain many overlapping declarations of arrays, e.g. @@ -745,6 +759,9 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct * struct { uint_32 e1; uint_32 e2; ... uint_32 e_4; } f6; * } * + * The same principle applies for a struct 32 bytes in size like in + * the case of Arm platforms. + * * So the struct/union needs setting up as follows: all non-array * elements copied across as is, and all array elements replaced with * an equivalent struct which has as many fields as the array has From 563ccded6e83bfdd8c5622663c4edb679e96e08b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 5 Dec 2023 17:40:49 +0200 Subject: [PATCH 185/228] gh-94692: Only catch OSError in shutil.rmtree() (#112756) Previously a symlink attack resistant version of shutil.rmtree() could ignore or pass to the error handler arbitrary exception when invalid arguments were provided. --- Lib/shutil.py | 4 +-- Lib/test/test_shutil.py | 33 +++++++++---------- ...3-12-05-16-20-40.gh-issue-94692.-e5C3c.rst | 4 +++ 3 files changed, 22 insertions(+), 19 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-12-05-16-20-40.gh-issue-94692.-e5C3c.rst diff --git a/Lib/shutil.py b/Lib/shutil.py index 93b00d73a0fd46..bdbbcbc282e266 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -768,13 +768,13 @@ def onexc(*args): # lstat()/open()/fstat() trick. try: orig_st = os.lstat(path, dir_fd=dir_fd) - except Exception as err: + except OSError as err: onexc(os.lstat, path, err) return try: fd = os.open(path, os.O_RDONLY, dir_fd=dir_fd) fd_closed = False - except Exception as err: + except OSError as err: onexc(os.open, path, err) return try: diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 7ea2496230da47..9b8ec42a99dd69 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -317,7 +317,7 @@ def test_rmtree_works_on_junctions(self): self.assertTrue(os.path.exists(dir3)) self.assertTrue(os.path.exists(file1)) - def test_rmtree_errors_onerror(self): + def test_rmtree_errors(self): # filename is guaranteed not to exist filename = tempfile.mktemp(dir=self.mkdtemp()) self.assertRaises(FileNotFoundError, shutil.rmtree, filename) @@ -326,8 +326,8 @@ def test_rmtree_errors_onerror(self): # existing file tmpdir = self.mkdtemp() - write_file((tmpdir, "tstfile"), "") filename = os.path.join(tmpdir, "tstfile") + write_file(filename, "") with self.assertRaises(NotADirectoryError) as cm: shutil.rmtree(filename) self.assertEqual(cm.exception.filename, filename) @@ -335,6 +335,19 @@ def test_rmtree_errors_onerror(self): # test that ignore_errors option is honored shutil.rmtree(filename, ignore_errors=True) self.assertTrue(os.path.exists(filename)) + + self.assertRaises(TypeError, shutil.rmtree, None) + self.assertRaises(TypeError, shutil.rmtree, None, ignore_errors=True) + exc = TypeError if shutil.rmtree.avoids_symlink_attacks else NotImplementedError + with self.assertRaises(exc): + shutil.rmtree(filename, dir_fd='invalid') + with self.assertRaises(exc): + shutil.rmtree(filename, dir_fd='invalid', ignore_errors=True) + + def test_rmtree_errors_onerror(self): + tmpdir = self.mkdtemp() + filename = os.path.join(tmpdir, "tstfile") + write_file(filename, "") errors = [] def onerror(*args): errors.append(args) @@ -350,23 +363,9 @@ def onerror(*args): self.assertEqual(errors[1][2][1].filename, filename) def test_rmtree_errors_onexc(self): - # filename is guaranteed not to exist - filename = tempfile.mktemp(dir=self.mkdtemp()) - self.assertRaises(FileNotFoundError, shutil.rmtree, filename) - # test that ignore_errors option is honored - shutil.rmtree(filename, ignore_errors=True) - - # existing file tmpdir = self.mkdtemp() - write_file((tmpdir, "tstfile"), "") filename = os.path.join(tmpdir, "tstfile") - with self.assertRaises(NotADirectoryError) as cm: - shutil.rmtree(filename) - self.assertEqual(cm.exception.filename, filename) - self.assertTrue(os.path.exists(filename)) - # test that ignore_errors option is honored - shutil.rmtree(filename, ignore_errors=True) - self.assertTrue(os.path.exists(filename)) + write_file(filename, "") errors = [] def onexc(*args): errors.append(args) diff --git a/Misc/NEWS.d/next/Library/2023-12-05-16-20-40.gh-issue-94692.-e5C3c.rst b/Misc/NEWS.d/next/Library/2023-12-05-16-20-40.gh-issue-94692.-e5C3c.rst new file mode 100644 index 00000000000000..c67ba6c9ececdb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-05-16-20-40.gh-issue-94692.-e5C3c.rst @@ -0,0 +1,4 @@ +:func:`shutil.rmtree` now only catches OSError exceptions. Previously a +symlink attack resistant version of ``shutil.rmtree()`` could ignore or pass +to the error handler arbitrary exception when invalid arguments were +provided. From de6bca956432cc852a4a41e2a2cee9cdacd19f35 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Tue, 5 Dec 2023 08:27:36 -0800 Subject: [PATCH 186/228] gh-112328: [Enum] Make some private attributes public. (GH-112514) * [Enum] Make some private attributes public. - ``_EnumDict`` --> ``EnumDict`` - ``EnumDict._member_names`` --> ``EnumDict.member_names`` - ``Enum._add_alias_`` - ``Enum._add_value_alias_`` --------- Co-authored-by: Alex Waygood Co-authored-by: Nikita Sobolev --- Doc/howto/enum.rst | 67 ++++-- Doc/library/enum.rst | 60 ++++-- Lib/enum.py | 203 +++++++++++------- Lib/test/test_enum.py | 86 +++++++- ...-11-28-20-47-39.gh-issue-112328.Z2AxEY.rst | 2 + 5 files changed, 301 insertions(+), 117 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-28-20-47-39.gh-issue-112328.Z2AxEY.rst diff --git a/Doc/howto/enum.rst b/Doc/howto/enum.rst index ffdafb749c73a9..1e9ac9b6761b64 100644 --- a/Doc/howto/enum.rst +++ b/Doc/howto/enum.rst @@ -868,7 +868,7 @@ Others While :class:`IntEnum` is part of the :mod:`enum` module, it would be very simple to implement independently:: - class IntEnum(int, Enum): + class IntEnum(int, ReprEnum): # or Enum instead of ReprEnum pass This demonstrates how similar derived enumerations can be defined; for example @@ -876,8 +876,8 @@ a :class:`FloatEnum` that mixes in :class:`float` instead of :class:`int`. Some rules: -1. When subclassing :class:`Enum`, mix-in types must appear before - :class:`Enum` itself in the sequence of bases, as in the :class:`IntEnum` +1. When subclassing :class:`Enum`, mix-in types must appear before the + :class:`Enum` class itself in the sequence of bases, as in the :class:`IntEnum` example above. 2. Mix-in types must be subclassable. For example, :class:`bool` and :class:`range` are not subclassable and will throw an error during Enum @@ -961,30 +961,34 @@ all the members are created it is no longer used. Supported ``_sunder_`` names """""""""""""""""""""""""""" -- ``_name_`` -- name of the member -- ``_value_`` -- value of the member; can be set / modified in ``__new__`` +- :attr:`~Enum._name_` -- name of the member +- :attr:`~Enum._value_` -- value of the member; can be set in ``__new__`` +- :meth:`~Enum._missing_` -- a lookup function used when a value is not found; + may be overridden +- :attr:`~Enum._ignore_` -- a list of names, either as a :class:`list` or a + :class:`str`, that will not be transformed into members, and will be removed + from the final class +- :meth:`~Enum._generate_next_value_` -- used to get an appropriate value for + an enum member; may be overridden +- :meth:`~Enum._add_alias_` -- adds a new name as an alias to an existing + member. +- :meth:`~Enum._add_value_alias_` -- adds a new value as an alias to an + existing member. See `MultiValueEnum`_ for an example. -- ``_missing_`` -- a lookup function used when a value is not found; may be - overridden -- ``_ignore_`` -- a list of names, either as a :class:`list` or a :class:`str`, - that will not be transformed into members, and will be removed from the final - class -- ``_order_`` -- used in Python 2/3 code to ensure member order is consistent - (class attribute, removed during class creation) -- ``_generate_next_value_`` -- used by the `Functional API`_ and by - :class:`auto` to get an appropriate value for an enum member; may be - overridden + .. note:: -.. note:: + For standard :class:`Enum` classes the next value chosen is the highest + value seen incremented by one. - For standard :class:`Enum` classes the next value chosen is the last value seen - incremented by one. + For :class:`Flag` classes the next value chosen will be the next highest + power-of-two. - For :class:`Flag` classes the next value chosen will be the next highest - power-of-two, regardless of the last value seen. + .. versionchanged:: 3.13 + Prior versions would use the last seen value instead of the highest value. .. versionadded:: 3.6 ``_missing_``, ``_order_``, ``_generate_next_value_`` .. versionadded:: 3.7 ``_ignore_`` +.. versionadded:: 3.13 ``_add_alias_``, ``_add_value_alias_`` To help keep Python 2 / Python 3 code in sync an :attr:`_order_` attribute can be provided. It will be checked against the actual order of the enumeration @@ -1447,6 +1451,29 @@ alias:: disallowing aliases, the :func:`unique` decorator can be used instead. +MultiValueEnum +^^^^^^^^^^^^^^^^^ + +Supports having more than one value per member:: + + >>> class MultiValueEnum(Enum): + ... def __new__(cls, value, *values): + ... self = object.__new__(cls) + ... self._value_ = value + ... for v in values: + ... self._add_value_alias_(v) + ... return self + ... + >>> class DType(MultiValueEnum): + ... float32 = 'f', 8 + ... double64 = 'd', 9 + ... + >>> DType('f') + + >>> DType(9) + + + Planet ^^^^^^ diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 2d5ae361c3f1e3..20222bfb3611ab 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -235,6 +235,10 @@ Data Types >>> len(Color) 3 + .. attribute:: EnumType.__members__ + + Returns a mapping of every enum name to its member, including aliases + .. method:: EnumType.__reversed__(cls) Returns each member in *cls* in reverse definition order:: @@ -242,9 +246,19 @@ Data Types >>> list(reversed(Color)) [, , ] + .. method:: EnumType._add_alias_ + + Adds a new name as an alias to an existing member. Raises a + :exc:`NameError` if the name is already assigned to a different member. + + .. method:: EnumType._add_value_alias_ + + Adds a new value as an alias to an existing member. Raises a + :exc:`ValueError` if the value is already linked with a different member. + .. versionadded:: 3.11 - Before 3.11 ``enum`` used ``EnumMeta`` type, which is kept as an alias. + Before 3.11 ``EnumType`` was called ``EnumMeta``, which is still available as an alias. .. class:: Enum @@ -323,7 +337,7 @@ Data Types >>> PowersOfThree.SECOND.value 9 - .. method:: Enum.__init_subclass__(cls, **kwds) + .. method:: Enum.__init_subclass__(cls, \**kwds) A *classmethod* that is used to further configure subsequent subclasses. By default, does nothing. @@ -549,7 +563,7 @@ Data Types .. method:: __invert__(self): - Returns all the flags in *type(self)* that are not in self:: + Returns all the flags in *type(self)* that are not in *self*:: >>> ~white @@ -769,37 +783,41 @@ Supported ``__dunder__`` names :attr:`~EnumType.__members__` is a read-only ordered mapping of ``member_name``:``member`` items. It is only available on the class. -:meth:`~object.__new__`, if specified, must create and return the enum members; it is -also a very good idea to set the member's :attr:`!_value_` appropriately. Once -all the members are created it is no longer used. +:meth:`~object.__new__`, if specified, must create and return the enum members; +it is also a very good idea to set the member's :attr:`!_value_` appropriately. +Once all the members are created it is no longer used. Supported ``_sunder_`` names """""""""""""""""""""""""""" -- ``_name_`` -- name of the member -- ``_value_`` -- value of the member; can be set / modified in ``__new__`` - -- ``_missing_`` -- a lookup function used when a value is not found; may be - overridden -- ``_ignore_`` -- a list of names, either as a :class:`list` or a :class:`str`, - that will not be transformed into members, and will be removed from the final - class -- ``_order_`` -- used in Python 2/3 code to ensure member order is consistent - (class attribute, removed during class creation) -- ``_generate_next_value_`` -- used to get an appropriate value for an enum - member; may be overridden +- :meth:`~EnumType._add_alias_` -- adds a new name as an alias to an existing + member. +- :meth:`~EnumType._add_value_alias_` -- adds a new value as an alias to an + existing member. +- :attr:`~Enum._name_` -- name of the member +- :attr:`~Enum._value_` -- value of the member; can be set in ``__new__`` +- :meth:`~Enum._missing_` -- a lookup function used when a value is not found; + may be overridden +- :attr:`~Enum._ignore_` -- a list of names, either as a :class:`list` or a + :class:`str`, that will not be transformed into members, and will be removed + from the final class +- :attr:`~Enum._order_` -- used in Python 2/3 code to ensure member order is + consistent (class attribute, removed during class creation) +- :meth:`~Enum._generate_next_value_` -- used to get an appropriate value for + an enum member; may be overridden .. note:: - For standard :class:`Enum` classes the next value chosen is the last value seen - incremented by one. + For standard :class:`Enum` classes the next value chosen is the highest + value seen incremented by one. For :class:`Flag` classes the next value chosen will be the next highest - power-of-two, regardless of the last value seen. + power-of-two. .. versionadded:: 3.6 ``_missing_``, ``_order_``, ``_generate_next_value_`` .. versionadded:: 3.7 ``_ignore_`` +.. versionadded:: 3.13 ``_add_alias_``, ``_add_value_alias_`` --------------- diff --git a/Lib/enum.py b/Lib/enum.py index 648401e80be685..a8a50a58380375 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -4,7 +4,7 @@ __all__ = [ - 'EnumType', 'EnumMeta', + 'EnumType', 'EnumMeta', 'EnumDict', 'Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag', 'ReprEnum', 'auto', 'unique', 'property', 'verify', 'member', 'nonmember', 'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP', @@ -313,45 +313,8 @@ def __set_name__(self, enum_class, member_name): ): # no other instances found, record this member in _member_names_ enum_class._member_names_.append(member_name) - # if necessary, get redirect in place and then add it to _member_map_ - found_descriptor = None - descriptor_type = None - class_type = None - for base in enum_class.__mro__[1:]: - attr = base.__dict__.get(member_name) - if attr is not None: - if isinstance(attr, (property, DynamicClassAttribute)): - found_descriptor = attr - class_type = base - descriptor_type = 'enum' - break - elif _is_descriptor(attr): - found_descriptor = attr - descriptor_type = descriptor_type or 'desc' - class_type = class_type or base - continue - else: - descriptor_type = 'attr' - class_type = base - if found_descriptor: - redirect = property() - redirect.member = enum_member - redirect.__set_name__(enum_class, member_name) - if descriptor_type in ('enum','desc'): - # earlier descriptor found; copy fget, fset, fdel to this one. - redirect.fget = getattr(found_descriptor, 'fget', None) - redirect._get = getattr(found_descriptor, '__get__', None) - redirect.fset = getattr(found_descriptor, 'fset', None) - redirect._set = getattr(found_descriptor, '__set__', None) - redirect.fdel = getattr(found_descriptor, 'fdel', None) - redirect._del = getattr(found_descriptor, '__delete__', None) - redirect._attr_type = descriptor_type - redirect._cls_type = class_type - setattr(enum_class, member_name, redirect) - else: - setattr(enum_class, member_name, enum_member) - # now add to _member_map_ (even aliases) - enum_class._member_map_[member_name] = enum_member + + enum_class._add_member_(member_name, enum_member) try: # This may fail if value is not hashable. We can't add the value # to the map, and by-value lookups for this value will be @@ -360,9 +323,10 @@ def __set_name__(self, enum_class, member_name): except TypeError: # keep track of the value in a list so containment checks are quick enum_class._unhashable_values_.append(value) + enum_class._unhashable_values_map_.setdefault(member_name, []).append(value) -class _EnumDict(dict): +class EnumDict(dict): """ Track enum member order and ensure member names are not reused. @@ -371,7 +335,7 @@ class _EnumDict(dict): """ def __init__(self): super().__init__() - self._member_names = {} # use a dict to keep insertion order + self._member_names = {} # use a dict -- faster look-up than a list, and keeps insertion order since 3.7 self._last_values = [] self._ignore = [] self._auto_called = False @@ -393,6 +357,7 @@ def __setitem__(self, key, value): '_order_', '_generate_next_value_', '_numeric_repr_', '_missing_', '_ignore_', '_iter_member_', '_iter_member_by_value_', '_iter_member_by_def_', + '_add_alias_', '_add_value_alias_', ): raise ValueError( '_sunder_ names, such as %r, are reserved for future Enum use' @@ -468,6 +433,10 @@ def __setitem__(self, key, value): self._last_values.append(value) super().__setitem__(key, value) + @property + def member_names(self): + return list(self._member_names) + def update(self, members, **more_members): try: for name in members.keys(): @@ -478,6 +447,8 @@ def update(self, members, **more_members): for name, value in more_members.items(): self[name] = value +_EnumDict = EnumDict # keep private name for backwards compatibility + class EnumType(type): """ @@ -489,7 +460,7 @@ def __prepare__(metacls, cls, bases, **kwds): # check that previous enum members do not exist metacls._check_for_existing_members_(cls, bases) # create the namespace dict - enum_dict = _EnumDict() + enum_dict = EnumDict() enum_dict._cls_name = cls # inherit previous flags and _generate_next_value_ function member_type, first_enum = metacls._get_mixins_(cls, bases) @@ -552,6 +523,7 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k classdict['_member_map_'] = {} classdict['_value2member_map_'] = {} classdict['_unhashable_values_'] = [] + classdict['_unhashable_values_map_'] = {} classdict['_member_type_'] = member_type # now set the __repr__ for the value classdict['_value_repr_'] = metacls._find_data_repr_(cls, bases) @@ -754,7 +726,10 @@ def __contains__(cls, value): """ if isinstance(value, cls): return True - return value in cls._value2member_map_ or value in cls._unhashable_values_ + try: + return value in cls._value2member_map_ + except TypeError: + return value in cls._unhashable_values_ def __delattr__(cls, attr): # nicer error message when someone tries to delete an attribute @@ -1050,7 +1025,57 @@ def _find_new_(mcls, classdict, member_type, first_enum): else: use_args = True return __new__, save_new, use_args -EnumMeta = EnumType + + def _add_member_(cls, name, member): + # _value_ structures are not updated + if name in cls._member_map_: + if cls._member_map_[name] is not member: + raise NameError('%r is already bound: %r' % (name, cls._member_map_[name])) + return + # + # if necessary, get redirect in place and then add it to _member_map_ + found_descriptor = None + descriptor_type = None + class_type = None + for base in cls.__mro__[1:]: + attr = base.__dict__.get(name) + if attr is not None: + if isinstance(attr, (property, DynamicClassAttribute)): + found_descriptor = attr + class_type = base + descriptor_type = 'enum' + break + elif _is_descriptor(attr): + found_descriptor = attr + descriptor_type = descriptor_type or 'desc' + class_type = class_type or base + continue + else: + descriptor_type = 'attr' + class_type = base + if found_descriptor: + redirect = property() + redirect.member = member + redirect.__set_name__(cls, name) + if descriptor_type in ('enum', 'desc'): + # earlier descriptor found; copy fget, fset, fdel to this one. + redirect.fget = getattr(found_descriptor, 'fget', None) + redirect._get = getattr(found_descriptor, '__get__', None) + redirect.fset = getattr(found_descriptor, 'fset', None) + redirect._set = getattr(found_descriptor, '__set__', None) + redirect.fdel = getattr(found_descriptor, 'fdel', None) + redirect._del = getattr(found_descriptor, '__delete__', None) + redirect._attr_type = descriptor_type + redirect._cls_type = class_type + setattr(cls, name, redirect) + else: + setattr(cls, name, member) + # now add to _member_map_ (even aliases) + cls._member_map_[name] = member + # + cls._member_map_[name] = member + +EnumMeta = EnumType # keep EnumMeta name for backwards compatibility class Enum(metaclass=EnumType): @@ -1116,9 +1141,9 @@ def __new__(cls, value): pass except TypeError: # not there, now do long search -- O(n) behavior - for member in cls._member_map_.values(): - if member._value_ == value: - return member + for name, values in cls._unhashable_values_map_.items(): + if value in values: + return cls[name] # still not found -- verify that members exist, in-case somebody got here mistakenly # (such as via super when trying to override __new__) if not cls._member_map_: @@ -1159,6 +1184,33 @@ def __new__(cls, value): def __init__(self, *args, **kwds): pass + def _add_alias_(self, name): + self.__class__._add_member_(name, self) + + def _add_value_alias_(self, value): + cls = self.__class__ + try: + if value in cls._value2member_map_: + if cls._value2member_map_[value] is not self: + raise ValueError('%r is already bound: %r' % (value, cls._value2member_map_[value])) + return + except TypeError: + # unhashable value, do long search + for m in cls._member_map_.values(): + if m._value_ == value: + if m is not self: + raise ValueError('%r is already bound: %r' % (value, cls._value2member_map_[value])) + return + try: + # This may fail if value is not hashable. We can't add the value + # to the map, and by-value lookups for this value will be + # linear. + cls._value2member_map_.setdefault(value, self) + except TypeError: + # keep track of the value in a list so containment checks are quick + cls._unhashable_values_.append(value) + cls._unhashable_values_map_.setdefault(self.name, []).append(value) + @staticmethod def _generate_next_value_(name, start, count, last_values): """ @@ -1671,7 +1723,8 @@ def convert_class(cls): body['_member_names_'] = member_names = [] body['_member_map_'] = member_map = {} body['_value2member_map_'] = value2member_map = {} - body['_unhashable_values_'] = [] + body['_unhashable_values_'] = unhashable_values = [] + body['_unhashable_values_map_'] = {} body['_member_type_'] = member_type = etype._member_type_ body['_value_repr_'] = etype._value_repr_ if issubclass(etype, Flag): @@ -1718,14 +1771,9 @@ def convert_class(cls): for name, value in attrs.items(): if isinstance(value, auto) and auto.value is _auto_null: value = gnv(name, 1, len(member_names), gnv_last_values) - if value in value2member_map: + if value in value2member_map or value in unhashable_values: # an alias to an existing member - member = value2member_map[value] - redirect = property() - redirect.member = member - redirect.__set_name__(enum_class, name) - setattr(enum_class, name, redirect) - member_map[name] = member + enum_class(value)._add_alias_(name) else: # create the member if use_args: @@ -1740,12 +1788,12 @@ def convert_class(cls): member._name_ = name member.__objclass__ = enum_class member.__init__(value) - redirect = property() - redirect.member = member - redirect.__set_name__(enum_class, name) - setattr(enum_class, name, redirect) - member_map[name] = member member._sort_order_ = len(member_names) + if name not in ('name', 'value'): + setattr(enum_class, name, member) + member_map[name] = member + else: + enum_class._add_member_(name, member) value2member_map[value] = member if _is_single_bit(value): # not a multi-bit alias, record in _member_names_ and _flag_mask_ @@ -1768,14 +1816,13 @@ def convert_class(cls): if value.value is _auto_null: value.value = gnv(name, 1, len(member_names), gnv_last_values) value = value.value - if value in value2member_map: + try: + contained = value in value2member_map + except TypeError: + contained = value in unhashable_values + if contained: # an alias to an existing member - member = value2member_map[value] - redirect = property() - redirect.member = member - redirect.__set_name__(enum_class, name) - setattr(enum_class, name, redirect) - member_map[name] = member + enum_class(value)._add_alias_(name) else: # create the member if use_args: @@ -1791,14 +1838,22 @@ def convert_class(cls): member.__objclass__ = enum_class member.__init__(value) member._sort_order_ = len(member_names) - redirect = property() - redirect.member = member - redirect.__set_name__(enum_class, name) - setattr(enum_class, name, redirect) - member_map[name] = member - value2member_map[value] = member + if name not in ('name', 'value'): + setattr(enum_class, name, member) + member_map[name] = member + else: + enum_class._add_member_(name, member) member_names.append(name) gnv_last_values.append(value) + try: + # This may fail if value is not hashable. We can't add the value + # to the map, and by-value lookups for this value will be + # linear. + enum_class._value2member_map_.setdefault(value, member) + except TypeError: + # keep track of the value in a list so containment checks are quick + enum_class._unhashable_values_.append(value) + enum_class._unhashable_values_map_.setdefault(name, []).append(value) if '__new__' in body: enum_class.__new_member__ = enum_class.__new__ enum_class.__new__ = Enum.__new__ diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index c602913ca69277..f99d4ca204b5a7 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -514,6 +514,7 @@ def test_contains_tf(self): self.assertFalse('first' in MainEnum) val = MainEnum.dupe self.assertIn(val, MainEnum) + self.assertNotIn(float('nan'), MainEnum) # class OtherEnum(Enum): one = auto() @@ -3268,6 +3269,65 @@ def __new__(cls, value): member._value_ = Base(value) return member + def test_extra_member_creation(self): + class IDEnumMeta(EnumMeta): + def __new__(metacls, cls, bases, classdict, **kwds): + # add new entries to classdict + for name in classdict.member_names: + classdict[f'{name}_DESC'] = f'-{classdict[name]}' + return super().__new__(metacls, cls, bases, classdict, **kwds) + class IDEnum(StrEnum, metaclass=IDEnumMeta): + pass + class MyEnum(IDEnum): + ID = 'id' + NAME = 'name' + self.assertEqual(list(MyEnum), [MyEnum.ID, MyEnum.NAME, MyEnum.ID_DESC, MyEnum.NAME_DESC]) + + def test_add_alias(self): + class mixin: + @property + def ORG(self): + return 'huh' + class Color(mixin, Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + Color.RED._add_alias_('ROJO') + self.assertIs(Color.RED, Color['ROJO']) + self.assertIs(Color.RED, Color.ROJO) + Color.BLUE._add_alias_('ORG') + self.assertIs(Color.BLUE, Color['ORG']) + self.assertIs(Color.BLUE, Color.ORG) + self.assertEqual(Color.RED.ORG, 'huh') + self.assertEqual(Color.GREEN.ORG, 'huh') + self.assertEqual(Color.BLUE.ORG, 'huh') + self.assertEqual(Color.ORG.ORG, 'huh') + + def test_add_value_alias_after_creation(self): + class Color(Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + Color.RED._add_value_alias_(5) + self.assertIs(Color.RED, Color(5)) + + def test_add_value_alias_during_creation(self): + class Types(Enum): + Unknown = 0, + Source = 1, 'src' + NetList = 2, 'nl' + def __new__(cls, int_value, *value_aliases): + member = object.__new__(cls) + member._value_ = int_value + for alias in value_aliases: + member._add_value_alias_(alias) + return member + self.assertIs(Types(0), Types.Unknown) + self.assertIs(Types(1), Types.Source) + self.assertIs(Types('src'), Types.Source) + self.assertIs(Types(2), Types.NetList) + self.assertIs(Types('nl'), Types.NetList) + class TestOrder(unittest.TestCase): "test usage of the `_order_` attribute" @@ -4941,12 +5001,14 @@ class CheckedColor(Enum): @bltns.property def zeroth(self): return 'zeroed %s' % self.name - self.assertTrue(_test_simple_enum(CheckedColor, SimpleColor) is None) + _test_simple_enum(CheckedColor, SimpleColor) SimpleColor.MAGENTA._value_ = 9 self.assertRaisesRegex( TypeError, "enum mismatch", _test_simple_enum, CheckedColor, SimpleColor, ) + # + # class CheckedMissing(IntFlag, boundary=KEEP): SIXTY_FOUR = 64 ONE_TWENTY_EIGHT = 128 @@ -4963,8 +5025,28 @@ class Missing: ALL = 2048 + 128 + 64 + 12 M = Missing self.assertEqual(list(CheckedMissing), [M.SIXTY_FOUR, M.ONE_TWENTY_EIGHT, M.TWENTY_FORTY_EIGHT]) - # _test_simple_enum(CheckedMissing, Missing) + # + # + class CheckedUnhashable(Enum): + ONE = dict() + TWO = set() + name = 'python' + self.assertIn(dict(), CheckedUnhashable) + self.assertIn('python', CheckedUnhashable) + self.assertEqual(CheckedUnhashable.name.value, 'python') + self.assertEqual(CheckedUnhashable.name.name, 'name') + # + @_simple_enum() + class Unhashable: + ONE = dict() + TWO = set() + name = 'python' + self.assertIn(dict(), Unhashable) + self.assertIn('python', Unhashable) + self.assertEqual(Unhashable.name.value, 'python') + self.assertEqual(Unhashable.name.name, 'name') + _test_simple_enum(Unhashable, Unhashable) class MiscTestCase(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2023-11-28-20-47-39.gh-issue-112328.Z2AxEY.rst b/Misc/NEWS.d/next/Library/2023-11-28-20-47-39.gh-issue-112328.Z2AxEY.rst new file mode 100644 index 00000000000000..6e6902486b7bc9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-28-20-47-39.gh-issue-112328.Z2AxEY.rst @@ -0,0 +1,2 @@ +[Enum] Make ``EnumDict``, ``EnumDict.member_names``, +``EnumType._add_alias_`` and ``EnumType._add_value_alias_`` public. From 11d88a178b077e42025da538b890db3151a47070 Mon Sep 17 00:00:00 2001 From: Zackery Spytz Date: Tue, 5 Dec 2023 09:09:39 -0800 Subject: [PATCH 187/228] bpo-35332: Handle os.close() errors in shutil.rmtree() (GH-23766) * Ignore os.close() errors when ignore_errors is True. * Pass os.close() errors to the error handler if specified. * os.close no longer retried after error. Co-authored-by: Serhiy Storchaka --- Lib/shutil.py | 20 +++++++++-- Lib/test/test_shutil.py | 35 +++++++++++++++++++ .../2020-12-14-09-31-13.bpo-35332.s22wAx.rst | 3 ++ 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-12-14-09-31-13.bpo-35332.s22wAx.rst diff --git a/Lib/shutil.py b/Lib/shutil.py index bdbbcbc282e266..dc3aac3e07f910 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -687,7 +687,12 @@ def _rmtree_safe_fd(topfd, path, onexc): _rmtree_safe_fd(dirfd, fullname, onexc) try: os.close(dirfd) + except OSError as err: + # close() should not be retried after an error. dirfd_closed = True + onexc(os.close, fullname, err) + dirfd_closed = True + try: os.rmdir(entry.name, dir_fd=topfd) except FileNotFoundError: continue @@ -704,7 +709,10 @@ def _rmtree_safe_fd(topfd, path, onexc): onexc(os.path.islink, fullname, err) finally: if not dirfd_closed: - os.close(dirfd) + try: + os.close(dirfd) + except OSError as err: + onexc(os.close, fullname, err) else: try: os.unlink(entry.name, dir_fd=topfd) @@ -782,7 +790,12 @@ def onexc(*args): _rmtree_safe_fd(fd, path, onexc) try: os.close(fd) + except OSError as err: + # close() should not be retried after an error. fd_closed = True + onexc(os.close, path, err) + fd_closed = True + try: os.rmdir(path, dir_fd=dir_fd) except OSError as err: onexc(os.rmdir, path, err) @@ -794,7 +807,10 @@ def onexc(*args): onexc(os.path.islink, path, err) finally: if not fd_closed: - os.close(fd) + try: + os.close(fd) + except OSError as err: + onexc(os.close, path, err) else: if dir_fd is not None: raise NotImplementedError("dir_fd unavailable on this platform") diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 9b8ec42a99dd69..d7061b2f9d8724 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -576,6 +576,41 @@ def _raiser(*args, **kwargs): self.assertFalse(shutil._use_fd_functions) self.assertFalse(shutil.rmtree.avoids_symlink_attacks) + @unittest.skipUnless(shutil._use_fd_functions, "requires safe rmtree") + def test_rmtree_fails_on_close(self): + # Test that the error handler is called for failed os.close() and that + # os.close() is only called once for a file descriptor. + tmp = self.mkdtemp() + dir1 = os.path.join(tmp, 'dir1') + os.mkdir(dir1) + dir2 = os.path.join(dir1, 'dir2') + os.mkdir(dir2) + def close(fd): + orig_close(fd) + nonlocal close_count + close_count += 1 + raise OSError + + close_count = 0 + with support.swap_attr(os, 'close', close) as orig_close: + with self.assertRaises(OSError): + shutil.rmtree(dir1) + self.assertTrue(os.path.isdir(dir2)) + self.assertEqual(close_count, 2) + + close_count = 0 + errors = [] + def onexc(*args): + errors.append(args) + with support.swap_attr(os, 'close', close) as orig_close: + shutil.rmtree(dir1, onexc=onexc) + self.assertEqual(len(errors), 2) + self.assertIs(errors[0][0], close) + self.assertEqual(errors[0][1], dir2) + self.assertIs(errors[1][0], close) + self.assertEqual(errors[1][1], dir1) + self.assertEqual(close_count, 2) + @unittest.skipUnless(shutil._use_fd_functions, "dir_fd is not supported") def test_rmtree_with_dir_fd(self): tmp_dir = self.mkdtemp() diff --git a/Misc/NEWS.d/next/Library/2020-12-14-09-31-13.bpo-35332.s22wAx.rst b/Misc/NEWS.d/next/Library/2020-12-14-09-31-13.bpo-35332.s22wAx.rst new file mode 100644 index 00000000000000..80564b99a079c6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-12-14-09-31-13.bpo-35332.s22wAx.rst @@ -0,0 +1,3 @@ +The :func:`shutil.rmtree` function now ignores errors when calling +:func:`os.close` when *ignore_errors* is ``True``, and +:func:`os.close` no longer retried after error. From c2e2df83560a3d4cb602f6d57cb70ac8aad7f9e6 Mon Sep 17 00:00:00 2001 From: "Jurjen N. E. Bos" Date: Tue, 5 Dec 2023 19:44:06 +0100 Subject: [PATCH 188/228] Minor stylistic edit to the grouper recipe (gh112759) --- Doc/library/itertools.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index ebb4ebcfa7618a..8a4254cf15ebe2 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -916,9 +916,9 @@ which incur interpreter overhead. args = [iter(iterable)] * n if incomplete == 'fill': return zip_longest(*args, fillvalue=fillvalue) - if incomplete == 'strict': + elif incomplete == 'strict': return zip(*args, strict=True) - if incomplete == 'ignore': + elif incomplete == 'ignore': return zip(*args) else: raise ValueError('Expected fill, strict, or ignore') From d109f637c048c2b5fc95dc7fdfd50f8ac41a7747 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 5 Dec 2023 19:27:59 +0000 Subject: [PATCH 189/228] gh-101100: Properly document frame object attributes (#112735) --- Doc/c-api/frame.rst | 8 ++-- Doc/c-api/init.rst | 5 ++- Doc/library/dis.rst | 4 +- Doc/library/inspect.rst | 4 +- Doc/library/sys.rst | 9 ++-- Doc/library/types.rst | 3 +- Doc/reference/datamodel.rst | 84 ++++++++++++++++++++++++++----------- Doc/whatsnew/2.3.rst | 4 +- Doc/whatsnew/3.10.rst | 7 ++-- Doc/whatsnew/3.11.rst | 5 ++- Doc/whatsnew/3.6.rst | 2 +- Doc/whatsnew/3.7.rst | 2 +- 12 files changed, 88 insertions(+), 49 deletions(-) diff --git a/Doc/c-api/frame.rst b/Doc/c-api/frame.rst index 1accee2767a485..6bb1e9b5803b58 100644 --- a/Doc/c-api/frame.rst +++ b/Doc/c-api/frame.rst @@ -50,7 +50,7 @@ See also :ref:`Reflection `. .. c:function:: PyObject* PyFrame_GetBuiltins(PyFrameObject *frame) - Get the *frame*'s ``f_builtins`` attribute. + Get the *frame*'s :attr:`~frame.f_builtins` attribute. Return a :term:`strong reference`. The result cannot be ``NULL``. @@ -81,7 +81,7 @@ See also :ref:`Reflection `. .. c:function:: PyObject* PyFrame_GetGlobals(PyFrameObject *frame) - Get the *frame*'s ``f_globals`` attribute. + Get the *frame*'s :attr:`~frame.f_globals` attribute. Return a :term:`strong reference`. The result cannot be ``NULL``. @@ -90,7 +90,7 @@ See also :ref:`Reflection `. .. c:function:: int PyFrame_GetLasti(PyFrameObject *frame) - Get the *frame*'s ``f_lasti`` attribute. + Get the *frame*'s :attr:`~frame.f_lasti` attribute. Returns -1 if ``frame.f_lasti`` is ``None``. @@ -120,7 +120,7 @@ See also :ref:`Reflection `. .. c:function:: PyObject* PyFrame_GetLocals(PyFrameObject *frame) - Get the *frame*'s ``f_locals`` attribute (:class:`dict`). + Get the *frame*'s :attr:`~frame.f_locals` attribute (:class:`dict`). Return a :term:`strong reference`. diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index e89641f74c7491..f8fd48e781d6da 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1662,7 +1662,8 @@ Python-level trace functions in previous versions. The value passed as the *what* parameter to a :c:type:`Py_tracefunc` function (but not a profiling function) when a line-number event is being reported. - It may be disabled for a frame by setting :attr:`f_trace_lines` to *0* on that frame. + It may be disabled for a frame by setting :attr:`~frame.f_trace_lines` to + *0* on that frame. .. c:var:: int PyTrace_RETURN @@ -1694,7 +1695,7 @@ Python-level trace functions in previous versions. The value for the *what* parameter to :c:type:`Py_tracefunc` functions (but not profiling functions) when a new opcode is about to be executed. This event is not emitted by default: it must be explicitly requested by setting - :attr:`f_trace_opcodes` to *1* on the frame. + :attr:`~frame.f_trace_opcodes` to *1* on the frame. .. c:function:: void PyEval_SetProfile(Py_tracefunc func, PyObject *obj) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 7e97f1a4524554..cf238f81b9cc64 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -865,8 +865,8 @@ iterations of the loop. .. opcode:: RERAISE Re-raises the exception currently on top of the stack. If oparg is non-zero, - pops an additional value from the stack which is used to set ``f_lasti`` - of the current frame. + pops an additional value from the stack which is used to set + :attr:`~frame.f_lasti` of the current frame. .. versionadded:: 3.9 diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 6fd0d32afe7415..08f15ae09b1b87 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -1605,8 +1605,8 @@ the following flags: .. data:: CO_NEWLOCALS - If set, a new dict will be created for the frame's ``f_locals`` when - the code object is executed. + If set, a new dict will be created for the frame's :attr:`~frame.f_locals` + when the code object is executed. .. data:: CO_VARARGS diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 7f359819e6847e..aaf79205d44282 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -1600,7 +1600,8 @@ always available. :file:`Objects/lnotab_notes.txt` for a detailed explanation of how this works. Per-line events may be disabled for a frame by setting - :attr:`!f_trace_lines` to :const:`False` on that :ref:`frame `. + :attr:`~frame.f_trace_lines` to :const:`False` on that + :ref:`frame `. ``'return'`` A function (or other code block) is about to return. The local trace @@ -1618,7 +1619,7 @@ always available. opcode details). The local trace function is called; *arg* is ``None``; the return value specifies the new local trace function. Per-opcode events are not emitted by default: they must be explicitly - requested by setting :attr:`!f_trace_opcodes` to :const:`True` on the + requested by setting :attr:`~frame.f_trace_opcodes` to :const:`True` on the :ref:`frame `. Note that as an exception is propagated down the chain of callers, an @@ -1648,8 +1649,8 @@ always available. .. versionchanged:: 3.7 - ``'opcode'`` event type added; :attr:`!f_trace_lines` and - :attr:`!f_trace_opcodes` attributes added to frames + ``'opcode'`` event type added; :attr:`~frame.f_trace_lines` and + :attr:`~frame.f_trace_opcodes` attributes added to frames .. function:: set_asyncgen_hooks(firstiter, finalizer) diff --git a/Doc/library/types.rst b/Doc/library/types.rst index 54c3907dec98cc..22766462822af9 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -388,7 +388,8 @@ Standard names are defined for the following types: .. data:: GetSetDescriptorType The type of objects defined in extension modules with ``PyGetSetDef``, such - as ``FrameType.f_locals`` or ``array.array.typecode``. This type is used as + as :attr:`FrameType.f_locals ` or ``array.array.typecode``. + This type is used as descriptor for object attributes; it has the same purpose as the :class:`property` type, but for classes defined in extension modules. diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 06e61393fccc24..8a94b7bb22c362 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1174,16 +1174,36 @@ Frame objects represent execution frames. They may occur in traceback objects single: f_lasti (frame attribute) single: f_builtins (frame attribute) -Special read-only attributes: :attr:`f_back` is to the previous stack frame -(towards the caller), or ``None`` if this is the bottom stack frame; -:attr:`f_code` is the code object being executed in this frame; :attr:`f_locals` -is the dictionary used to look up local variables; :attr:`f_globals` is used for -global variables; :attr:`f_builtins` is used for built-in (intrinsic) names; -:attr:`f_lasti` gives the precise instruction (this is an index into the -bytecode string of the code object). +Special read-only attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Accessing ``f_code`` raises an :ref:`auditing event ` -``object.__getattr__`` with arguments ``obj`` and ``"f_code"``. +.. list-table:: + + * - .. attribute:: frame.f_back + - Points to the previous stack frame (towards the caller), + or ``None`` if this is the bottom stack frame + + * - .. attribute:: frame.f_code + - The :ref:`code object ` being executed in this frame. + Accessing this attribute raises an :ref:`auditing event ` + ``object.__getattr__`` with arguments ``obj`` and ``"f_code"``. + + * - .. attribute:: frame.f_locals + - The dictionary used by the frame to look up + :ref:`local variables ` + + * - .. attribute:: frame.f_globals + - The dictionary used by the frame to look up + :ref:`global variables ` + + * - .. attribute:: frame.f_builtins + - The dictionary used by the frame to look up + :ref:`built-in (intrinsic) names ` + + * - .. attribute:: frame.f_lasti + - The "precise instruction" of the frame object + (this is an index into the :term:`bytecode` string of the + :ref:`code object `) .. index:: single: f_trace (frame attribute) @@ -1191,30 +1211,44 @@ Accessing ``f_code`` raises an :ref:`auditing event ` single: f_trace_opcodes (frame attribute) single: f_lineno (frame attribute) -Special writable attributes: :attr:`f_trace`, if not ``None``, is a function -called for various events during code execution (this is used by the debugger). -Normally an event is triggered for each new source line - this can be -disabled by setting :attr:`f_trace_lines` to :const:`False`. +Special writable attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. list-table:: + + * - .. attribute:: frame.f_trace + - If not ``None``, this is a function called for various events during + code execution (this is used by debuggers). Normally an event is + triggered for each new source line (see :attr:`~frame.f_trace_lines`). + + * - .. attribute:: frame.f_trace_lines + - Set this attribute to :const:`False` to disable triggering a tracing + event for each source line. + + * - .. attribute:: frame.f_trace_opcodes + - Set this attribute to :const:`True` to allow per-opcode events to be + requested. Note that this may lead to + undefined interpreter behaviour if exceptions raised by the trace + function escape to the function being traced. -Implementations *may* allow per-opcode events to be requested by setting -:attr:`f_trace_opcodes` to :const:`True`. Note that this may lead to -undefined interpreter behaviour if exceptions raised by the trace -function escape to the function being traced. + * - .. attribute:: frame.f_lineno + - The current line number of the frame -- writing to this + from within a trace function jumps to the given line (only for the bottom-most + frame). A debugger can implement a Jump command (aka Set Next Statement) + by writing to this attribute. -:attr:`f_lineno` is the current line number of the frame --- writing to this -from within a trace function jumps to the given line (only for the bottom-most -frame). A debugger can implement a Jump command (aka Set Next Statement) -by writing to f_lineno. +Frame object methods +~~~~~~~~~~~~~~~~~~~~ Frame objects support one method: .. method:: frame.clear() - This method clears all references to local variables held by the - frame. Also, if the frame belonged to a generator, the generator + This method clears all references to :ref:`local variables ` held by the + frame. Also, if the frame belonged to a :term:`generator`, the generator is finalized. This helps break reference cycles involving frame - objects (for example when catching an exception and storing its - traceback for later use). + objects (for example when catching an :ref:`exception ` + and storing its :ref:`traceback ` for later use). :exc:`RuntimeError` is raised if the frame is currently executing or suspended. diff --git a/Doc/whatsnew/2.3.rst b/Doc/whatsnew/2.3.rst index af332b28a28231..c989e6d3fa5787 100644 --- a/Doc/whatsnew/2.3.rst +++ b/Doc/whatsnew/2.3.rst @@ -1998,13 +1998,13 @@ Some of the more notable changes are: It would be difficult to detect any resulting difference from Python code, apart from a slight speed up when Python is run without :option:`-O`. - C extensions that access the :attr:`f_lineno` field of frame objects should + C extensions that access the :attr:`~frame.f_lineno` field of frame objects should instead call ``PyCode_Addr2Line(f->f_code, f->f_lasti)``. This will have the added effect of making the code work as desired under "python -O" in earlier versions of Python. A nifty new feature is that trace functions can now assign to the - :attr:`f_lineno` attribute of frame objects, changing the line that will be + :attr:`~frame.f_lineno` attribute of frame objects, changing the line that will be executed next. A ``jump`` command has been added to the :mod:`pdb` debugger taking advantage of this new feature. (Implemented by Richie Hindle.) diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index df821d68eb8d9f..15479cc979624f 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -399,7 +399,8 @@ PEP 626: Precise line numbers for debugging and other tools PEP 626 brings more precise and reliable line numbers for debugging, profiling and coverage tools. Tracing events, with the correct line number, are generated for all lines of code executed and only for lines of code that are executed. -The ``f_lineno`` attribute of frame objects will always contain the expected line number. +The :attr:`~frame.f_lineno` attribute of frame objects will always contain the +expected line number. The ``co_lnotab`` attribute of code objects is deprecated and will be removed in 3.12. Code that needs to convert from offset to line number should use the new ``co_lines()`` method instead. @@ -1959,11 +1960,11 @@ Changes in the C API source_buf = PyBytes_AsString(source_bytes_object); code = Py_CompileString(source_buf, filename, Py_file_input); - * For ``FrameObject`` objects, the ``f_lasti`` member now represents a wordcode + * For ``FrameObject`` objects, the :attr:`~frame.f_lasti` member now represents a wordcode offset instead of a simple offset into the bytecode string. This means that this number needs to be multiplied by 2 to be used with APIs that expect a byte offset instead (like :c:func:`PyCode_Addr2Line` for example). Notice as well that the - ``f_lasti`` member of ``FrameObject`` objects is not considered stable: please + :attr:`!f_lasti` member of ``FrameObject`` objects is not considered stable: please use :c:func:`PyFrame_GetLineNumber` instead. CPython bytecode changes diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 48a0e621baad02..8db133b90a7a4b 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -2458,11 +2458,12 @@ Porting to Python 3.11 * ``f_valuestack``: removed. The Python frame object is now created lazily. A side effect is that the - ``f_back`` member must not be accessed directly, since its value is now also + :attr:`~frame.f_back` member must not be accessed directly, + since its value is now also computed lazily. The :c:func:`PyFrame_GetBack` function must be called instead. - Debuggers that accessed the ``f_locals`` directly *must* call + Debuggers that accessed the :attr:`~frame.f_locals` directly *must* call :c:func:`PyFrame_GetLocals` instead. They no longer need to call :c:func:`PyFrame_FastToLocalsWithError` or :c:func:`PyFrame_LocalsToFast`, in fact they should not call those functions. The necessary updating of the diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst index c15d8be651fd17..2f618929793fc6 100644 --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -2162,7 +2162,7 @@ Changes in the Python API * The format of the ``co_lnotab`` attribute of code objects changed to support a negative line number delta. By default, Python does not emit bytecode with - a negative line number delta. Functions using ``frame.f_lineno``, + a negative line number delta. Functions using :attr:`frame.f_lineno`, ``PyFrame_GetLineNumber()`` or ``PyCode_Addr2Line()`` are not affected. Functions directly decoding ``co_lnotab`` should be updated to use a signed 8-bit integer type for the line number delta, but this is only required to diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index a7d5c3db6ddcb2..99f280af84ab01 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -1891,7 +1891,7 @@ Other CPython Implementation Changes * Trace hooks may now opt out of receiving the ``line`` and opt into receiving the ``opcode`` events from the interpreter by setting the corresponding new - ``f_trace_lines`` and ``f_trace_opcodes`` attributes on the + :attr:`~frame.f_trace_lines` and :attr:`~frame.f_trace_opcodes` attributes on the frame being traced. (Contributed by Nick Coghlan in :issue:`31344`.) * Fixed some consistency problems with namespace package module attributes. From d384813ff18b33280a90b6d2011654528a2b6ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 5 Dec 2023 20:39:28 +0100 Subject: [PATCH 190/228] gh-112769: test_zlib: Fix comparison of ZLIB_RUNTIME_VERSION with non-int suffix (GH-112771) zlib-ng defines the version as "1.3.0.zlib-ng". --- Lib/test/test_zlib.py | 29 +++++++++++-------- ...-12-05-19-50-03.gh-issue-112769.kdLJmS.rst | 3 ++ 2 files changed, 20 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-12-05-19-50-03.gh-issue-112769.kdLJmS.rst diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index 1dc8b91a453f92..ef02c64f886f8a 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -18,6 +18,21 @@ hasattr(zlib.decompressobj(), "copy"), 'requires Decompress.copy()') + +def _zlib_runtime_version_tuple(zlib_version=zlib.ZLIB_RUNTIME_VERSION): + # Register "1.2.3" as "1.2.3.0" + # or "1.2.0-linux","1.2.0.f","1.2.0.f-linux" + v = zlib_version.split('-', 1)[0].split('.') + if len(v) < 4: + v.append('0') + elif not v[-1].isnumeric(): + v[-1] = '0' + return tuple(map(int, v)) + + +ZLIB_RUNTIME_VERSION_TUPLE = _zlib_runtime_version_tuple() + + # bpo-46623: On s390x, when a hardware accelerator is used, using different # ways to compress data with zlib can produce different compressed data. # Simplified test_pair() code: @@ -473,9 +488,8 @@ def test_flushes(self): sync_opt = ['Z_NO_FLUSH', 'Z_SYNC_FLUSH', 'Z_FULL_FLUSH', 'Z_PARTIAL_FLUSH'] - ver = tuple(int(v) for v in zlib.ZLIB_RUNTIME_VERSION.split('.')) # Z_BLOCK has a known failure prior to 1.2.5.3 - if ver >= (1, 2, 5, 3): + if ZLIB_RUNTIME_VERSION_TUPLE >= (1, 2, 5, 3): sync_opt.append('Z_BLOCK') sync_opt = [getattr(zlib, opt) for opt in sync_opt @@ -793,16 +807,7 @@ def test_large_unconsumed_tail(self, size): def test_wbits(self): # wbits=0 only supported since zlib v1.2.3.5 - # Register "1.2.3" as "1.2.3.0" - # or "1.2.0-linux","1.2.0.f","1.2.0.f-linux" - v = zlib.ZLIB_RUNTIME_VERSION.split('-', 1)[0].split('.') - if len(v) < 4: - v.append('0') - elif not v[-1].isnumeric(): - v[-1] = '0' - - v = tuple(map(int, v)) - supports_wbits_0 = v >= (1, 2, 3, 5) + supports_wbits_0 = ZLIB_RUNTIME_VERSION_TUPLE >= (1, 2, 3, 5) co = zlib.compressobj(level=1, wbits=15) zlib15 = co.compress(HAMLET_SCENE) + co.flush() diff --git a/Misc/NEWS.d/next/Tests/2023-12-05-19-50-03.gh-issue-112769.kdLJmS.rst b/Misc/NEWS.d/next/Tests/2023-12-05-19-50-03.gh-issue-112769.kdLJmS.rst new file mode 100644 index 00000000000000..1bbbb26fc322fa --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-12-05-19-50-03.gh-issue-112769.kdLJmS.rst @@ -0,0 +1,3 @@ +The tests now correctly compare zlib version when +:const:`zlib.ZLIB_RUNTIME_VERSION` contains non-integer suffixes. For +example zlib-ng defines the version as ``1.3.0.zlib-ng``. From a2a46f9f1e08be26fa1f732a2b92e355ad812abf Mon Sep 17 00:00:00 2001 From: Matt Prodani Date: Wed, 6 Dec 2023 01:54:57 -0500 Subject: [PATCH 191/228] gh-112606: Use sem_clockwait with monotonic time when supported in parking_lot.c (gh-112733) --- Python/parking_lot.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Python/parking_lot.c b/Python/parking_lot.c index 664e622cc17474..d44c1b4b93b4d2 100644 --- a/Python/parking_lot.c +++ b/Python/parking_lot.c @@ -118,10 +118,19 @@ _PySemaphore_PlatformWait(_PySemaphore *sema, _PyTime_t timeout) if (timeout >= 0) { struct timespec ts; +#if defined(CLOCK_MONOTONIC) && defined(HAVE_SEM_CLOCKWAIT) + _PyTime_t deadline = _PyTime_Add(_PyTime_GetMonotonicClock(), timeout); + + _PyTime_AsTimespec_clamp(deadline, &ts); + + err = sem_clockwait(&sema->platform_sem, CLOCK_MONOTONIC, &ts); +#else _PyTime_t deadline = _PyTime_Add(_PyTime_GetSystemClock(), timeout); - _PyTime_AsTimespec(deadline, &ts); + + _PyTime_AsTimespec_clamp(deadline, &ts); err = sem_timedwait(&sema->platform_sem, &ts); +#endif } else { err = sem_wait(&sema->platform_sem); @@ -151,7 +160,7 @@ _PySemaphore_PlatformWait(_PySemaphore *sema, _PyTime_t timeout) struct timespec ts; _PyTime_t deadline = _PyTime_Add(_PyTime_GetSystemClock(), timeout); - _PyTime_AsTimespec(deadline, &ts); + _PyTime_AsTimespec_clamp(deadline, &ts); err = pthread_cond_timedwait(&sema->cond, &sema->mutex, &ts); } From f8c0198e3bfa2f6f65e426765a5efddd8ece78b0 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 6 Dec 2023 09:48:27 +0200 Subject: [PATCH 192/228] gh-108927: Include new dir test/regrtestdata in the installation (GH-112765) Co-authored-by: Victor Stinner --- Makefile.pre.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile.pre.in b/Makefile.pre.in index e7f8abce43d648..b5edb4e6748fb0 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2194,6 +2194,9 @@ TESTSUBDIRS= idlelib/idle_test \ test/leakers \ test/libregrtest \ test/mathdata \ + test/regrtestdata \ + test/regrtestdata/import_from_tests \ + test/regrtestdata/import_from_tests/test_regrtest_b \ test/subprocessdata \ test/support \ test/support/_hypothesis_stubs \ From e3f670e13792305cfb977d5cffd8e6aa03e8fe7f Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 6 Dec 2023 08:44:06 +0000 Subject: [PATCH 193/228] gh-101100: Fix most Sphinx nitpicks in the glossary and `stdtypes.rst` (#112757) --- Doc/glossary.rst | 47 ++++++++++++++++++++++------------------ Doc/library/stdtypes.rst | 26 ++++++++++++---------- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 29f2f80cebd5f0..601443d5aade94 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -151,9 +151,9 @@ Glossary A :term:`file object` able to read and write :term:`bytes-like objects `. Examples of binary files are files opened in binary mode (``'rb'``, - ``'wb'`` or ``'rb+'``), :data:`sys.stdin.buffer`, - :data:`sys.stdout.buffer`, and instances of :class:`io.BytesIO` and - :class:`gzip.GzipFile`. + ``'wb'`` or ``'rb+'``), :data:`sys.stdin.buffer `, + :data:`sys.stdout.buffer `, and instances of + :class:`io.BytesIO` and :class:`gzip.GzipFile`. See also :term:`text file` for a file object able to read and write :class:`str` objects. @@ -304,8 +304,9 @@ Glossary :ref:`class definitions ` for more about decorators. descriptor - Any object which defines the methods :meth:`__get__`, :meth:`__set__`, or - :meth:`__delete__`. When a class attribute is a descriptor, its special + Any object which defines the methods :meth:`~object.__get__`, + :meth:`~object.__set__`, or :meth:`~object.__delete__`. + When a class attribute is a descriptor, its special binding behavior is triggered upon attribute lookup. Normally, using *a.b* to get, set or delete an attribute looks up the object named *b* in the class dictionary for *a*, but if *b* is a descriptor, the respective @@ -319,7 +320,8 @@ Glossary dictionary An associative array, where arbitrary keys are mapped to values. The - keys can be any object with :meth:`__hash__` and :meth:`__eq__` methods. + keys can be any object with :meth:`~object.__hash__` and + :meth:`~object.__eq__` methods. Called a hash in Perl. dictionary comprehension @@ -383,7 +385,7 @@ Glossary file object An object exposing a file-oriented API (with methods such as - :meth:`read()` or :meth:`write()`) to an underlying resource. Depending + :meth:`!read` or :meth:`!write`) to an underlying resource. Depending on the way it was created, a file object can mediate access to a real on-disk file or to another type of storage or communication device (for example standard input/output, in-memory buffers, sockets, pipes, @@ -559,8 +561,9 @@ Glossary hashable An object is *hashable* if it has a hash value which never changes during - its lifetime (it needs a :meth:`__hash__` method), and can be compared to - other objects (it needs an :meth:`__eq__` method). Hashable objects which + its lifetime (it needs a :meth:`~object.__hash__` method), and can be + compared to other objects (it needs an :meth:`~object.__eq__` method). + Hashable objects which compare equal must have the same hash value. Hashability makes an object usable as a dictionary key and a set member, @@ -646,7 +649,8 @@ Glossary iterables include all sequence types (such as :class:`list`, :class:`str`, and :class:`tuple`) and some non-sequence types like :class:`dict`, :term:`file objects `, and objects of any classes you define - with an :meth:`__iter__` method or with a :meth:`~object.__getitem__` method + with an :meth:`~iterator.__iter__` method or with a + :meth:`~object.__getitem__` method that implements :term:`sequence` semantics. Iterables can be @@ -655,7 +659,7 @@ Glossary as an argument to the built-in function :func:`iter`, it returns an iterator for the object. This iterator is good for one pass over the set of values. When using iterables, it is usually not necessary to call - :func:`iter` or deal with iterator objects yourself. The ``for`` + :func:`iter` or deal with iterator objects yourself. The :keyword:`for` statement does that automatically for you, creating a temporary unnamed variable to hold the iterator for the duration of the loop. See also :term:`iterator`, :term:`sequence`, and :term:`generator`. @@ -666,8 +670,8 @@ Glossary :func:`next`) return successive items in the stream. When no more data are available a :exc:`StopIteration` exception is raised instead. At this point, the iterator object is exhausted and any further calls to its - :meth:`__next__` method just raise :exc:`StopIteration` again. Iterators - are required to have an :meth:`__iter__` method that returns the iterator + :meth:`!__next__` method just raise :exc:`StopIteration` again. Iterators + are required to have an :meth:`~iterator.__iter__` method that returns the iterator object itself so every iterator is also iterable and may be used in most places where other iterables are accepted. One notable exception is code which attempts multiple iteration passes. A container object (such as a @@ -681,7 +685,7 @@ Glossary .. impl-detail:: CPython does not consistently apply the requirement that an iterator - define :meth:`__iter__`. + define :meth:`~iterator.__iter__`. key function A key function or collation function is a callable that returns a value @@ -875,7 +879,8 @@ Glossary Old name for the flavor of classes now used for all class objects. In earlier Python versions, only new-style classes could use Python's newer, versatile features like :attr:`~object.__slots__`, descriptors, - properties, :meth:`__getattribute__`, class methods, and static methods. + properties, :meth:`~object.__getattribute__`, class methods, and static + methods. object Any data with state (attributes or value) and defined behavior @@ -955,7 +960,7 @@ Glossary finders implement. path entry hook - A callable on the :data:`sys.path_hook` list which returns a :term:`path + A callable on the :data:`sys.path_hooks` list which returns a :term:`path entry finder` if it knows how to find modules on a specific :term:`path entry`. @@ -1089,18 +1094,18 @@ Glossary sequence An :term:`iterable` which supports efficient element access using integer indices via the :meth:`~object.__getitem__` special method and defines a - :meth:`__len__` method that returns the length of the sequence. + :meth:`~object.__len__` method that returns the length of the sequence. Some built-in sequence types are :class:`list`, :class:`str`, :class:`tuple`, and :class:`bytes`. Note that :class:`dict` also - supports :meth:`~object.__getitem__` and :meth:`__len__`, but is considered a + supports :meth:`~object.__getitem__` and :meth:`!__len__`, but is considered a mapping rather than a sequence because the lookups use arbitrary :term:`immutable` keys rather than integers. The :class:`collections.abc.Sequence` abstract base class defines a much richer interface that goes beyond just - :meth:`~object.__getitem__` and :meth:`__len__`, adding :meth:`count`, - :meth:`index`, :meth:`__contains__`, and - :meth:`__reversed__`. Types that implement this expanded + :meth:`~object.__getitem__` and :meth:`~object.__len__`, adding + :meth:`count`, :meth:`index`, :meth:`~object.__contains__`, and + :meth:`~object.__reversed__`. Types that implement this expanded interface can be registered explicitly using :func:`~abc.ABCMeta.register`. diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index f204b287b565eb..44c13bd9474ea1 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -44,7 +44,8 @@ Any object can be tested for truth value, for use in an :keyword:`if` or .. index:: single: true By default, an object is considered true unless its class defines either a -:meth:`~object.__bool__` method that returns ``False`` or a :meth:`__len__` method that +:meth:`~object.__bool__` method that returns ``False`` or a +:meth:`~object.__len__` method that returns zero, when called with the object. [1]_ Here are most of the built-in objects considered false: @@ -197,7 +198,7 @@ exception. Two more operations with the same syntactic priority, :keyword:`in` and :keyword:`not in`, are supported by types that are :term:`iterable` or -implement the :meth:`__contains__` method. +implement the :meth:`~object.__contains__` method. .. _typesnumeric: @@ -717,7 +718,7 @@ that's defined for any rational number, and hence applies to all instances of :class:`int` and :class:`fractions.Fraction`, and all finite instances of :class:`float` and :class:`decimal.Decimal`. Essentially, this function is given by reduction modulo ``P`` for a fixed prime ``P``. The value of ``P`` is -made available to Python as the :attr:`modulus` attribute of +made available to Python as the :attr:`~sys.hash_info.modulus` attribute of :data:`sys.hash_info`. .. impl-detail:: @@ -906,9 +907,9 @@ Generator Types --------------- Python's :term:`generator`\s provide a convenient way to implement the iterator -protocol. If a container object's :meth:`__iter__` method is implemented as a +protocol. If a container object's :meth:`~iterator.__iter__` method is implemented as a generator, it will automatically return an iterator object (technically, a -generator object) supplying the :meth:`__iter__` and :meth:`~generator.__next__` +generator object) supplying the :meth:`!__iter__` and :meth:`~generator.__next__` methods. More information about generators can be found in :ref:`the documentation for the yield expression `. @@ -3672,7 +3673,7 @@ The conversion types are: +------------+-----------------------------------------------------+-------+ | ``'b'`` | Bytes (any object that follows the | \(5) | | | :ref:`buffer protocol ` or has | | -| | :meth:`__bytes__`). | | +| | :meth:`~object.__bytes__`). | | +------------+-----------------------------------------------------+-------+ | ``'s'`` | ``'s'`` is an alias for ``'b'`` and should only | \(6) | | | be used for Python2/3 code bases. | | @@ -4410,7 +4411,8 @@ The constructors for both classes work the same: :meth:`symmetric_difference_update` methods will accept any iterable as an argument. - Note, the *elem* argument to the :meth:`__contains__`, :meth:`remove`, and + Note, the *elem* argument to the :meth:`~object.__contains__`, + :meth:`remove`, and :meth:`discard` methods may be a set. To support searching for an equivalent frozenset, a temporary one is created from *elem*. @@ -5236,9 +5238,11 @@ instantiated from the type:: TypeError: cannot create 'types.UnionType' instances .. note:: - The :meth:`__or__` method for type objects was added to support the syntax - ``X | Y``. If a metaclass implements :meth:`__or__`, the Union may - override it:: + The :meth:`!__or__` method for type objects was added to support the syntax + ``X | Y``. If a metaclass implements :meth:`!__or__`, the Union may + override it: + + .. doctest:: >>> class M(type): ... def __or__(self, other): @@ -5250,7 +5254,7 @@ instantiated from the type:: >>> C | int 'Hello' >>> int | C - int | __main__.C + int | C .. seealso:: From 00cce0fe495ee820cd3ca5878bdbe3dd65b1be7b Mon Sep 17 00:00:00 2001 From: Christopher Chavez Date: Wed, 6 Dec 2023 03:44:41 -0600 Subject: [PATCH 194/228] gh-111178: Docs: fix `traverseproc`, `inquiry`, and `destructor` parameters in slot typedefs table (GH-112742) In the slot typedefs table, the parameter of `destructor` and the first parameter of `traverseproc` should both be `PyObject *` rather than `void *`. Same for `inquiry`. --- Doc/c-api/typeobj.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 10c05beda7c66f..8a26f237652d12 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -343,13 +343,13 @@ slot typedefs | | :c:type:`PyTypeObject` * | | | | :c:type:`Py_ssize_t` | | +-----------------------------+-----------------------------+----------------------+ -| :c:type:`destructor` | void * | void | +| :c:type:`destructor` | :c:type:`PyObject` * | void | +-----------------------------+-----------------------------+----------------------+ | :c:type:`freefunc` | void * | void | +-----------------------------+-----------------------------+----------------------+ | :c:type:`traverseproc` | .. line-block:: | int | | | | | -| | void * | | +| | :c:type:`PyObject` * | | | | :c:type:`visitproc` | | | | void * | | +-----------------------------+-----------------------------+----------------------+ @@ -426,7 +426,7 @@ slot typedefs | | :c:type:`PyObject` * | | | | :c:type:`Py_buffer` * | | +-----------------------------+-----------------------------+----------------------+ -| :c:type:`inquiry` | void * | int | +| :c:type:`inquiry` | :c:type:`PyObject` * | int | +-----------------------------+-----------------------------+----------------------+ | :c:type:`unaryfunc` | .. line-block:: | :c:type:`PyObject` * | | | | | From f8852634edf1232ac1aa4561e34796b52f9f7aa2 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 6 Dec 2023 12:55:58 +0100 Subject: [PATCH 195/228] gh-108223: Refer to PEP 703 as Free Threading (#112780) --- Doc/using/configure.rst | 5 ++++- Lib/test/libregrtest/utils.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 56d2d6dc4ab5f1..cb7eda42fe3fad 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -292,7 +292,10 @@ General Options .. option:: --disable-gil Enables **experimental** support for running Python without the - :term:`global interpreter lock` (GIL). + :term:`global interpreter lock` (GIL): free threading build. + + Defines the ``Py_GIL_DISABLED`` macro and adds ``"t"`` to + :data:`sys.abiflags`. See :pep:`703` "Making the Global Interpreter Lock Optional in CPython". diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index d4972ce4a50d2a..26481e71221ade 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -291,7 +291,7 @@ def get_build_info(): # --disable-gil if sysconfig.get_config_var('Py_GIL_DISABLED'): - build.append("nogil") + build.append("free_threading") if hasattr(sys, 'gettotalrefcount'): # --with-pydebug From 828451dfde324f9499ffebc023a22b84dc5a125b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 6 Dec 2023 15:09:22 +0100 Subject: [PATCH 196/228] gh-111545: Add Py_HashPointer() function (#112096) * Implement _Py_HashPointerRaw() as a static inline function. * Add Py_HashPointer() tests to test_capi.test_hash. * Keep _Py_HashPointer() function as an alias to Py_HashPointer(). --- Doc/c-api/hash.rst | 10 ++++ Doc/whatsnew/3.13.rst | 3 ++ Include/cpython/pyhash.h | 6 ++- Include/internal/pycore_pyhash.h | 16 ++++++- Lib/test/test_capi/test_hash.py | 48 ++++++++++++++++++- ...-11-15-01-26-59.gh-issue-111545.iAoFtA.rst | 2 + Modules/_testcapi/hash.c | 16 +++++++ Python/hashtable.c | 2 +- Python/pyhash.c | 22 ++------- 9 files changed, 103 insertions(+), 22 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2023-11-15-01-26-59.gh-issue-111545.iAoFtA.rst diff --git a/Doc/c-api/hash.rst b/Doc/c-api/hash.rst index 3bfaf8b9f54c14..91d88ae27bc9f4 100644 --- a/Doc/c-api/hash.rst +++ b/Doc/c-api/hash.rst @@ -49,3 +49,13 @@ See also the :c:member:`PyTypeObject.tp_hash` member. :pep:`456` "Secure and interchangeable hash algorithm". .. versionadded:: 3.4 + + +.. c:function:: Py_hash_t Py_HashPointer(const void *ptr) + + Hash a pointer value: process the pointer value as an integer (cast it to + ``uintptr_t`` internally). The pointer is not dereferenced. + + The function cannot fail: it cannot return ``-1``. + + .. versionadded:: 3.13 diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 534813f3659c9d..c9facad6375ef3 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1249,6 +1249,9 @@ New Features :exc:`KeyError` if the key missing. (Contributed by Stefan Behnel and Victor Stinner in :gh:`111262`.) +* Add :c:func:`Py_HashPointer` function to hash a pointer. + (Contributed by Victor Stinner in :gh:`111545`.) + Porting to Python 3.13 ---------------------- diff --git a/Include/cpython/pyhash.h b/Include/cpython/pyhash.h index 6f7113daa5fe4d..396c208e1b106a 100644 --- a/Include/cpython/pyhash.h +++ b/Include/cpython/pyhash.h @@ -21,7 +21,9 @@ /* Helpers for hash functions */ PyAPI_FUNC(Py_hash_t) _Py_HashDouble(PyObject *, double); -PyAPI_FUNC(Py_hash_t) _Py_HashPointer(const void*); + +// Kept for backward compatibility +#define _Py_HashPointer Py_HashPointer /* hash function definition */ @@ -33,3 +35,5 @@ typedef struct { } PyHash_FuncDef; PyAPI_FUNC(PyHash_FuncDef*) PyHash_GetFuncDef(void); + +PyAPI_FUNC(Py_hash_t) Py_HashPointer(const void *ptr); diff --git a/Include/internal/pycore_pyhash.h b/Include/internal/pycore_pyhash.h index c3b72d90de3a69..0ce08900e96f0b 100644 --- a/Include/internal/pycore_pyhash.h +++ b/Include/internal/pycore_pyhash.h @@ -5,8 +5,20 @@ # error "this header requires Py_BUILD_CORE define" #endif -// Similar to _Py_HashPointer(), but don't replace -1 with -2 -extern Py_hash_t _Py_HashPointerRaw(const void*); +// Similar to Py_HashPointer(), but don't replace -1 with -2. +static inline Py_hash_t +_Py_HashPointerRaw(const void *ptr) +{ + uintptr_t x = (uintptr_t)ptr; + Py_BUILD_ASSERT(sizeof(x) == sizeof(ptr)); + + // Bottom 3 or 4 bits are likely to be 0; rotate x by 4 to the right + // to avoid excessive hash collisions for dicts and sets. + x = (x >> 4) | (x << (8 * sizeof(uintptr_t) - 4)); + + Py_BUILD_ASSERT(sizeof(x) == sizeof(Py_hash_t)); + return (Py_hash_t)x; +} // Export for '_datetime' shared extension PyAPI_FUNC(Py_hash_t) _Py_HashBytes(const void*, Py_ssize_t); diff --git a/Lib/test/test_capi/test_hash.py b/Lib/test/test_capi/test_hash.py index 59dec15bc21445..8436da7c32df10 100644 --- a/Lib/test/test_capi/test_hash.py +++ b/Lib/test/test_capi/test_hash.py @@ -4,7 +4,8 @@ _testcapi = import_helper.import_module('_testcapi') -SIZEOF_PY_HASH_T = _testcapi.SIZEOF_VOID_P +SIZEOF_VOID_P = _testcapi.SIZEOF_VOID_P +SIZEOF_PY_HASH_T = SIZEOF_VOID_P class CAPITest(unittest.TestCase): @@ -31,3 +32,48 @@ def test_hash_getfuncdef(self): self.assertEqual(func_def.name, hash_info.algorithm) self.assertEqual(func_def.hash_bits, hash_info.hash_bits) self.assertEqual(func_def.seed_bits, hash_info.seed_bits) + + def test_hash_pointer(self): + # Test Py_HashPointer() + hash_pointer = _testcapi.hash_pointer + + UHASH_T_MASK = ((2 ** (8 * SIZEOF_PY_HASH_T)) - 1) + HASH_T_MAX = (2 ** (8 * SIZEOF_PY_HASH_T - 1) - 1) + + def python_hash_pointer(x): + # Py_HashPointer() rotates the pointer bits by 4 bits to the right + x = (x >> 4) | ((x & 15) << (8 * SIZEOF_VOID_P - 4)) + + # Convert unsigned uintptr_t (Py_uhash_t) to signed Py_hash_t + if HASH_T_MAX < x: + x = (~x) + 1 + x &= UHASH_T_MASK + x = (~x) + 1 + return x + + if SIZEOF_VOID_P == 8: + values = ( + 0xABCDEF1234567890, + 0x1234567890ABCDEF, + 0xFEE4ABEDD1CECA5E, + ) + else: + values = ( + 0x12345678, + 0x1234ABCD, + 0xDEADCAFE, + ) + + for value in values: + expected = python_hash_pointer(value) + with self.subTest(value=value): + self.assertEqual(hash_pointer(value), expected, + f"hash_pointer({value:x}) = " + f"{hash_pointer(value):x} != {expected:x}") + + # Py_HashPointer(NULL) returns 0 + self.assertEqual(hash_pointer(0), 0) + + # Py_HashPointer((void*)(uintptr_t)-1) doesn't return -1 but -2 + VOID_P_MAX = -1 & (2 ** (8 * SIZEOF_VOID_P) - 1) + self.assertEqual(hash_pointer(VOID_P_MAX), -2) diff --git a/Misc/NEWS.d/next/C API/2023-11-15-01-26-59.gh-issue-111545.iAoFtA.rst b/Misc/NEWS.d/next/C API/2023-11-15-01-26-59.gh-issue-111545.iAoFtA.rst new file mode 100644 index 00000000000000..7bde2498acf999 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-11-15-01-26-59.gh-issue-111545.iAoFtA.rst @@ -0,0 +1,2 @@ +Add :c:func:`Py_HashPointer` function to hash a pointer. Patch by Victor +Stinner. diff --git a/Modules/_testcapi/hash.c b/Modules/_testcapi/hash.c index d0b8127020c5c1..aee76787dcddb3 100644 --- a/Modules/_testcapi/hash.c +++ b/Modules/_testcapi/hash.c @@ -44,8 +44,24 @@ hash_getfuncdef(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) return result; } + +static PyObject * +hash_pointer(PyObject *Py_UNUSED(module), PyObject *arg) +{ + void *ptr = PyLong_AsVoidPtr(arg); + if (ptr == NULL && PyErr_Occurred()) { + return NULL; + } + + Py_hash_t hash = Py_HashPointer(ptr); + Py_BUILD_ASSERT(sizeof(long long) >= sizeof(hash)); + return PyLong_FromLongLong(hash); +} + + static PyMethodDef test_methods[] = { {"hash_getfuncdef", hash_getfuncdef, METH_NOARGS}, + {"hash_pointer", hash_pointer, METH_O}, {NULL}, }; diff --git a/Python/hashtable.c b/Python/hashtable.c index 8f5e8168ba1339..faf68fe4ff0bca 100644 --- a/Python/hashtable.c +++ b/Python/hashtable.c @@ -45,7 +45,7 @@ */ #include "Python.h" -#include "pycore_hashtable.h" +#include "pycore_hashtable.h" // export _Py_hashtable_new() #include "pycore_pyhash.h" // _Py_HashPointerRaw() #define HASHTABLE_MIN_SIZE 16 diff --git a/Python/pyhash.c b/Python/pyhash.c index f9060b8003a0a7..141407c265677a 100644 --- a/Python/pyhash.c +++ b/Python/pyhash.c @@ -83,8 +83,6 @@ static Py_ssize_t hashstats[Py_HASH_STATS_MAX + 1] = {0}; */ -Py_hash_t _Py_HashPointer(const void *); - Py_hash_t _Py_HashDouble(PyObject *inst, double v) { @@ -132,23 +130,13 @@ _Py_HashDouble(PyObject *inst, double v) } Py_hash_t -_Py_HashPointerRaw(const void *p) -{ - size_t y = (size_t)p; - /* bottom 3 or 4 bits are likely to be 0; rotate y by 4 to avoid - excessive hash collisions for dicts and sets */ - y = (y >> 4) | (y << (8 * SIZEOF_VOID_P - 4)); - return (Py_hash_t)y; -} - -Py_hash_t -_Py_HashPointer(const void *p) +Py_HashPointer(const void *ptr) { - Py_hash_t x = _Py_HashPointerRaw(p); - if (x == -1) { - x = -2; + Py_hash_t hash = _Py_HashPointerRaw(ptr); + if (hash == -1) { + hash = -2; } - return x; + return hash; } Py_hash_t From cc7e45cc572dd818412a649970fdee579417701f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 6 Dec 2023 16:42:15 +0200 Subject: [PATCH 197/228] gh-75666: Tkinter: "unbind(sequence, funcid)" now only unbinds "funcid" (GH-111322) Previously, "widget.unbind(sequence, funcid)" destroyed the current binding for "sequence", leaving "sequence" unbound, and deleted the "funcid" command. Now it removes only "funcid" from the binding for "sequence", keeping other commands, and deletes the "funcid" command. It leaves "sequence" unbound only if "funcid" was the last bound command. Co-authored-by: GiovanniL <13402461+GiovaLomba@users.noreply.github.com> --- Lib/test/test_tkinter/test_misc.py | 34 +++++++++++++++---- Lib/tkinter/__init__.py | 22 +++++++++--- ...3-10-25-16-37-13.gh-issue-75666.BpsWut.rst | 6 ++++ 3 files changed, 51 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-25-16-37-13.gh-issue-75666.BpsWut.rst diff --git a/Lib/test/test_tkinter/test_misc.py b/Lib/test/test_tkinter/test_misc.py index ca99caaf88b80d..6639eaaa59936a 100644 --- a/Lib/test/test_tkinter/test_misc.py +++ b/Lib/test/test_tkinter/test_misc.py @@ -479,26 +479,46 @@ def test2(e): pass def test_unbind2(self): f = self.frame + f.wait_visibility() + f.focus_force() + f.update_idletasks() event = '' self.assertEqual(f.bind(), ()) self.assertEqual(f.bind(event), '') - def test1(e): pass - def test2(e): pass + def test1(e): events.append('a') + def test2(e): events.append('b') + def test3(e): events.append('c') funcid = f.bind(event, test1) funcid2 = f.bind(event, test2, add=True) + funcid3 = f.bind(event, test3, add=True) + events = [] + f.event_generate(event) + self.assertEqual(events, ['a', 'b', 'c']) - f.unbind(event, funcid) + f.unbind(event, funcid2) script = f.bind(event) - self.assertNotIn(funcid, script) - self.assertCommandNotExist(funcid) - self.assertCommandExist(funcid2) + self.assertNotIn(funcid2, script) + self.assertIn(funcid, script) + self.assertIn(funcid3, script) + self.assertEqual(f.bind(), (event,)) + self.assertCommandNotExist(funcid2) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid3) + events = [] + f.event_generate(event) + self.assertEqual(events, ['a', 'c']) - f.unbind(event, funcid2) + f.unbind(event, funcid) + f.unbind(event, funcid3) self.assertEqual(f.bind(event), '') self.assertEqual(f.bind(), ()) self.assertCommandNotExist(funcid) self.assertCommandNotExist(funcid2) + self.assertCommandNotExist(funcid3) + events = [] + f.event_generate(event) + self.assertEqual(events, []) # non-idempotent self.assertRaises(tkinter.TclError, f.unbind, event, funcid2) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 0df7f9d889413c..124882420c255c 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -1527,10 +1527,24 @@ def bind(self, sequence=None, func=None, add=None): return self._bind(('bind', self._w), sequence, func, add) def unbind(self, sequence, funcid=None): - """Unbind for this widget for event SEQUENCE the - function identified with FUNCID.""" - self.tk.call('bind', self._w, sequence, '') - if funcid: + """Unbind for this widget the event SEQUENCE. + + If FUNCID is given, only unbind the function identified with FUNCID + and also delete the corresponding Tcl command. + + Otherwise destroy the current binding for SEQUENCE, leaving SEQUENCE + unbound. + """ + if funcid is None: + self.tk.call('bind', self._w, sequence, '') + else: + lines = self.tk.call('bind', self._w, sequence).split('\n') + prefix = f'if {{"[{funcid} ' + keep = '\n'.join(line for line in lines + if not line.startswith(prefix)) + if not keep.strip(): + keep = '' + self.tk.call('bind', self._w, sequence, keep) self.deletecommand(funcid) def bind_all(self, sequence=None, func=None, add=None): diff --git a/Misc/NEWS.d/next/Library/2023-10-25-16-37-13.gh-issue-75666.BpsWut.rst b/Misc/NEWS.d/next/Library/2023-10-25-16-37-13.gh-issue-75666.BpsWut.rst new file mode 100644 index 00000000000000..d774cc4f7c687f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-25-16-37-13.gh-issue-75666.BpsWut.rst @@ -0,0 +1,6 @@ +Fix the behavior of :mod:`tkinter` widget's ``unbind()`` method with two +arguments. Previously, ``widget.unbind(sequence, funcid)`` destroyed the +current binding for *sequence*, leaving *sequence* unbound, and deleted the +*funcid* command. Now it removes only *funcid* from the binding for +*sequence*, keeping other commands, and deletes the *funcid* command. It +leaves *sequence* unbound only if *funcid* was the last bound command. From b920d6ceaa5532bf2bc8128e006229ec583374d0 Mon Sep 17 00:00:00 2001 From: Christopher Chavez Date: Wed, 6 Dec 2023 09:30:37 -0600 Subject: [PATCH 198/228] gh-111178: Define `visitproc` callback functions properly and remove unnecessary casts in gcmodule.c (#112687) --- Modules/gcmodule.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 568e02a4210a2b..8233fc56159b60 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -491,15 +491,16 @@ subtract_refs(PyGC_Head *containers) PyObject *op = FROM_GC(gc); traverse = Py_TYPE(op)->tp_traverse; (void) traverse(op, - (visitproc)visit_decref, + visit_decref, op); } } /* A traversal callback for move_unreachable. */ static int -visit_reachable(PyObject *op, PyGC_Head *reachable) +visit_reachable(PyObject *op, void *arg) { + PyGC_Head *reachable = arg; OBJECT_STAT_INC(object_visits); if (!_PyObject_IS_GC(op)) { return 0; @@ -603,7 +604,7 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable) // NOTE: visit_reachable may change gc->_gc_next when // young->_gc_prev == gc. Don't do gc = GC_NEXT(gc) before! (void) traverse(op, - (visitproc)visit_reachable, + visit_reachable, (void *)young); // relink gc_prev to prev element. _PyGCHead_SET_PREV(gc, prev); @@ -726,8 +727,9 @@ clear_unreachable_mask(PyGC_Head *unreachable) /* A traversal callback for move_legacy_finalizer_reachable. */ static int -visit_move(PyObject *op, PyGC_Head *tolist) +visit_move(PyObject *op, void *arg) { + PyGC_Head *tolist = arg; OBJECT_STAT_INC(object_visits); if (_PyObject_IS_GC(op)) { PyGC_Head *gc = AS_GC(op); @@ -751,7 +753,7 @@ move_legacy_finalizer_reachable(PyGC_Head *finalizers) /* Note that the finalizers list may grow during this. */ traverse = Py_TYPE(FROM_GC(gc))->tp_traverse; (void) traverse(FROM_GC(gc), - (visitproc)visit_move, + visit_move, (void *)finalizers); } } @@ -1684,8 +1686,9 @@ gc_get_count_impl(PyObject *module) } static int -referrersvisit(PyObject* obj, PyObject *objs) +referrersvisit(PyObject* obj, void *arg) { + PyObject *objs = arg; Py_ssize_t i; for (i = 0; i < PyTuple_GET_SIZE(objs); i++) if (PyTuple_GET_ITEM(objs, i) == obj) @@ -1704,7 +1707,7 @@ gc_referrers_for(PyObject *objs, PyGC_Head *list, PyObject *resultlist) traverse = Py_TYPE(obj)->tp_traverse; if (obj == objs || obj == resultlist) continue; - if (traverse(obj, (visitproc)referrersvisit, objs)) { + if (traverse(obj, referrersvisit, objs)) { if (PyList_Append(resultlist, obj) < 0) return 0; /* error */ } @@ -1740,8 +1743,9 @@ gc_get_referrers(PyObject *self, PyObject *args) /* Append obj to list; return true if error (out of memory), false if OK. */ static int -referentsvisit(PyObject *obj, PyObject *list) +referentsvisit(PyObject *obj, void *arg) { + PyObject *list = arg; return PyList_Append(list, obj) < 0; } @@ -1770,7 +1774,7 @@ gc_get_referents(PyObject *self, PyObject *args) traverse = Py_TYPE(obj)->tp_traverse; if (! traverse) continue; - if (traverse(obj, (visitproc)referentsvisit, result)) { + if (traverse(obj, referentsvisit, result)) { Py_DECREF(result); return NULL; } From e9707d3c3deb45a8352e85dbd5cf41afdb4a2a26 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 6 Dec 2023 20:15:46 +0000 Subject: [PATCH 199/228] gh-101100: Improve documentation of code object attributes (#112781) --- Doc/c-api/function.rst | 4 +- Doc/c-api/import.rst | 2 +- Doc/library/dis.rst | 19 +++--- Doc/library/inspect.rst | 4 +- Doc/reference/datamodel.rst | 114 ++++++++++++++++++++++++++---------- Doc/whatsnew/2.7.rst | 2 +- Doc/whatsnew/3.10.rst | 3 +- Doc/whatsnew/3.12.rst | 5 +- Doc/whatsnew/3.13.rst | 5 +- Doc/whatsnew/3.6.rst | 7 ++- 10 files changed, 112 insertions(+), 53 deletions(-) diff --git a/Doc/c-api/function.rst b/Doc/c-api/function.rst index 5857dba82c11c6..0a18e63c7e7a2c 100644 --- a/Doc/c-api/function.rst +++ b/Doc/c-api/function.rst @@ -37,7 +37,7 @@ There are a few functions specific to Python functions. The function's docstring and name are retrieved from the code object. *__module__* is retrieved from *globals*. The argument defaults, annotations and closure are set to ``NULL``. *__qualname__* is set to the same value as the code object's - ``co_qualname`` field. + :attr:`~codeobject.co_qualname` field. .. c:function:: PyObject* PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname) @@ -45,7 +45,7 @@ There are a few functions specific to Python functions. As :c:func:`PyFunction_New`, but also allows setting the function object's ``__qualname__`` attribute. *qualname* should be a unicode object or ``NULL``; if ``NULL``, the ``__qualname__`` attribute is set to the same value as the - code object's ``co_qualname`` field. + code object's :attr:`~codeobject.co_qualname` field. .. versionadded:: 3.3 diff --git a/Doc/c-api/import.rst b/Doc/c-api/import.rst index 137780cc359cf9..51c20b202f091c 100644 --- a/Doc/c-api/import.rst +++ b/Doc/c-api/import.rst @@ -154,7 +154,7 @@ Importing Modules :class:`~importlib.machinery.SourceFileLoader` otherwise. The module's :attr:`__file__` attribute will be set to the code object's - :attr:`!co_filename`. If applicable, :attr:`__cached__` will also + :attr:`~codeobject.co_filename`. If applicable, :attr:`__cached__` will also be set. This function will reload the module if it was already imported. See diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index cf238f81b9cc64..0d93bc9f5da774 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -346,8 +346,9 @@ operation is being performed, so the intermediate analysis object isn't useful: Line numbers can be decreasing. Before, they were always increasing. .. versionchanged:: 3.10 - The :pep:`626` ``co_lines`` method is used instead of the ``co_firstlineno`` - and ``co_lnotab`` attributes of the code object. + The :pep:`626` ``co_lines`` method is used instead of the + :attr:`~codeobject.co_firstlineno` and :attr:`~codeobject.co_lnotab` + attributes of the code object. .. versionchanged:: 3.13 Line numbers can be ``None`` for bytecode that does not map to source lines. @@ -983,13 +984,13 @@ iterations of the loop. .. opcode:: STORE_NAME (namei) Implements ``name = STACK.pop()``. *namei* is the index of *name* in the attribute - :attr:`!co_names` of the :ref:`code object `. + :attr:`~codeobject.co_names` of the :ref:`code object `. The compiler tries to use :opcode:`STORE_FAST` or :opcode:`STORE_GLOBAL` if possible. .. opcode:: DELETE_NAME (namei) - Implements ``del name``, where *namei* is the index into :attr:`!co_names` + Implements ``del name``, where *namei* is the index into :attr:`~codeobject.co_names` attribute of the :ref:`code object `. @@ -1029,7 +1030,7 @@ iterations of the loop. value = STACK.pop() obj.name = value - where *namei* is the index of name in :attr:`!co_names` of the + where *namei* is the index of name in :attr:`~codeobject.co_names` of the :ref:`code object `. .. opcode:: DELETE_ATTR (namei) @@ -1039,7 +1040,7 @@ iterations of the loop. obj = STACK.pop() del obj.name - where *namei* is the index of name into :attr:`!co_names` of the + where *namei* is the index of name into :attr:`~codeobject.co_names` of the :ref:`code object `. @@ -1402,7 +1403,7 @@ iterations of the loop. Pushes a reference to the object the cell contains on the stack. .. versionchanged:: 3.11 - ``i`` is no longer offset by the length of ``co_varnames``. + ``i`` is no longer offset by the length of :attr:`~codeobject.co_varnames`. .. opcode:: LOAD_FROM_DICT_OR_DEREF (i) @@ -1424,7 +1425,7 @@ iterations of the loop. storage. .. versionchanged:: 3.11 - ``i`` is no longer offset by the length of ``co_varnames``. + ``i`` is no longer offset by the length of :attr:`~codeobject.co_varnames`. .. opcode:: DELETE_DEREF (i) @@ -1435,7 +1436,7 @@ iterations of the loop. .. versionadded:: 3.2 .. versionchanged:: 3.11 - ``i`` is no longer offset by the length of ``co_varnames``. + ``i`` is no longer offset by the length of :attr:`~codeobject.co_varnames`. .. opcode:: COPY_FREE_VARS (n) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 08f15ae09b1b87..0138557f5fd84c 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -1596,8 +1596,8 @@ updated as expected: Code Objects Bit Flags ---------------------- -Python code objects have a ``co_flags`` attribute, which is a bitmap of -the following flags: +Python code objects have a :attr:`~codeobject.co_flags` attribute, +which is a bitmap of the following flags: .. data:: CO_OPTIMIZED diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 8a94b7bb22c362..3bcc170faa087a 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1077,57 +1077,111 @@ indirectly) to mutable objects. single: co_freevars (code object attribute) single: co_qualname (code object attribute) -Special read-only attributes: :attr:`co_name` gives the function name; -:attr:`co_qualname` gives the fully qualified function name; -:attr:`co_argcount` is the total number of positional arguments -(including positional-only arguments and arguments with default values); -:attr:`co_posonlyargcount` is the number of positional-only arguments -(including arguments with default values); :attr:`co_kwonlyargcount` is -the number of keyword-only arguments (including arguments with default -values); :attr:`co_nlocals` is the number of local variables used by the -function (including arguments); :attr:`co_varnames` is a tuple containing -the names of the local variables (starting with the argument names); -:attr:`co_cellvars` is a tuple containing the names of local variables -that are referenced by nested functions; :attr:`co_freevars` is a tuple -containing the names of free variables; :attr:`co_code` is a string -representing the sequence of bytecode instructions; :attr:`co_consts` is -a tuple containing the literals used by the bytecode; :attr:`co_names` is -a tuple containing the names used by the bytecode; :attr:`co_filename` is -the filename from which the code was compiled; :attr:`co_firstlineno` is -the first line number of the function; :attr:`co_lnotab` is a string -encoding the mapping from bytecode offsets to line numbers (for details -see the source code of the interpreter, is deprecated since 3.12 -and may be removed in 3.14); :attr:`co_stacksize` is the -required stack size; :attr:`co_flags` is an integer encoding a number -of flags for the interpreter. +Special read-only attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. list-table:: + + * - .. attribute:: codeobject.co_name + - The function name + + * - .. attribute:: codeobject.co_qualname + - The fully qualified function name + + * - .. attribute:: codeobject.co_argcount + - The total number of positional :term:`parameters ` + (including positional-only parameters and parameters with default values) + that the function has + + * - .. attribute:: codeobject.co_posonlyargcount + - The number of positional-only :term:`parameters ` + (including arguments with default values) that the function has + + * - .. attribute:: codeobject.co_kwonlyargcount + - The number of keyword-only :term:`parameters ` + (including arguments with default values) that the function has + + * - .. attribute:: codeobject.co_nlocals + - The number of :ref:`local variables ` used by the function + (including parameters) + + * - .. attribute:: codeobject.co_varnames + - A :class:`tuple` containing the names of the local variables in the + function (starting with the parameter names) + + * - .. attribute:: codeobject.co_cellvars + - A :class:`tuple` containing the names of :ref:`local variables ` + that are referenced by nested functions inside the function + + * - .. attribute:: codeobject.co_freevars + - A :class:`tuple` containing the names of free variables in the function + + * - .. attribute:: codeobject.co_code + - A string representing the sequence of :term:`bytecode` instructions in + the function + + * - .. attribute:: codeobject.co_consts + - A :class:`tuple` containing the literals used by the :term:`bytecode` in + the function + + * - .. attribute:: codeobject.co_names + - A :class:`tuple` containing the names used by the :term:`bytecode` in + the function + + * - .. attribute:: codeobject.co_filename + - The name of the file from which the code was compiled + + * - .. attribute:: codeobject.co_firstlineno + - The line number of the first line of the function + + * - .. attribute:: codeobject.co_lnotab + - A string encoding the mapping from :term:`bytecode` offsets to line + numbers. For details, see the source code of the interpreter. + + .. deprecated:: 3.12 + This attribute of code objects is deprecated, and may be removed in + Python 3.14. + + * - .. attribute:: codeobject.co_stacksize + - The required stack size of the code object + + * - .. attribute:: codeobject.co_flags + - An :class:`integer ` encoding a number of flags for the + interpreter. .. index:: pair: object; generator -The following flag bits are defined for :attr:`co_flags`: bit ``0x04`` is set if +The following flag bits are defined for :attr:`~codeobject.co_flags`: +bit ``0x04`` is set if the function uses the ``*arguments`` syntax to accept an arbitrary number of positional arguments; bit ``0x08`` is set if the function uses the ``**keywords`` syntax to accept arbitrary keyword arguments; bit ``0x20`` is set -if the function is a generator. +if the function is a generator. See :ref:`inspect-module-co-flags` for details +on the semantics of each flags that might be present. Future feature declarations (``from __future__ import division``) also use bits -in :attr:`co_flags` to indicate whether a code object was compiled with a +in :attr:`~codeobject.co_flags` to indicate whether a code object was compiled with a particular feature enabled: bit ``0x2000`` is set if the function was compiled with future division enabled; bits ``0x10`` and ``0x1000`` were used in earlier versions of Python. -Other bits in :attr:`co_flags` are reserved for internal use. +Other bits in :attr:`~codeobject.co_flags` are reserved for internal use. .. index:: single: documentation string -If a code object represents a function, the first item in :attr:`co_consts` is +If a code object represents a function, the first item in +:attr:`~codeobject.co_consts` is the documentation string of the function, or ``None`` if undefined. +The :meth:`!co_positions` method +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + .. method:: codeobject.co_positions() - Returns an iterable over the source code positions of each bytecode + Returns an iterable over the source code positions of each :term:`bytecode` instruction in the code object. - The iterator returns tuples containing the ``(start_line, end_line, + The iterator returns :class:`tuple`\s containing the ``(start_line, end_line, start_column, end_column)``. The *i-th* tuple corresponds to the position of the source code that compiled to the *i-th* instruction. Column information is 0-indexed utf-8 byte offsets on the given source diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index 4072e040dc9130..162dd74637479a 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -2405,7 +2405,7 @@ Other Changes and Fixes :issue:`5464`.) * When importing a module from a :file:`.pyc` or :file:`.pyo` file - with an existing :file:`.py` counterpart, the :attr:`co_filename` + with an existing :file:`.py` counterpart, the :attr:`~codeobject.co_filename` attributes of the resulting code objects are overwritten when the original filename is obsolete. This can happen if the file has been renamed, moved, or is accessed through different paths. (Patch by diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 15479cc979624f..2da90b7ed55744 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -402,7 +402,8 @@ Tracing events, with the correct line number, are generated for all lines of cod The :attr:`~frame.f_lineno` attribute of frame objects will always contain the expected line number. -The ``co_lnotab`` attribute of code objects is deprecated and will be removed in 3.12. +The :attr:`~codeobject.co_lnotab` attribute of code objects is deprecated and +will be removed in 3.12. Code that needs to convert from offset to line number should use the new ``co_lines()`` method instead. PEP 634: Structural Pattern Matching diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index fc17c86665335c..07d22a4a5fb773 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -1323,7 +1323,8 @@ Deprecated ``int``, convert to int explicitly: ``~int(x)``. (Contributed by Tim Hoffmann in :gh:`103487`.) -* Accessing ``co_lnotab`` on code objects was deprecated in Python 3.10 via :pep:`626`, +* Accessing :attr:`~codeobject.co_lnotab` on code objects was deprecated in + Python 3.10 via :pep:`626`, but it only got a proper :exc:`DeprecationWarning` in 3.12, therefore it will be removed in 3.14. (Contributed by Nikita Sobolev in :gh:`101866`.) @@ -1430,7 +1431,7 @@ and will be removed in Python 3.14. * The ``__package__`` and ``__cached__`` attributes on module objects. -* The ``co_lnotab`` attribute of code objects. +* The :attr:`~codeobject.co_lnotab` attribute of code objects. Pending Removal in Python 3.15 ------------------------------ diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index c9facad6375ef3..f5723e050a2762 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -572,7 +572,8 @@ Pending Removal in Python 3.14 * date and datetime adapter, date and timestamp converter: see the :mod:`sqlite3` documentation for suggested replacement recipes. -* :class:`types.CodeType`: Accessing ``co_lnotab`` was deprecated in :pep:`626` +* :class:`types.CodeType`: Accessing :attr:`~codeobject.co_lnotab` was + deprecated in :pep:`626` since 3.10 and was planned to be removed in 3.12, but it only got a proper :exc:`DeprecationWarning` in 3.12. May be removed in 3.14. @@ -735,7 +736,7 @@ although there is currently no date scheduled for their removal. * :mod:`!sre_compile`, :mod:`!sre_constants` and :mod:`!sre_parse` modules. -* ``types.CodeType.co_lnotab``: use the ``co_lines`` attribute instead. +* :attr:`~codeobject.co_lnotab`: use the ``co_lines`` attribute instead. * :class:`typing.Text` (:gh:`92332`). diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst index 2f618929793fc6..5a3cea0ec87cb2 100644 --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -2160,14 +2160,15 @@ Changes in the Python API * :c:func:`PyErr_SetImportError` now sets :exc:`TypeError` when its **msg** argument is not set. Previously only ``NULL`` was returned. -* The format of the ``co_lnotab`` attribute of code objects changed to support +* The format of the :attr:`~codeobject.co_lnotab` attribute of code objects + changed to support a negative line number delta. By default, Python does not emit bytecode with a negative line number delta. Functions using :attr:`frame.f_lineno`, ``PyFrame_GetLineNumber()`` or ``PyCode_Addr2Line()`` are not affected. - Functions directly decoding ``co_lnotab`` should be updated to use a signed + Functions directly decoding :attr:`!co_lnotab` should be updated to use a signed 8-bit integer type for the line number delta, but this is only required to support applications using a negative line number delta. See - ``Objects/lnotab_notes.txt`` for the ``co_lnotab`` format and how to decode + ``Objects/lnotab_notes.txt`` for the :attr:`!co_lnotab` format and how to decode it, and see the :pep:`511` for the rationale. * The functions in the :mod:`compileall` module now return booleans instead From 3870d19d151c31d77b737d6a480aa946b4e87af6 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 6 Dec 2023 20:16:12 +0000 Subject: [PATCH 200/228] gh-101100: Fix Sphinx nitpicks in `library/reprlib.rst` (#112811) --- Doc/library/reprlib.rst | 52 ++++++++++++++++++++++++----------------- Doc/tools/.nitignore | 1 - 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/Doc/library/reprlib.rst b/Doc/library/reprlib.rst index 5ebb0a7780c37b..678a11c6f45490 100644 --- a/Doc/library/reprlib.rst +++ b/Doc/library/reprlib.rst @@ -10,7 +10,7 @@ -------------- -The :mod:`reprlib` module provides a means for producing object representations +The :mod:`!reprlib` module provides a means for producing object representations with limits on the size of the resulting strings. This is used in the Python debugger and may be useful in other contexts as well. @@ -58,29 +58,31 @@ This module provides a class, an instance, and a function: limits on most sizes. In addition to size-limiting tools, the module also provides a decorator for -detecting recursive calls to :meth:`__repr__` and substituting a placeholder -string instead. +detecting recursive calls to :meth:`~object.__repr__` and substituting a +placeholder string instead. .. index:: single: ...; placeholder .. decorator:: recursive_repr(fillvalue="...") - Decorator for :meth:`__repr__` methods to detect recursive calls within the + Decorator for :meth:`~object.__repr__` methods to detect recursive calls within the same thread. If a recursive call is made, the *fillvalue* is returned, - otherwise, the usual :meth:`__repr__` call is made. For example: - - >>> from reprlib import recursive_repr - >>> class MyList(list): - ... @recursive_repr() - ... def __repr__(self): - ... return '<' + '|'.join(map(repr, self)) + '>' - ... - >>> m = MyList('abc') - >>> m.append(m) - >>> m.append('x') - >>> print(m) - <'a'|'b'|'c'|...|'x'> + otherwise, the usual :meth:`!__repr__` call is made. For example: + + .. doctest:: + + >>> from reprlib import recursive_repr + >>> class MyList(list): + ... @recursive_repr() + ... def __repr__(self): + ... return '<' + '|'.join(map(repr, self)) + '>' + ... + >>> m = MyList('abc') + >>> m.append(m) + >>> m.append('x') + >>> print(m) + <'a'|'b'|'c'|...|'x'> .. versionadded:: 3.2 @@ -148,10 +150,10 @@ which format specific object types. with no line breaks or indentation, like the standard :func:`repr`. For example: - .. code-block:: pycon + .. doctest:: indent >>> example = [ - 1, 'spam', {'a': 2, 'b': 'spam eggs', 'c': {3: 4.5, 6: []}}, 'ham'] + ... 1, 'spam', {'a': 2, 'b': 'spam eggs', 'c': {3: 4.5, 6: []}}, 'ham'] >>> import reprlib >>> aRepr = reprlib.Repr() >>> print(aRepr.repr(example)) @@ -160,7 +162,7 @@ which format specific object types. If :attr:`~Repr.indent` is set to a string, each recursion level is placed on its own line, indented by that string: - .. code-block:: pycon + .. doctest:: indent >>> aRepr.indent = '-->' >>> print(aRepr.repr(example)) @@ -181,7 +183,7 @@ which format specific object types. Setting :attr:`~Repr.indent` to a positive integer value behaves as if it was set to a string with that number of spaces: - .. code-block:: pycon + .. doctest:: indent >>> aRepr.indent = 4 >>> print(aRepr.repr(example)) @@ -234,7 +236,9 @@ Subclassing Repr Objects The use of dynamic dispatching by :meth:`Repr.repr1` allows subclasses of :class:`Repr` to add support for additional built-in object types or to modify the handling of types already supported. This example shows how special support -for file objects could be added:: +for file objects could be added: + +.. testcode:: import reprlib import sys @@ -248,3 +252,7 @@ for file objects could be added:: aRepr = MyRepr() print(aRepr.repr(sys.stdin)) # prints '' + +.. testoutput:: + + diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 50f04d72c0dee0..ada1fc5fafc9c9 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -84,7 +84,6 @@ Doc/library/pydoc.rst Doc/library/pyexpat.rst Doc/library/random.rst Doc/library/readline.rst -Doc/library/reprlib.rst Doc/library/resource.rst Doc/library/rlcompleter.rst Doc/library/select.rst From 16448cab44e23d350824e9ac75e699f5bcc48a14 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Wed, 6 Dec 2023 22:29:54 +0000 Subject: [PATCH 201/228] gh-112730: Use color to highlight error locations (gh-112732) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pablo Galindo Co-authored-by: Łukasz Langa --- Doc/using/cmdline.rst | 27 +++ Doc/whatsnew/3.13.rst | 6 + Lib/test/test_traceback.py | 126 ++++++++++- Lib/traceback.py | 197 ++++++++++++++---- ...-12-04-23-09-07.gh-issue-112730.BXHlFa.rst | 1 + Modules/clinic/posixmodule.c.h | 28 ++- Modules/posixmodule.c | 22 ++ Python/initconfig.c | 2 + 8 files changed, 369 insertions(+), 40 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-12-04-23-09-07.gh-issue-112730.BXHlFa.rst diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index 39c8d114f1e2c5..56235bf4c28c7c 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -612,6 +612,27 @@ Miscellaneous options .. versionadded:: 3.13 The ``-X presite`` option. +Controlling Color +~~~~~~~~~~~~~~~~~ + +The Python interpreter is configured by default to use colors to highlight +output in certain situations such as when displaying tracebacks. This +behavior can be controlled by setting different environment variables. + +Setting the environment variable ``TERM`` to ``dumb`` will disable color. + +If the environment variable ``FORCE_COLOR`` is set, then color will be +enabled regardless of the value of TERM. This is useful on CI systems which +aren’t terminals but can none-the-less display ANSI escape sequences. + +If the environment variable ``NO_COLOR`` is set, Python will disable all color +in the output. This takes precedence over ``FORCE_COLOR``. + +All these environment variables are used also by other tools to control color +output. To control the color output only in the Python interpreter, the +:envvar:`PYTHON_COLORS` environment variable can be used. This variable takes +precedence over ``NO_COLOR``, which in turn takes precedence over +``FORCE_COLOR``. Options you shouldn't use ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1110,6 +1131,12 @@ conflict. .. versionadded:: 3.13 +.. envvar:: PYTHON_COLORS + + If this variable is set to ``1``, the interpreter will colorize various kinds + of output. Setting it to ``0`` deactivates this behavior. + + .. versionadded:: 3.13 Debug-mode variables ~~~~~~~~~~~~~~~~~~~~ diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index f5723e050a2762..9adf7a3893bd70 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -85,7 +85,13 @@ Important deprecations, removals or restrictions: New Features ============ +Improved Error Messages +----------------------- +* The interpreter now colorizes error messages when displaying tracebacks by default. + This feature can be controlled via the new :envvar:`PYTHON_COLORS` environment + variable as well as the canonical ``NO_COLOR`` and ``FORCE_COLOR`` environment + variables. (Contributed by Pablo Galindo Salgado in :gh:`112730`.) Other Language Changes ====================== diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index b60e06ff37f494..a6708119b81191 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -8,6 +8,7 @@ import inspect import builtins import unittest +import unittest.mock import re import tempfile import random @@ -24,6 +25,7 @@ import json import textwrap import traceback +import contextlib from functools import partial from pathlib import Path @@ -41,6 +43,14 @@ class TracebackCases(unittest.TestCase): # For now, a very minimal set of tests. I want to be sure that # formatting of SyntaxErrors works based on changes for 2.1. + def setUp(self): + super().setUp() + self.colorize = traceback._COLORIZE + traceback._COLORIZE = False + + def tearDown(self): + super().tearDown() + traceback._COLORIZE = self.colorize def get_exception_format(self, func, exc): try: @@ -521,7 +531,7 @@ def test_signatures(self): self.assertEqual( str(inspect.signature(traceback.print_exception)), ('(exc, /, value=, tb=, ' - 'limit=None, file=None, chain=True)')) + 'limit=None, file=None, chain=True, **kwargs)')) self.assertEqual( str(inspect.signature(traceback.format_exception)), @@ -3031,7 +3041,7 @@ def some_inner(k, v): def test_custom_format_frame(self): class CustomStackSummary(traceback.StackSummary): - def format_frame_summary(self, frame_summary): + def format_frame_summary(self, frame_summary, colorize=False): return f'{frame_summary.filename}:{frame_summary.lineno}' def some_inner(): @@ -3056,7 +3066,7 @@ def g(): tb = g() class Skip_G(traceback.StackSummary): - def format_frame_summary(self, frame_summary): + def format_frame_summary(self, frame_summary, colorize=False): if frame_summary.name == 'g': return None return super().format_frame_summary(frame_summary) @@ -3076,7 +3086,6 @@ def __repr__(self) -> str: raise Exception("Unrepresentable") class TestTracebackException(unittest.TestCase): - def do_test_smoke(self, exc, expected_type_str): try: raise exc @@ -4245,6 +4254,115 @@ def test_levenshtein_distance_short_circuit(self): res3 = traceback._levenshtein_distance(a, b, threshold) self.assertGreater(res3, threshold, msg=(a, b, threshold)) +class TestColorizedTraceback(unittest.TestCase): + def test_colorized_traceback(self): + def foo(*args): + x = {'a':{'b': None}} + y = x['a']['b']['c'] + + def baz(*args): + return foo(1,2,3,4) + + def bar(): + return baz(1, + 2,3 + ,4) + try: + bar() + except Exception as e: + exc = traceback.TracebackException.from_exception( + e, capture_locals=True + ) + lines = "".join(exc.format(colorize=True)) + red = traceback._ANSIColors.RED + boldr = traceback._ANSIColors.BOLD_RED + reset = traceback._ANSIColors.RESET + self.assertIn("y = " + red + "x['a']['b']" + reset + boldr + "['c']" + reset, lines) + self.assertIn("return " + red + "foo" + reset + boldr + "(1,2,3,4)" + reset, lines) + self.assertIn("return " + red + "baz" + reset + boldr + "(1," + reset, lines) + self.assertIn(boldr + "2,3" + reset, lines) + self.assertIn(boldr + ",4)" + reset, lines) + self.assertIn(red + "bar" + reset + boldr + "()" + reset, lines) + + def test_colorized_syntax_error(self): + try: + compile("a $ b", "", "exec") + except SyntaxError as e: + exc = traceback.TracebackException.from_exception( + e, capture_locals=True + ) + actual = "".join(exc.format(colorize=True)) + red = traceback._ANSIColors.RED + magenta = traceback._ANSIColors.MAGENTA + boldm = traceback._ANSIColors.BOLD_MAGENTA + boldr = traceback._ANSIColors.BOLD_RED + reset = traceback._ANSIColors.RESET + expected = "".join([ + f' File {magenta}""{reset}, line {magenta}1{reset}\n', + f' a {boldr}${reset} b\n', + f' {boldr}^{reset}\n', + f'{boldm}SyntaxError{reset}: {magenta}invalid syntax{reset}\n'] + ) + self.assertIn(expected, actual) + + def test_colorized_traceback_is_the_default(self): + def foo(): + 1/0 + + from _testcapi import exception_print + try: + foo() + self.fail("No exception thrown.") + except Exception as e: + with captured_output("stderr") as tbstderr: + with unittest.mock.patch('traceback._can_colorize', return_value=True): + exception_print(e) + actual = tbstderr.getvalue().splitlines() + + red = traceback._ANSIColors.RED + boldr = traceback._ANSIColors.BOLD_RED + magenta = traceback._ANSIColors.MAGENTA + boldm = traceback._ANSIColors.BOLD_MAGENTA + reset = traceback._ANSIColors.RESET + lno_foo = foo.__code__.co_firstlineno + expected = ['Traceback (most recent call last):', + f' File {magenta}"{__file__}"{reset}, ' + f'line {magenta}{lno_foo+5}{reset}, in {magenta}test_colorized_traceback_is_the_default{reset}', + f' {red}foo{reset+boldr}(){reset}', + f' {red}~~~{reset+boldr}^^{reset}', + f' File {magenta}"{__file__}"{reset}, ' + f'line {magenta}{lno_foo+1}{reset}, in {magenta}foo{reset}', + f' {red}1{reset+boldr}/{reset+red}0{reset}', + f' {red}~{reset+boldr}^{reset+red}~{reset}', + f'{boldm}ZeroDivisionError{reset}: {magenta}division by zero{reset}'] + self.assertEqual(actual, expected) + + def test_colorized_detection_checks_for_environment_variables(self): + if sys.platform == "win32": + virtual_patching = unittest.mock.patch("nt._supports_virtual_terminal", return_value=True) + else: + virtual_patching = contextlib.nullcontext() + with virtual_patching: + with unittest.mock.patch("os.isatty") as isatty_mock: + isatty_mock.return_value = True + with unittest.mock.patch("os.environ", {'TERM': 'dumb'}): + self.assertEqual(traceback._can_colorize(), False) + with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '1'}): + self.assertEqual(traceback._can_colorize(), True) + with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '0'}): + self.assertEqual(traceback._can_colorize(), False) + with unittest.mock.patch("os.environ", {'NO_COLOR': '1'}): + self.assertEqual(traceback._can_colorize(), False) + with unittest.mock.patch("os.environ", {'NO_COLOR': '1', "PYTHON_COLORS": '1'}): + self.assertEqual(traceback._can_colorize(), True) + with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1'}): + self.assertEqual(traceback._can_colorize(), True) + with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1', 'NO_COLOR': '1'}): + self.assertEqual(traceback._can_colorize(), False) + with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1', "PYTHON_COLORS": '0'}): + self.assertEqual(traceback._can_colorize(), False) + isatty_mock.return_value = False + self.assertEqual(traceback._can_colorize(), False) if __name__ == "__main__": unittest.main() diff --git a/Lib/traceback.py b/Lib/traceback.py index a0485a7023d07d..1cf008c7e9da97 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -1,5 +1,7 @@ """Extract, format and print information about Python stack traces.""" +import os +import io import collections.abc import itertools import linecache @@ -19,6 +21,8 @@ # Formatting and printing lists of traceback lines. # +_COLORIZE = True + def print_list(extracted_list, file=None): """Print the list of tuples as returned by extract_tb() or extract_stack() as a formatted stack trace to the given file.""" @@ -110,7 +114,7 @@ def _parse_value_tb(exc, value, tb): def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \ - file=None, chain=True): + file=None, chain=True, **kwargs): """Print exception up to 'limit' stack trace entries from 'tb' to 'file'. This differs from print_tb() in the following ways: (1) if @@ -121,17 +125,44 @@ def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \ occurred with a caret on the next line indicating the approximate position of the error. """ + colorize = kwargs.get("colorize", False) value, tb = _parse_value_tb(exc, value, tb) te = TracebackException(type(value), value, tb, limit=limit, compact=True) - te.print(file=file, chain=chain) + te.print(file=file, chain=chain, colorize=colorize) BUILTIN_EXCEPTION_LIMIT = object() +def _can_colorize(): + if sys.platform == "win32": + try: + import nt + if not nt._supports_virtual_terminal(): + return False + except (ImportError, AttributeError): + return False + + if os.environ.get("PYTHON_COLORS") == "0": + return False + if os.environ.get("PYTHON_COLORS") == "1": + return True + if "NO_COLOR" in os.environ: + return False + if not _COLORIZE: + return False + if "FORCE_COLOR" in os.environ: + return True + if os.environ.get("TERM") == "dumb": + return False + try: + return os.isatty(sys.stderr.fileno()) + except io.UnsupportedOperation: + return sys.stderr.isatty() def _print_exception_bltin(exc, /): file = sys.stderr if sys.stderr is not None else sys.__stderr__ - return print_exception(exc, limit=BUILTIN_EXCEPTION_LIMIT, file=file) + colorize = _can_colorize() + return print_exception(exc, limit=BUILTIN_EXCEPTION_LIMIT, file=file, colorize=colorize) def format_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \ @@ -172,13 +203,19 @@ def format_exception_only(exc, /, value=_sentinel, *, show_group=False): # -- not official API but folk probably use these two functions. -def _format_final_exc_line(etype, value, *, insert_final_newline=True): +def _format_final_exc_line(etype, value, *, insert_final_newline=True, colorize=False): valuestr = _safe_string(value, 'exception') end_char = "\n" if insert_final_newline else "" - if value is None or not valuestr: - line = f"{etype}{end_char}" + if colorize: + if value is None or not valuestr: + line = f"{_ANSIColors.BOLD_MAGENTA}{etype}{_ANSIColors.RESET}{end_char}" + else: + line = f"{_ANSIColors.BOLD_MAGENTA}{etype}{_ANSIColors.RESET}: {_ANSIColors.MAGENTA}{valuestr}{_ANSIColors.RESET}{end_char}" else: - line = f"{etype}: {valuestr}{end_char}" + if value is None or not valuestr: + line = f"{etype}{end_char}" + else: + line = f"{etype}: {valuestr}{end_char}" return line def _safe_string(value, what, func=str): @@ -406,6 +443,14 @@ def _get_code_position(code, instruction_index): _RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c. +class _ANSIColors: + RED = '\x1b[31m' + BOLD_RED = '\x1b[1;31m' + MAGENTA = '\x1b[35m' + BOLD_MAGENTA = '\x1b[1;35m' + GREY = '\x1b[90m' + RESET = '\x1b[0m' + class StackSummary(list): """A list of FrameSummary objects, representing a stack of frames.""" @@ -496,18 +541,33 @@ def from_list(klass, a_list): result.append(FrameSummary(filename, lineno, name, line=line)) return result - def format_frame_summary(self, frame_summary): + def format_frame_summary(self, frame_summary, **kwargs): """Format the lines for a single FrameSummary. Returns a string representing one frame involved in the stack. This gets called for every frame to be printed in the stack summary. """ + colorize = kwargs.get("colorize", False) row = [] filename = frame_summary.filename if frame_summary.filename.startswith("-"): filename = "" - row.append(' File "{}", line {}, in {}\n'.format( - filename, frame_summary.lineno, frame_summary.name)) + if colorize: + row.append(' File {}"{}"{}, line {}{}{}, in {}{}{}\n'.format( + _ANSIColors.MAGENTA, + filename, + _ANSIColors.RESET, + _ANSIColors.MAGENTA, + frame_summary.lineno, + _ANSIColors.RESET, + _ANSIColors.MAGENTA, + frame_summary.name, + _ANSIColors.RESET, + ) + ) + else: + row.append(' File "{}", line {}, in {}\n'.format( + filename, frame_summary.lineno, frame_summary.name)) if frame_summary._dedented_lines and frame_summary._dedented_lines.strip(): if ( frame_summary.colno is None or @@ -619,7 +679,31 @@ def output_line(lineno): carets.append(secondary_char) else: carets.append(primary_char) - result.append("".join(carets) + "\n") + if colorize: + # Replace the previous line with a red version of it only in the parts covered + # by the carets. + line = result[-1] + colorized_line_parts = [] + colorized_carets_parts = [] + + for color, group in itertools.groupby(itertools.zip_longest(line, carets, fillvalue=""), key=lambda x: x[1]): + caret_group = list(group) + if color == "^": + colorized_line_parts.append(_ANSIColors.BOLD_RED + "".join(char for char, _ in caret_group) + _ANSIColors.RESET) + colorized_carets_parts.append(_ANSIColors.BOLD_RED + "".join(caret for _, caret in caret_group) + _ANSIColors.RESET) + elif color == "~": + colorized_line_parts.append(_ANSIColors.RED + "".join(char for char, _ in caret_group) + _ANSIColors.RESET) + colorized_carets_parts.append(_ANSIColors.RED + "".join(caret for _, caret in caret_group) + _ANSIColors.RESET) + else: + colorized_line_parts.append("".join(char for char, _ in caret_group)) + colorized_carets_parts.append("".join(caret for _, caret in caret_group)) + + colorized_line = "".join(colorized_line_parts) + colorized_carets = "".join(colorized_carets_parts) + result[-1] = colorized_line + result.append(colorized_carets + "\n") + else: + result.append("".join(carets) + "\n") # display significant lines sig_lines_list = sorted(significant_lines) @@ -643,7 +727,7 @@ def output_line(lineno): return ''.join(row) - def format(self): + def format(self, **kwargs): """Format the stack ready for printing. Returns a list of strings ready for printing. Each string in the @@ -655,13 +739,14 @@ def format(self): repetitions are shown, followed by a summary line stating the exact number of further repetitions. """ + colorize = kwargs.get("colorize", False) result = [] last_file = None last_line = None last_name = None count = 0 for frame_summary in self: - formatted_frame = self.format_frame_summary(frame_summary) + formatted_frame = self.format_frame_summary(frame_summary, colorize=colorize) if formatted_frame is None: continue if (last_file is None or last_file != frame_summary.filename or @@ -1118,7 +1203,7 @@ def __eq__(self, other): def __str__(self): return self._str - def format_exception_only(self, *, show_group=False, _depth=0): + def format_exception_only(self, *, show_group=False, _depth=0, **kwargs): """Format the exception part of the traceback. The return value is a generator of strings, each ending in a newline. @@ -1135,10 +1220,11 @@ def format_exception_only(self, *, show_group=False, _depth=0): :exc:`BaseExceptionGroup`, the nested exceptions are included as well, recursively, with indentation relative to their nesting depth. """ + colorize = kwargs.get("colorize", False) indent = 3 * _depth * ' ' if not self._have_exc_type: - yield indent + _format_final_exc_line(None, self._str) + yield indent + _format_final_exc_line(None, self._str, colorize=colorize) return stype = self.exc_type_str @@ -1146,16 +1232,16 @@ def format_exception_only(self, *, show_group=False, _depth=0): if _depth > 0: # Nested exceptions needs correct handling of multiline messages. formatted = _format_final_exc_line( - stype, self._str, insert_final_newline=False, + stype, self._str, insert_final_newline=False, colorize=colorize ).split('\n') yield from [ indent + l + '\n' for l in formatted ] else: - yield _format_final_exc_line(stype, self._str) + yield _format_final_exc_line(stype, self._str, colorize=colorize) else: - yield from [indent + l for l in self._format_syntax_error(stype)] + yield from [indent + l for l in self._format_syntax_error(stype, colorize=colorize)] if ( isinstance(self.__notes__, collections.abc.Sequence) @@ -1169,15 +1255,26 @@ def format_exception_only(self, *, show_group=False, _depth=0): if self.exceptions and show_group: for ex in self.exceptions: - yield from ex.format_exception_only(show_group=show_group, _depth=_depth+1) + yield from ex.format_exception_only(show_group=show_group, _depth=_depth+1, colorize=colorize) - def _format_syntax_error(self, stype): + def _format_syntax_error(self, stype, **kwargs): """Format SyntaxError exceptions (internal helper).""" # Show exactly where the problem was found. + colorize = kwargs.get("colorize", False) filename_suffix = '' if self.lineno is not None: - yield ' File "{}", line {}\n'.format( - self.filename or "", self.lineno) + if colorize: + yield ' File {}"{}"{}, line {}{}{}\n'.format( + _ANSIColors.MAGENTA, + self.filename or "", + _ANSIColors.RESET, + _ANSIColors.MAGENTA, + self.lineno, + _ANSIColors.RESET, + ) + else: + yield ' File "{}", line {}\n'.format( + self.filename or "", self.lineno) elif self.filename is not None: filename_suffix = ' ({})'.format(self.filename) @@ -1189,9 +1286,9 @@ def _format_syntax_error(self, stype): rtext = text.rstrip('\n') ltext = rtext.lstrip(' \n\f') spaces = len(rtext) - len(ltext) - yield ' {}\n'.format(ltext) - - if self.offset is not None: + if self.offset is None: + yield ' {}\n'.format(ltext) + else: offset = self.offset end_offset = self.end_offset if self.end_offset not in {None, 0} else offset if self.text and offset > len(self.text): @@ -1204,14 +1301,43 @@ def _format_syntax_error(self, stype): # Convert 1-based column offset to 0-based index into stripped text colno = offset - 1 - spaces end_colno = end_offset - 1 - spaces + caretspace = ' ' if colno >= 0: # non-space whitespace (likes tabs) must be kept for alignment caretspace = ((c if c.isspace() else ' ') for c in ltext[:colno]) - yield ' {}{}'.format("".join(caretspace), ('^' * (end_colno - colno) + "\n")) + start_color = end_color = "" + if colorize: + # colorize from colno to end_colno + ltext = ( + ltext[:colno] + + _ANSIColors.BOLD_RED + ltext[colno:end_colno] + _ANSIColors.RESET + + ltext[end_colno:] + ) + start_color = _ANSIColors.BOLD_RED + end_color = _ANSIColors.RESET + yield ' {}\n'.format(ltext) + yield ' {}{}{}{}\n'.format( + "".join(caretspace), + start_color, + ('^' * (end_colno - colno)), + end_color, + ) + else: + yield ' {}\n'.format(ltext) msg = self.msg or "" - yield "{}: {}{}\n".format(stype, msg, filename_suffix) + if colorize: + yield "{}{}{}: {}{}{}{}\n".format( + _ANSIColors.BOLD_MAGENTA, + stype, + _ANSIColors.RESET, + _ANSIColors.MAGENTA, + msg, + _ANSIColors.RESET, + filename_suffix) + else: + yield "{}: {}{}\n".format(stype, msg, filename_suffix) - def format(self, *, chain=True, _ctx=None): + def format(self, *, chain=True, _ctx=None, **kwargs): """Format the exception. If chain is not *True*, *__cause__* and *__context__* will not be formatted. @@ -1223,7 +1349,7 @@ def format(self, *, chain=True, _ctx=None): The message indicating which exception occurred is always the last string in the output. """ - + colorize = kwargs.get("colorize", False) if _ctx is None: _ctx = _ExceptionPrintContext() @@ -1253,8 +1379,8 @@ def format(self, *, chain=True, _ctx=None): if exc.exceptions is None: if exc.stack: yield from _ctx.emit('Traceback (most recent call last):\n') - yield from _ctx.emit(exc.stack.format()) - yield from _ctx.emit(exc.format_exception_only()) + yield from _ctx.emit(exc.stack.format(colorize=colorize)) + yield from _ctx.emit(exc.format_exception_only(colorize=colorize)) elif _ctx.exception_group_depth > self.max_group_depth: # exception group, but depth exceeds limit yield from _ctx.emit( @@ -1269,9 +1395,9 @@ def format(self, *, chain=True, _ctx=None): yield from _ctx.emit( 'Exception Group Traceback (most recent call last):\n', margin_char = '+' if is_toplevel else None) - yield from _ctx.emit(exc.stack.format()) + yield from _ctx.emit(exc.stack.format(colorize=colorize)) - yield from _ctx.emit(exc.format_exception_only()) + yield from _ctx.emit(exc.format_exception_only(colorize=colorize)) num_excs = len(exc.exceptions) if num_excs <= self.max_group_width: n = num_excs @@ -1312,11 +1438,12 @@ def format(self, *, chain=True, _ctx=None): _ctx.exception_group_depth = 0 - def print(self, *, file=None, chain=True): + def print(self, *, file=None, chain=True, **kwargs): """Print the result of self.format(chain=chain) to 'file'.""" + colorize = kwargs.get("colorize", False) if file is None: file = sys.stderr - for line in self.format(chain=chain): + for line in self.format(chain=chain, colorize=colorize): print(line, file=file, end="") diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-04-23-09-07.gh-issue-112730.BXHlFa.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-04-23-09-07.gh-issue-112730.BXHlFa.rst new file mode 100644 index 00000000000000..51758dd5f4c318 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-04-23-09-07.gh-issue-112730.BXHlFa.rst @@ -0,0 +1 @@ +Use color to highlight error locations in tracebacks. Patch by Pablo Galindo diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index a6c76370f241be..9d6cd337f4a2f4 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -11756,6 +11756,28 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #endif /* (defined(WIFEXITED) || defined(MS_WINDOWS)) */ +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(os__supports_virtual_terminal__doc__, +"_supports_virtual_terminal($module, /)\n" +"--\n" +"\n" +"Checks if virtual terminal is supported in windows"); + +#define OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF \ + {"_supports_virtual_terminal", (PyCFunction)os__supports_virtual_terminal, METH_NOARGS, os__supports_virtual_terminal__doc__}, + +static PyObject * +os__supports_virtual_terminal_impl(PyObject *module); + +static PyObject * +os__supports_virtual_terminal(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return os__supports_virtual_terminal_impl(module); +} + +#endif /* defined(MS_WINDOWS) */ + #ifndef OS_TTYNAME_METHODDEF #define OS_TTYNAME_METHODDEF #endif /* !defined(OS_TTYNAME_METHODDEF) */ @@ -12395,4 +12417,8 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF #define OS_WAITSTATUS_TO_EXITCODE_METHODDEF #endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */ -/*[clinic end generated code: output=2900675ac5219924 input=a9049054013a1b77]*/ + +#ifndef OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF + #define OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF +#endif /* !defined(OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF) */ +/*[clinic end generated code: output=ff0ec3371de19904 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 70d107a297f315..ddbb4cd43babfc 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -16073,6 +16073,26 @@ os_waitstatus_to_exitcode_impl(PyObject *module, PyObject *status_obj) } #endif +#if defined(MS_WINDOWS) +/*[clinic input] +os._supports_virtual_terminal + +Checks if virtual terminal is supported in windows +[clinic start generated code]*/ + +static PyObject * +os__supports_virtual_terminal_impl(PyObject *module) +/*[clinic end generated code: output=bd0556a6d9d99fe6 input=0752c98e5d321542]*/ +{ + DWORD mode = 0; + HANDLE handle = GetStdHandle(STD_ERROR_HANDLE); + if (!GetConsoleMode(handle, &mode)) { + Py_RETURN_FALSE; + } + return PyBool_FromLong(mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING); +} +#endif + static PyMethodDef posix_methods[] = { @@ -16277,6 +16297,8 @@ static PyMethodDef posix_methods[] = { OS__PATH_ISFILE_METHODDEF OS__PATH_ISLINK_METHODDEF OS__PATH_EXISTS_METHODDEF + + OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF {NULL, NULL} /* Sentinel */ }; diff --git a/Python/initconfig.c b/Python/initconfig.c index d7f3195ed5fcf0..06e317907b8ec9 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -293,6 +293,8 @@ static const char usage_envvars[] = "PYTHON_FROZEN_MODULES : if this variable is set, it determines whether or not \n" " frozen modules should be used. The default is \"on\" (or \"off\" if you are \n" " running a local build).\n" +"PYTHON_COLORS : If this variable is set to 1, the interpreter will" +" colorize various kinds of output. Setting it to 0 deactivates this behavior.\n" "These variables have equivalent command-line parameters (see --help for details):\n" "PYTHONDEBUG : enable parser debug mode (-d)\n" "PYTHONDONTWRITEBYTECODE : don't write .pyc files (-B)\n" From 953ee622b3901d3467e65e3484dcfa75ba6fcddf Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 7 Dec 2023 10:30:15 +0100 Subject: [PATCH 202/228] gh-109981: Fix support.fd_count() on macOS 14 (#112797) Use scanning "/dev/fd/" on macOS in support.fd_count(). That's both more efficient than scanning all possible file descriptors, and avoids crashing the interpreter when there are open "guarded" file descriptors. "Guarded" file descriptors are a macOS feature where file descriptors used by system libraries are marked and cause hard crashes when used by "user" code. Co-authored-by: Victor Stinner --- Lib/test/support/os_helper.py | 11 +++++++++-- .../2023-12-06-12-11-13.gh-issue-109981.mOHg10.rst | 3 +++ 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/macOS/2023-12-06-12-11-13.gh-issue-109981.mOHg10.rst diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py index 46ae53aa11a91f..7a67d87fb9e846 100644 --- a/Lib/test/support/os_helper.py +++ b/Lib/test/support/os_helper.py @@ -592,10 +592,17 @@ def fd_count(): """Count the number of open file descriptors. """ if sys.platform.startswith(('linux', 'freebsd', 'emscripten')): + fd_path = "/proc/self/fd" + elif sys.platform == "darwin": + fd_path = "/dev/fd" + else: + fd_path = None + + if fd_path is not None: try: - names = os.listdir("/proc/self/fd") + names = os.listdir(fd_path) # Subtract one because listdir() internally opens a file - # descriptor to list the content of the /proc/self/fd/ directory. + # descriptor to list the content of the directory. return len(names) - 1 except FileNotFoundError: pass diff --git a/Misc/NEWS.d/next/macOS/2023-12-06-12-11-13.gh-issue-109981.mOHg10.rst b/Misc/NEWS.d/next/macOS/2023-12-06-12-11-13.gh-issue-109981.mOHg10.rst new file mode 100644 index 00000000000000..f86ab2c37ee6ec --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-12-06-12-11-13.gh-issue-109981.mOHg10.rst @@ -0,0 +1,3 @@ +Use ``/dev/fd`` on macOS to determine the number of open files in +``test.support.os_helper.fd_count`` to avoid a crash with "guarded" file +descriptors when probing for open files. From 8660fb7fd7cdcbfe58ef304f5720efe97ca7c842 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 7 Dec 2023 12:19:43 +0200 Subject: [PATCH 203/228] gh-112660: Do not clear arbitrary errors on import (GH-112661) Previously arbitrary errors could be cleared during formatting error messages for ImportError or AttributeError for modules. Now all unexpected errors are reported. --- ...-12-03-15-29-53.gh-issue-112660.gldBvh.rst | 2 + Objects/moduleobject.c | 57 ++++++++----------- Python/ceval.c | 46 ++++++++++----- Python/import.c | 25 ++++---- 4 files changed, 72 insertions(+), 58 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-12-03-15-29-53.gh-issue-112660.gldBvh.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-03-15-29-53.gh-issue-112660.gldBvh.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-03-15-29-53.gh-issue-112660.gldBvh.rst new file mode 100644 index 00000000000000..ea9052b3e35c48 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-03-15-29-53.gh-issue-112660.gldBvh.rst @@ -0,0 +1,2 @@ +Do not clear unexpected errors during formatting error messages for +ImportError and AttributeError for modules. diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index bba77ce8ab7e7b..e2741fef6debd3 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -749,27 +749,20 @@ module_repr(PyModuleObject *m) } /* Check if the "_initializing" attribute of the module spec is set to true. - Clear the exception and return 0 if spec is NULL. */ int _PyModuleSpec_IsInitializing(PyObject *spec) { - if (spec != NULL) { - PyObject *value; - int ok = PyObject_GetOptionalAttr(spec, &_Py_ID(_initializing), &value); - if (ok == 0) { - return 0; - } - if (value != NULL) { - int initializing = PyObject_IsTrue(value); - Py_DECREF(value); - if (initializing >= 0) { - return initializing; - } - } + if (spec == NULL) { + return 0; } - PyErr_Clear(); - return 0; + PyObject *value; + int rc = PyObject_GetOptionalAttr(spec, &_Py_ID(_initializing), &value); + if (rc > 0) { + rc = PyObject_IsTrue(value); + Py_DECREF(value); + } + return rc; } /* Check if the submodule name is in the "_uninitialized_submodules" attribute @@ -782,17 +775,13 @@ _PyModuleSpec_IsUninitializedSubmodule(PyObject *spec, PyObject *name) return 0; } - PyObject *value = PyObject_GetAttr(spec, &_Py_ID(_uninitialized_submodules)); - if (value == NULL) { - return 0; + PyObject *value; + int rc = PyObject_GetOptionalAttr(spec, &_Py_ID(_uninitialized_submodules), &value); + if (rc > 0) { + rc = PySequence_Contains(value, name); + Py_DECREF(value); } - - int is_uninitialized = PySequence_Contains(value, name); - Py_DECREF(value); - if (is_uninitialized == -1) { - return 0; - } - return is_uninitialized; + return rc; } PyObject* @@ -840,23 +829,27 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress) return NULL; } if (suppress != 1) { - if (_PyModuleSpec_IsInitializing(spec)) { + int rc = _PyModuleSpec_IsInitializing(spec); + if (rc > 0) { PyErr_Format(PyExc_AttributeError, "partially initialized " "module '%U' has no attribute '%U' " "(most likely due to a circular import)", mod_name, name); } - else if (_PyModuleSpec_IsUninitializedSubmodule(spec, name)) { - PyErr_Format(PyExc_AttributeError, + else if (rc == 0) { + rc = _PyModuleSpec_IsUninitializedSubmodule(spec, name); + if (rc > 0) { + PyErr_Format(PyExc_AttributeError, "cannot access submodule '%U' of module '%U' " "(most likely due to a circular import)", name, mod_name); - } - else { - PyErr_Format(PyExc_AttributeError, + } + else if (rc == 0) { + PyErr_Format(PyExc_AttributeError, "module '%U' has no attribute '%U'", mod_name, name); + } } } Py_XDECREF(spec); diff --git a/Python/ceval.c b/Python/ceval.c index 1806ceb7fa9681..f8fa50eb46c75e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2614,11 +2614,10 @@ import_from(PyThreadState *tstate, PyObject *v, PyObject *name) /* Issue #17636: in case this failed because of a circular relative import, try to fallback on reading the module directly from sys.modules. */ - pkgname = PyObject_GetAttr(v, &_Py_ID(__name__)); - if (pkgname == NULL) { - goto error; + if (PyObject_GetOptionalAttr(v, &_Py_ID(__name__), &pkgname) < 0) { + return NULL; } - if (!PyUnicode_Check(pkgname)) { + if (pkgname == NULL || !PyUnicode_Check(pkgname)) { Py_CLEAR(pkgname); goto error; } @@ -2635,42 +2634,59 @@ import_from(PyThreadState *tstate, PyObject *v, PyObject *name) Py_DECREF(pkgname); return x; error: - pkgpath = PyModule_GetFilenameObject(v); if (pkgname == NULL) { pkgname_or_unknown = PyUnicode_FromString(""); if (pkgname_or_unknown == NULL) { - Py_XDECREF(pkgpath); return NULL; } } else { pkgname_or_unknown = pkgname; } + pkgpath = NULL; + if (PyModule_Check(v)) { + pkgpath = PyModule_GetFilenameObject(v); + if (pkgpath == NULL) { + if (!PyErr_ExceptionMatches(PyExc_SystemError)) { + Py_DECREF(pkgname_or_unknown); + return NULL; + } + // module filename missing + _PyErr_Clear(tstate); + } + } if (pkgpath == NULL || !PyUnicode_Check(pkgpath)) { - _PyErr_Clear(tstate); + Py_CLEAR(pkgpath); errmsg = PyUnicode_FromFormat( "cannot import name %R from %R (unknown location)", name, pkgname_or_unknown ); - /* NULL checks for errmsg and pkgname done by PyErr_SetImportError. */ - _PyErr_SetImportErrorWithNameFrom(errmsg, pkgname, NULL, name); } else { - PyObject *spec = PyObject_GetAttr(v, &_Py_ID(__spec__)); + PyObject *spec; + int rc = PyObject_GetOptionalAttr(v, &_Py_ID(__spec__), &spec); + if (rc > 0) { + rc = _PyModuleSpec_IsInitializing(spec); + Py_DECREF(spec); + } + if (rc < 0) { + Py_DECREF(pkgname_or_unknown); + Py_DECREF(pkgpath); + return NULL; + } const char *fmt = - _PyModuleSpec_IsInitializing(spec) ? + rc ? "cannot import name %R from partially initialized module %R " "(most likely due to a circular import) (%S)" : "cannot import name %R from %R (%S)"; - Py_XDECREF(spec); errmsg = PyUnicode_FromFormat(fmt, name, pkgname_or_unknown, pkgpath); - /* NULL checks for errmsg and pkgname done by PyErr_SetImportError. */ - _PyErr_SetImportErrorWithNameFrom(errmsg, pkgname, pkgpath, name); } + /* NULL checks for errmsg and pkgname done by PyErr_SetImportError. */ + _PyErr_SetImportErrorWithNameFrom(errmsg, pkgname, pkgpath, name); Py_XDECREF(errmsg); - Py_XDECREF(pkgname_or_unknown); + Py_DECREF(pkgname_or_unknown); Py_XDECREF(pkgpath); return NULL; } diff --git a/Python/import.c b/Python/import.c index f37393bbdc4269..ef81f46a4d65c1 100644 --- a/Python/import.c +++ b/Python/import.c @@ -252,18 +252,21 @@ import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *n NOTE: because of this, initializing must be set *before* stuffing the new module in sys.modules. */ - spec = PyObject_GetAttr(mod, &_Py_ID(__spec__)); - int busy = _PyModuleSpec_IsInitializing(spec); - Py_XDECREF(spec); - if (busy) { - /* Wait until module is done importing. */ - PyObject *value = PyObject_CallMethodOneArg( - IMPORTLIB(interp), &_Py_ID(_lock_unlock_module), name); - if (value == NULL) { - return -1; - } - Py_DECREF(value); + int rc = PyObject_GetOptionalAttr(mod, &_Py_ID(__spec__), &spec); + if (rc > 0) { + rc = _PyModuleSpec_IsInitializing(spec); + Py_DECREF(spec); + } + if (rc <= 0) { + return rc; } + /* Wait until module is done importing. */ + PyObject *value = PyObject_CallMethodOneArg( + IMPORTLIB(interp), &_Py_ID(_lock_unlock_module), name); + if (value == NULL) { + return -1; + } + Py_DECREF(value); return 0; } From 4ba15de19153bb97308996ec85242bdeda358387 Mon Sep 17 00:00:00 2001 From: Kushal Das Date: Thu, 7 Dec 2023 11:22:52 +0100 Subject: [PATCH 204/228] gh-74616: Raise ValueError in case of null character in input prompt (GH-1738) If the input prompt to the builtin input function on terminal has any null character, then raise ValueError instead of silently truncating it. Co-authored-by: Serhiy Storchaka --- Lib/test/test_builtin.py | 44 +++++++++++++++---- ...3-12-07-12-00-04.gh-issue-74616.kgTGVb.rst | 2 + Python/bltinmodule.c | 5 +++ 3 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-12-07-12-00-04.gh-issue-74616.kgTGVb.rst diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 535856adaea4d3..558715383c82ee 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -2269,7 +2269,10 @@ def _run_child(self, child, terminal_input): return lines - def check_input_tty(self, prompt, terminal_input, stdio_encoding=None): + def check_input_tty(self, prompt, terminal_input, stdio_encoding=None, *, + expected=None, + stdin_errors='surrogateescape', + stdout_errors='replace'): if not sys.stdin.isatty() or not sys.stdout.isatty(): self.skipTest("stdin and stdout must be ttys") def child(wpipe): @@ -2277,22 +2280,26 @@ def child(wpipe): if stdio_encoding: sys.stdin = io.TextIOWrapper(sys.stdin.detach(), encoding=stdio_encoding, - errors='surrogateescape') + errors=stdin_errors) sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding=stdio_encoding, - errors='replace') + errors=stdout_errors) print("tty =", sys.stdin.isatty() and sys.stdout.isatty(), file=wpipe) - print(ascii(input(prompt)), file=wpipe) + try: + print(ascii(input(prompt)), file=wpipe) + except BaseException as e: + print(ascii(f'{e.__class__.__name__}: {e!s}'), file=wpipe) lines = self.run_child(child, terminal_input + b"\r\n") # Check we did exercise the GNU readline path self.assertIn(lines[0], {'tty = True', 'tty = False'}) if lines[0] != 'tty = True': self.skipTest("standard IO in should have been a tty") input_result = eval(lines[1]) # ascii() -> eval() roundtrip - if stdio_encoding: - expected = terminal_input.decode(stdio_encoding, 'surrogateescape') - else: - expected = terminal_input.decode(sys.stdin.encoding) # what else? + if expected is None: + if stdio_encoding: + expected = terminal_input.decode(stdio_encoding, 'surrogateescape') + else: + expected = terminal_input.decode(sys.stdin.encoding) # what else? self.assertEqual(input_result, expected) def test_input_tty(self): @@ -2313,13 +2320,32 @@ def skip_if_readline(self): def test_input_tty_non_ascii(self): self.skip_if_readline() # Check stdin/stdout encoding is used when invoking PyOS_Readline() - self.check_input_tty("prompté", b"quux\xe9", "utf-8") + self.check_input_tty("prompté", b"quux\xc3\xa9", "utf-8") def test_input_tty_non_ascii_unicode_errors(self): self.skip_if_readline() # Check stdin/stdout error handler is used when invoking PyOS_Readline() self.check_input_tty("prompté", b"quux\xe9", "ascii") + def test_input_tty_null_in_prompt(self): + self.check_input_tty("prompt\0", b"", + expected='ValueError: input: prompt string cannot contain ' + 'null characters') + + def test_input_tty_nonencodable_prompt(self): + self.skip_if_readline() + self.check_input_tty("prompté", b"quux", "ascii", stdout_errors='strict', + expected="UnicodeEncodeError: 'ascii' codec can't encode " + "character '\\xe9' in position 6: ordinal not in " + "range(128)") + + def test_input_tty_nondecodable_input(self): + self.skip_if_readline() + self.check_input_tty("prompt", b"quux\xe9", "ascii", stdin_errors='strict', + expected="UnicodeDecodeError: 'ascii' codec can't decode " + "byte 0xe9 in position 4: ordinal not in " + "range(128)") + def test_input_no_stdout_fileno(self): # Issue #24402: If stdin is the original terminal but stdout.fileno() # fails, do not use the original stdout file descriptor diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-07-12-00-04.gh-issue-74616.kgTGVb.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-07-12-00-04.gh-issue-74616.kgTGVb.rst new file mode 100644 index 00000000000000..5c345be9de6d0b --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-07-12-00-04.gh-issue-74616.kgTGVb.rst @@ -0,0 +1,2 @@ +:func:`input` now raises a ValueError when output on the terminal if the +prompt contains embedded null characters instead of silently truncating it. diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 7a9625134761f9..960bca01990c83 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2262,6 +2262,11 @@ builtin_input_impl(PyObject *module, PyObject *prompt) goto _readline_errors; assert(PyBytes_Check(po)); promptstr = PyBytes_AS_STRING(po); + if ((Py_ssize_t)strlen(promptstr) != PyBytes_GET_SIZE(po)) { + PyErr_SetString(PyExc_ValueError, + "input: prompt string cannot contain null characters"); + goto _readline_errors; + } } else { po = NULL; From 4b125dd31a634871d3b2d06ebfd1b9aef539766b Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 7 Dec 2023 11:27:30 +0100 Subject: [PATCH 205/228] gh-51944: Add missing macOS constants to termios (#112823) * gh-51944: Add some macOS constants to termios This changeset adds all public constants in and on macOS that weren't present already. Based on the macOS 14.2 SDK Co-authored-by: Serhiy Storchaka --- ...3-12-06-14-06-14.gh-issue-51944.-5qq_L.rst | 6 ++ Modules/termios.c | 61 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-12-06-14-06-14.gh-issue-51944.-5qq_L.rst diff --git a/Misc/NEWS.d/next/Library/2023-12-06-14-06-14.gh-issue-51944.-5qq_L.rst b/Misc/NEWS.d/next/Library/2023-12-06-14-06-14.gh-issue-51944.-5qq_L.rst new file mode 100644 index 00000000000000..821eefa7cffcd5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-06-14-06-14.gh-issue-51944.-5qq_L.rst @@ -0,0 +1,6 @@ +Add the following constants to the :mod:`termios` module. These values are +present in macOS system headers: ``ALTWERASE``, ``B14400``, ``B28800``, +``B7200``, ``B76800``, ``CCAR_OFLOW``, ``CCTS_OFLOW``, ``CDSR_OFLOW``, +``CDTR_IFLOW``, ``CIGNORE``, ``CRTS_IFLOW``, ``EXTPROC``, ``IUTF8``, +``MDMBUF``, ``NL2``, ``NL3``, ``NOKERNINFO``, ``ONOEOT``, ``OXTABS``, +``VDSUSP``, ``VSTATUS``. diff --git a/Modules/termios.c b/Modules/termios.c index 9fc2673ce0e788..1d97a3a2757966 100644 --- a/Modules/termios.c +++ b/Modules/termios.c @@ -702,6 +702,9 @@ static struct constant { #ifdef IMAXBEL {"IMAXBEL", IMAXBEL}, #endif +#ifdef IUTF8 + {"IUTF8", IUTF8}, +#endif /* struct termios.c_oflag constants */ {"OPOST", OPOST}, @@ -726,6 +729,12 @@ static struct constant { #ifdef OFDEL {"OFDEL", OFDEL}, #endif +#ifdef OXTABS + {"OXTABS", OXTABS}, +#endif +#ifdef ONOEOT + {"ONOEOT", ONOEOT}, +#endif #ifdef NLDLY {"NLDLY", NLDLY}, #endif @@ -752,6 +761,12 @@ static struct constant { #ifdef NL1 {"NL1", NL1}, #endif +#ifdef NL2 + {"NL2", NL2}, +#endif +#ifdef NL3 + {"NL3", NL3}, +#endif #ifdef CR0 {"CR0", CR0}, #endif @@ -799,6 +814,9 @@ static struct constant { #endif /* struct termios.c_cflag constants */ +#ifdef CIGNORE + {"CIGNORE", CIGNORE}, +#endif {"CSIZE", CSIZE}, {"CSTOPB", CSTOPB}, {"CREAD", CREAD}, @@ -813,6 +831,22 @@ static struct constant { {"CRTSCTS", (long)CRTSCTS}, #endif +#ifdef CRTS_IFLOW + {"CRTS_IFLOW", CRTS_IFLOW}, +#endif +#ifdef CDTR_IFLOW + {"CDTR_IFLOW", CDTR_IFLOW}, +#endif +#ifdef CDSR_OFLOW + {"CDSR_OFLOW", CDSR_OFLOW}, +#endif +#ifdef CCAR_OFLOW + {"CCAR_OFLOW", CCAR_OFLOW}, +#endif +#ifdef MDMBUF + {"MDMBUF", MDMBUF}, +#endif + /* struct termios.c_cflag-related values (character size) */ {"CS5", CS5}, {"CS6", CS6}, @@ -820,6 +854,9 @@ static struct constant { {"CS8", CS8}, /* struct termios.c_lflag constants */ +#ifdef ALTWERASE + {"ALTWERASE", ALTWERASE}, +#endif {"ISIG", ISIG}, {"ICANON", ICANON}, #ifdef XCASE @@ -840,6 +877,9 @@ static struct constant { #endif #ifdef FLUSHO {"FLUSHO", FLUSHO}, +#endif +#ifdef NOKERNINFO + {"NOKERNINFO", NOKERNINFO}, #endif {"NOFLSH", NOFLSH}, {"TOSTOP", TOSTOP}, @@ -847,6 +887,9 @@ static struct constant { {"PENDIN", PENDIN}, #endif {"IEXTEN", IEXTEN}, +#ifdef EXTPROC + {"EXTPROC", EXTPROC}, +#endif /* indexes into the control chars array returned by tcgetattr() */ {"VINTR", VINTR}, @@ -855,6 +898,9 @@ static struct constant { {"VKILL", VKILL}, {"VEOF", VEOF}, {"VTIME", VTIME}, +#ifdef VSTATUS + {"VSTATUS", VSTATUS}, +#endif {"VMIN", VMIN}, #ifdef VSWTC /* The #defines above ensure that if either is defined, both are, @@ -865,6 +911,9 @@ static struct constant { {"VSTART", VSTART}, {"VSTOP", VSTOP}, {"VSUSP", VSUSP}, +#ifdef VDSUSP + {"VDSUSP", VREPRINT}, +#endif {"VEOL", VEOL}, #ifdef VREPRINT {"VREPRINT", VREPRINT}, @@ -883,6 +932,18 @@ static struct constant { #endif +#ifdef B7200 + {"B7200", B7200}, +#endif +#ifdef B14400 + {"B14400", B14400}, +#endif +#ifdef B28800 + {"B28800", B28800}, +#endif +#ifdef B76800 + {"B76800", B76800}, +#endif #ifdef B460800 {"B460800", B460800}, #endif From 3d712a9f4c9f366edbe16b804ec4f6ae50b0a59f Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Thu, 7 Dec 2023 02:19:33 -0900 Subject: [PATCH 206/228] gh-102980: Redirect output of pdb's `interact` command, add tests and improve docs (#111194) --- Doc/library/pdb.rst | 19 ++++++- Lib/pdb.py | 17 ++++-- Lib/test/test_pdb.py | 53 +++++++++++++++++++ ...-10-23-03-49-34.gh-issue-102980.aXBd54.rst | 1 + 4 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-23-03-49-34.gh-issue-102980.aXBd54.rst diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst index bbc6aacc62aafa..2495dcf50bb17f 100644 --- a/Doc/library/pdb.rst +++ b/Doc/library/pdb.rst @@ -570,10 +570,27 @@ can be overridden by the local file. Start an interactive interpreter (using the :mod:`code` module) whose global namespace contains all the (global and local) names found in the current - scope. + scope. Use ``exit()`` or ``quit()`` to exit the interpreter and return to + the debugger. + + .. note:: + + Because interact creates a new global namespace with the current global + and local namespace for execution, assignment to variables will not + affect the original namespaces. + However, modification to the mutable objects will be reflected in the + original namespaces. .. versionadded:: 3.2 + .. versionadded:: 3.13 + ``exit()`` and ``quit()`` can be used to exit :pdbcmd:`interact` + command. + + .. versionchanged:: 3.13 + :pdbcmd:`interact` directs its output to the debugger's + output channel rather than :data:`sys.stderr`. + .. _debugger-aliases: .. pdbcommand:: alias [name [command]] diff --git a/Lib/pdb.py b/Lib/pdb.py index 9d124189df11cf..83b7fefec63636 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -207,6 +207,15 @@ def namespace(self): ) +class _PdbInteractiveConsole(code.InteractiveConsole): + def __init__(self, ns, message): + self._message = message + super().__init__(locals=ns, local_exit=True) + + def write(self, data): + self._message(data, end='') + + # Interaction prompt line will separate file and call info from code # text using value of line_prefix string. A newline and arrow may # be to your liking. You can set it once pdb is imported using the @@ -672,8 +681,8 @@ def handle_command_def(self, line): # interface abstraction functions - def message(self, msg): - print(msg, file=self.stdout) + def message(self, msg, end='\n'): + print(msg, end=end, file=self.stdout) def error(self, msg): print('***', msg, file=self.stdout) @@ -1786,7 +1795,9 @@ def do_interact(self, arg): contains all the (global and local) names found in the current scope. """ ns = {**self.curframe.f_globals, **self.curframe_locals} - code.interact("*interactive*", local=ns, local_exit=True) + console = _PdbInteractiveConsole(ns, message=self.message) + console.interact(banner="*pdb interact start*", + exitmsg="*exit from pdb interact command*") def do_alias(self, arg): """alias [name [command]] diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 50d8c8f52a909d..d53fe3c611bc35 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -778,6 +778,59 @@ def test_pdb_where_command(): (Pdb) continue """ +def test_pdb_interact_command(): + """Test interact command + + >>> g = 0 + >>> dict_g = {} + + >>> def test_function(): + ... x = 1 + ... lst_local = [] + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + + >>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE + ... 'interact', + ... 'x', + ... 'g', + ... 'x = 2', + ... 'g = 3', + ... 'dict_g["a"] = True', + ... 'lst_local.append(x)', + ... 'exit()', + ... 'p x', + ... 'p g', + ... 'p dict_g', + ... 'p lst_local', + ... 'continue', + ... ]): + ... test_function() + --Return-- + > (4)test_function()->None + -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + (Pdb) interact + *pdb interact start* + ... x + 1 + ... g + 0 + ... x = 2 + ... g = 3 + ... dict_g["a"] = True + ... lst_local.append(x) + ... exit() + *exit from pdb interact command* + (Pdb) p x + 1 + (Pdb) p g + 0 + (Pdb) p dict_g + {'a': True} + (Pdb) p lst_local + [2] + (Pdb) continue + """ + def test_convenience_variables(): """Test convenience variables diff --git a/Misc/NEWS.d/next/Library/2023-10-23-03-49-34.gh-issue-102980.aXBd54.rst b/Misc/NEWS.d/next/Library/2023-10-23-03-49-34.gh-issue-102980.aXBd54.rst new file mode 100644 index 00000000000000..d4bae4790d6fa4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-23-03-49-34.gh-issue-102980.aXBd54.rst @@ -0,0 +1 @@ +Redirect the output of ``interact`` command of :mod:`pdb` to the same channel as the debugger. Add tests and improve docs. From b449415b2f1b41e1c44cb453428657fdf6ff1d36 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 7 Dec 2023 12:49:40 +0000 Subject: [PATCH 207/228] GH-111485: Separate out parsing, analysis and code-gen phases of tier 1 code generator (GH-112299) --- Include/internal/pycore_opcode_metadata.h | 6 +- Makefile.pre.in | 3 +- Python/abstract_interp_cases.c.h | 3 +- Python/bytecodes.c | 5 +- Python/executor_cases.c.h | 2 +- Python/generated_cases.c.h | 1047 +++++++++++---------- Tools/cases_generator/analyzer.py | 456 +++++++++ Tools/cases_generator/cwriter.py | 111 +++ Tools/cases_generator/generate_cases.py | 1 - Tools/cases_generator/lexer.py | 9 +- Tools/cases_generator/mypy.ini | 2 + Tools/cases_generator/parser.py | 55 ++ Tools/cases_generator/parsing.py | 3 +- Tools/cases_generator/stack.py | 81 ++ Tools/cases_generator/tier1_generator.py | 417 ++++++++ 15 files changed, 1675 insertions(+), 526 deletions(-) create mode 100644 Tools/cases_generator/analyzer.py create mode 100644 Tools/cases_generator/cwriter.py create mode 100644 Tools/cases_generator/parser.py create mode 100644 Tools/cases_generator/stack.py create mode 100644 Tools/cases_generator/tier1_generator.py diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 4ae15e71e8d318..774c0f99379ed6 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1285,11 +1285,11 @@ int _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case _INIT_CALL_PY_EXACT_ARGS: return 1; case _PUSH_FRAME: - return 1; + return 0; case CALL_BOUND_METHOD_EXACT_ARGS: - return 1; + return 0; case CALL_PY_EXACT_ARGS: - return 1; + return 0; case CALL_PY_WITH_DEFAULTS: return 1; case CALL_TYPE_1: diff --git a/Makefile.pre.in b/Makefile.pre.in index b5edb4e6748fb0..6ac68d59c8c47f 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1587,7 +1587,6 @@ regen-cases: $(PYTHON_FOR_REGEN) \ $(srcdir)/Tools/cases_generator/generate_cases.py \ $(CASESFLAG) \ - -o $(srcdir)/Python/generated_cases.c.h.new \ -n $(srcdir)/Include/opcode_ids.h.new \ -t $(srcdir)/Python/opcode_targets.h.new \ -m $(srcdir)/Include/internal/pycore_opcode_metadata.h.new \ @@ -1595,6 +1594,8 @@ regen-cases: -p $(srcdir)/Lib/_opcode_metadata.py.new \ -a $(srcdir)/Python/abstract_interp_cases.c.h.new \ $(srcdir)/Python/bytecodes.c + $(PYTHON_FOR_REGEN) \ + $(srcdir)/Tools/cases_generator/tier1_generator.py -o $(srcdir)/Python/generated_cases.c.h.new $(srcdir)/Python/bytecodes.c $(UPDATE_FILE) $(srcdir)/Python/generated_cases.c.h $(srcdir)/Python/generated_cases.c.h.new $(UPDATE_FILE) $(srcdir)/Include/opcode_ids.h $(srcdir)/Include/opcode_ids.h.new $(UPDATE_FILE) $(srcdir)/Python/opcode_targets.h $(srcdir)/Python/opcode_targets.h.new diff --git a/Python/abstract_interp_cases.c.h b/Python/abstract_interp_cases.c.h index 0d7fbe8a39a5d4..96ac0aabd1b59f 100644 --- a/Python/abstract_interp_cases.c.h +++ b/Python/abstract_interp_cases.c.h @@ -774,7 +774,8 @@ } case _PUSH_FRAME: { - PARTITIONNODE_OVERWRITE((_Py_PARTITIONNODE_t *)PARTITIONNODE_NULLROOT, PEEK(-(-1)), true); + STACK_SHRINK(1); + PARTITIONNODE_OVERWRITE((_Py_PARTITIONNODE_t *)PARTITIONNODE_NULLROOT, PEEK(-(0)), true); break; } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 2075c195df3d38..bcad8dcf0e7dab 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -800,11 +800,11 @@ dummy_func( // We also push it onto the stack on exit, but that's a // different frame, and it's accounted for by _PUSH_FRAME. op(_POP_FRAME, (retval --)) { - assert(EMPTY()); #if TIER_ONE assert(frame != &entry_frame); #endif STORE_SP(); + assert(EMPTY()); _Py_LeaveRecursiveCallPy(tstate); // GH-99729: We need to unlink the frame *before* clearing it: _PyInterpreterFrame *dying = frame; @@ -1165,7 +1165,6 @@ dummy_func( } } - inst(STORE_NAME, (v -- )) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); PyObject *ns = LOCALS(); @@ -3130,7 +3129,7 @@ dummy_func( // The 'unused' output effect represents the return value // (which will be pushed when the frame returns). // It is needed so CALL_PY_EXACT_ARGS matches its family. - op(_PUSH_FRAME, (new_frame: _PyInterpreterFrame* -- unused)) { + op(_PUSH_FRAME, (new_frame: _PyInterpreterFrame* -- unused if (0))) { // Write it out explicitly because it's subtly different. // Eventually this should be the only occurrence of this code. assert(tstate->interp->eval_frame == NULL); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 547be6f13237dd..974e3f28a411b8 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -661,11 +661,11 @@ PyObject *retval; retval = stack_pointer[-1]; STACK_SHRINK(1); - assert(EMPTY()); #if TIER_ONE assert(frame != &entry_frame); #endif STORE_SP(); + assert(EMPTY()); _Py_LeaveRecursiveCallPy(tstate); // GH-99729: We need to unlink the frame *before* clearing it: _PyInterpreterFrame *dying = frame; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 0ac99e759deb12..24243ecfb5b8df 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1,6 +1,6 @@ -// This file is generated by Tools/cases_generator/generate_cases.py +// This file is generated by Tools/cases_generator/tier1_generator.py // from: -// Python/bytecodes.c +// ['./Python/bytecodes.c'] // Do not edit! #ifdef TIER_TWO @@ -8,6 +8,7 @@ #endif #define TIER_ONE 1 + TARGET(BEFORE_ASYNC_WITH) { frame->instr_ptr = next_instr; next_instr += 1; @@ -45,9 +46,9 @@ Py_DECREF(exit); if (true) goto pop_1_error; } - STACK_GROW(1); - stack_pointer[-2] = exit; - stack_pointer[-1] = res; + stack_pointer[-1] = exit; + stack_pointer[0] = res; + stack_pointer += 1; DISPATCH(); } @@ -91,9 +92,9 @@ Py_DECREF(exit); if (true) goto pop_1_error; } - STACK_GROW(1); - stack_pointer[-2] = exit; - stack_pointer[-1] = res; + stack_pointer[-1] = exit; + stack_pointer[0] = res; + stack_pointer += 1; DISPATCH(); } @@ -103,7 +104,6 @@ INSTRUCTION_STATS(BINARY_OP); PREDICTED(BINARY_OP); _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); PyObject *rhs; PyObject *lhs; PyObject *res; @@ -133,8 +133,8 @@ Py_DECREF(rhs); if (res == NULL) goto pop_2_error; } - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -142,6 +142,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_OP_ADD_FLOAT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); PyObject *right; PyObject *left; PyObject *res; @@ -156,12 +157,12 @@ { STAT_INC(BINARY_OP, hit); double dres = - ((PyFloatObject *)left)->ob_fval + - ((PyFloatObject *)right)->ob_fval; + ((PyFloatObject *)left)->ob_fval + + ((PyFloatObject *)right)->ob_fval; DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); } - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -169,6 +170,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_OP_ADD_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); PyObject *right; PyObject *left; PyObject *res; @@ -187,8 +189,8 @@ _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); if (res == NULL) goto pop_2_error; } - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -196,6 +198,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_OP_ADD_UNICODE); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); PyObject *right; PyObject *left; PyObject *res; @@ -214,8 +217,8 @@ _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); if (res == NULL) goto pop_2_error; } - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -223,6 +226,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_OP_INPLACE_ADD_UNICODE); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); PyObject *right; PyObject *left; // _GUARD_BOTH_UNICODE @@ -258,7 +262,7 @@ assert(next_instr->op.code == STORE_FAST); SKIP_OVER(1); } - STACK_SHRINK(2); + stack_pointer += -2; DISPATCH(); } @@ -266,6 +270,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_OP_MULTIPLY_FLOAT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); PyObject *right; PyObject *left; PyObject *res; @@ -280,12 +285,12 @@ { STAT_INC(BINARY_OP, hit); double dres = - ((PyFloatObject *)left)->ob_fval * - ((PyFloatObject *)right)->ob_fval; + ((PyFloatObject *)left)->ob_fval * + ((PyFloatObject *)right)->ob_fval; DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); } - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -293,6 +298,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_OP_MULTIPLY_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); PyObject *right; PyObject *left; PyObject *res; @@ -311,8 +317,8 @@ _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); if (res == NULL) goto pop_2_error; } - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -320,6 +326,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_OP_SUBTRACT_FLOAT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); PyObject *right; PyObject *left; PyObject *res; @@ -334,12 +341,12 @@ { STAT_INC(BINARY_OP, hit); double dres = - ((PyFloatObject *)left)->ob_fval - - ((PyFloatObject *)right)->ob_fval; + ((PyFloatObject *)left)->ob_fval - + ((PyFloatObject *)right)->ob_fval; DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); } - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -347,6 +354,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_OP_SUBTRACT_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); PyObject *right; PyObject *left; PyObject *res; @@ -365,8 +373,8 @@ _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); if (res == NULL) goto pop_2_error; } - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -393,8 +401,8 @@ } Py_DECREF(container); if (res == NULL) goto pop_3_error; - STACK_SHRINK(2); - stack_pointer[-1] = res; + stack_pointer[-3] = res; + stack_pointer += -2; DISPATCH(); } @@ -404,7 +412,6 @@ INSTRUCTION_STATS(BINARY_SUBSCR); PREDICTED(BINARY_SUBSCR); _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); PyObject *sub; PyObject *container; PyObject *res; @@ -431,8 +438,8 @@ Py_DECREF(sub); if (res == NULL) goto pop_2_error; } - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -440,6 +447,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_SUBSCR_DICT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); PyObject *sub; PyObject *dict; PyObject *res; @@ -454,8 +462,9 @@ Py_DECREF(dict); Py_DECREF(sub); if (rc <= 0) goto pop_2_error; - STACK_SHRINK(1); - stack_pointer[-1] = res; + // not found or error + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -463,6 +472,7 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_SUBSCR_GETITEM); + static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); PyObject *sub; PyObject *container; sub = stack_pointer[-1]; @@ -494,6 +504,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_SUBSCR_LIST_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); PyObject *sub; PyObject *list; PyObject *res; @@ -501,7 +512,6 @@ list = stack_pointer[-2]; DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); DEOPT_IF(!PyList_CheckExact(list), BINARY_SUBSCR); - // Deopt unless 0 <= sub < PyList_Size(list) DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR); Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; @@ -512,8 +522,8 @@ Py_INCREF(res); _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); Py_DECREF(list); - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -521,6 +531,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_SUBSCR_STR_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); PyObject *sub; PyObject *str; PyObject *res; @@ -538,8 +549,8 @@ res = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); Py_DECREF(str); - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -547,6 +558,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_SUBSCR_TUPLE_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); PyObject *sub; PyObject *tuple; PyObject *res; @@ -554,7 +566,6 @@ tuple = stack_pointer[-2]; DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); DEOPT_IF(!PyTuple_CheckExact(tuple), BINARY_SUBSCR); - // Deopt unless 0 <= sub < PyTuple_Size(list) DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR); Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; @@ -565,8 +576,8 @@ Py_INCREF(res); _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); Py_DECREF(tuple); - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -578,7 +589,7 @@ PyObject **values; PyObject *map; keys = stack_pointer[-1]; - values = stack_pointer - 1 - oparg; + values = &stack_pointer[-1 - oparg]; if (!PyTuple_CheckExact(keys) || PyTuple_GET_SIZE(keys) != (Py_ssize_t)oparg) { _PyErr_SetString(tstate, PyExc_SystemError, @@ -586,15 +597,15 @@ GOTO_ERROR(error); // Pop the keys and values. } map = _PyDict_FromItems( - &PyTuple_GET_ITEM(keys, 0), 1, - values, 1, oparg); + &PyTuple_GET_ITEM(keys, 0), 1, + values, 1, oparg); for (int _i = oparg; --_i >= 0;) { Py_DECREF(values[_i]); } Py_DECREF(keys); - if (map == NULL) { STACK_SHRINK(oparg); goto pop_1_error; } - STACK_SHRINK(oparg); - stack_pointer[-1] = map; + if (map == NULL) { stack_pointer += -1 - oparg; goto error; } + stack_pointer[-1 - oparg] = map; + stack_pointer += -oparg; DISPATCH(); } @@ -604,12 +615,11 @@ INSTRUCTION_STATS(BUILD_LIST); PyObject **values; PyObject *list; - values = stack_pointer - oparg; + values = &stack_pointer[-oparg]; list = _PyList_FromArraySteal(values, oparg); - if (list == NULL) { STACK_SHRINK(oparg); goto error; } - STACK_SHRINK(oparg); - STACK_GROW(1); - stack_pointer[-1] = list; + if (list == NULL) { stack_pointer += -oparg; goto error; } + stack_pointer[-oparg] = list; + stack_pointer += 1 - oparg; DISPATCH(); } @@ -619,18 +629,17 @@ INSTRUCTION_STATS(BUILD_MAP); PyObject **values; PyObject *map; - values = stack_pointer - oparg*2; + values = &stack_pointer[-oparg*2]; map = _PyDict_FromItems( - values, 2, - values+1, 2, - oparg); + values, 2, + values+1, 2, + oparg); for (int _i = oparg*2; --_i >= 0;) { Py_DECREF(values[_i]); } - if (map == NULL) { STACK_SHRINK(oparg*2); goto error; } - STACK_SHRINK(oparg*2); - STACK_GROW(1); - stack_pointer[-1] = map; + if (map == NULL) { stack_pointer += -oparg*2; goto error; } + stack_pointer[-oparg*2] = map; + stack_pointer += 1 - oparg*2; DISPATCH(); } @@ -640,24 +649,23 @@ INSTRUCTION_STATS(BUILD_SET); PyObject **values; PyObject *set; - values = stack_pointer - oparg; + values = &stack_pointer[-oparg]; set = PySet_New(NULL); if (set == NULL) - GOTO_ERROR(error); + GOTO_ERROR(error); int err = 0; for (int i = 0; i < oparg; i++) { PyObject *item = values[i]; if (err == 0) - err = PySet_Add(set, item); + err = PySet_Add(set, item); Py_DECREF(item); } if (err != 0) { Py_DECREF(set); - if (true) { STACK_SHRINK(oparg); goto error; } + if (true) { stack_pointer += -oparg; goto error; } } - STACK_SHRINK(oparg); - STACK_GROW(1); - stack_pointer[-1] = set; + stack_pointer[-oparg] = set; + stack_pointer += 1 - oparg; DISPATCH(); } @@ -669,17 +677,16 @@ PyObject *stop; PyObject *start; PyObject *slice; - if (oparg == 3) { step = stack_pointer[-(oparg == 3 ? 1 : 0)]; } - stop = stack_pointer[-1 - (oparg == 3 ? 1 : 0)]; - start = stack_pointer[-2 - (oparg == 3 ? 1 : 0)]; + if (oparg == 3) { step = stack_pointer[-(((oparg == 3) ? 1 : 0))]; } + stop = stack_pointer[-1 - (((oparg == 3) ? 1 : 0))]; + start = stack_pointer[-2 - (((oparg == 3) ? 1 : 0))]; slice = PySlice_New(start, stop, step); Py_DECREF(start); Py_DECREF(stop); Py_XDECREF(step); - if (slice == NULL) { STACK_SHRINK(((oparg == 3) ? 1 : 0)); goto pop_2_error; } - STACK_SHRINK(((oparg == 3) ? 1 : 0)); - STACK_SHRINK(1); - stack_pointer[-1] = slice; + if (slice == NULL) { stack_pointer += -2 - (((oparg == 3) ? 1 : 0)); goto error; } + stack_pointer[-2 - (((oparg == 3) ? 1 : 0))] = slice; + stack_pointer += -1 - (((oparg == 3) ? 1 : 0)); DISPATCH(); } @@ -689,15 +696,14 @@ INSTRUCTION_STATS(BUILD_STRING); PyObject **pieces; PyObject *str; - pieces = stack_pointer - oparg; + pieces = &stack_pointer[-oparg]; str = _PyUnicode_JoinArray(&_Py_STR(empty), pieces, oparg); for (int _i = oparg; --_i >= 0;) { Py_DECREF(pieces[_i]); } - if (str == NULL) { STACK_SHRINK(oparg); goto error; } - STACK_SHRINK(oparg); - STACK_GROW(1); - stack_pointer[-1] = str; + if (str == NULL) { stack_pointer += -oparg; goto error; } + stack_pointer[-oparg] = str; + stack_pointer += 1 - oparg; DISPATCH(); } @@ -707,12 +713,11 @@ INSTRUCTION_STATS(BUILD_TUPLE); PyObject **values; PyObject *tup; - values = stack_pointer - oparg; + values = &stack_pointer[-oparg]; tup = _PyTuple_FromArraySteal(values, oparg); - if (tup == NULL) { STACK_SHRINK(oparg); goto error; } - STACK_SHRINK(oparg); - STACK_GROW(1); - stack_pointer[-1] = tup; + if (tup == NULL) { stack_pointer += -oparg; goto error; } + stack_pointer[-oparg] = tup; + stack_pointer += 1 - oparg; DISPATCH(); } @@ -730,13 +735,12 @@ INSTRUCTION_STATS(CALL); PREDICTED(CALL); _Py_CODEUNIT *this_instr = next_instr - 4; - static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; PyObject *self_or_null; PyObject *callable; PyObject *res; // _SPECIALIZE_CALL - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; { @@ -793,12 +797,12 @@ } /* Callable is not a normal Python function */ res = PyObject_Vectorcall( - callable, args, - total_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - NULL); + callable, args, + total_args | PY_VECTORCALL_ARGUMENTS_OFFSET, + NULL); if (opcode == INSTRUMENTED_CALL) { PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : args[0]; + &_PyInstrumentation_MISSING : args[0]; if (res == NULL) { _Py_call_instrumentation_exc2( tstate, PY_MONITORING_EVENT_C_RAISE, @@ -818,11 +822,10 @@ for (int i = 0; i < total_args; i++) { Py_DECREF(args[i]); } - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; CHECK_EVAL_BREAKER(); DISPATCH(); } @@ -831,10 +834,11 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_ALLOC_AND_ENTER_INIT); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; PyObject *null; PyObject *callable; - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; /* This instruction does the following: @@ -890,13 +894,15 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_BOUND_METHOD_EXACT_ARGS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject *null; PyObject *callable; + PyObject *func; PyObject *self; PyObject *self_or_null; - PyObject *func; PyObject **args; _PyInterpreterFrame *new_frame; + /* Skip 1 cache entry */ // _CHECK_PEP_523 { DEOPT_IF(tstate->interp->eval_frame, CALL); @@ -936,7 +942,8 @@ DEOPT_IF(tstate->py_recursion_remaining <= 1, CALL); } // _INIT_CALL_PY_EXACT_ARGS - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; { int argcount = oparg; if (self_or_null != NULL) { @@ -960,26 +967,26 @@ #endif } // _PUSH_FRAME - STACK_SHRINK(oparg); - STACK_SHRINK(2); { // Write it out explicitly because it's subtly different. // Eventually this should be the only occurrence of this code. assert(tstate->interp->eval_frame == NULL); - STORE_SP(); + stack_pointer += -2 - oparg; + _PyFrame_SetStackPointer(frame, stack_pointer); new_frame->previous = frame; CALL_STAT_INC(inlined_py_calls); frame = tstate->current_frame = new_frame; tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); - #if LLTRACE && TIER_ONE + #if LLTRACE && TIER_ONE lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); if (lltrace < 0) { goto exit_unwind; } - #endif + #endif } + stack_pointer += (((0) ? 1 : 0)); DISPATCH(); } @@ -987,11 +994,12 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_BUILTIN_CLASS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; PyObject *self_or_null; PyObject *callable; PyObject *res; - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; int total_args = oparg; @@ -1009,10 +1017,9 @@ Py_DECREF(args[i]); } Py_DECREF(tp); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; CHECK_EVAL_BREAKER(); DISPATCH(); } @@ -1021,11 +1028,12 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_BUILTIN_FAST); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; PyObject *self_or_null; PyObject *callable; PyObject *res; - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; /* Builtin METH_FASTCALL functions, without keywords */ @@ -1044,21 +1052,19 @@ args, total_args); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - /* Free the arguments. */ for (int i = 0; i < total_args; i++) { Py_DECREF(args[i]); } Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - /* Not deopting because this doesn't mean our optimization was - wrong. `res` can be NULL for valid reasons. Eg. getattr(x, - 'invalid'). In those cases an exception is set, so we must - handle it. - */ - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + /* Not deopting because this doesn't mean our optimization was + wrong. `res` can be NULL for valid reasons. Eg. getattr(x, + 'invalid'). In those cases an exception is set, so we must + handle it. + */ + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; CHECK_EVAL_BREAKER(); DISPATCH(); } @@ -1067,11 +1073,12 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_BUILTIN_FAST_WITH_KEYWORDS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; PyObject *self_or_null; PyObject *callable; PyObject *res; - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ @@ -1085,20 +1092,18 @@ STAT_INC(CALL, hit); /* res = func(self, args, nargs, kwnames) */ _PyCFunctionFastWithKeywords cfunc = - (_PyCFunctionFastWithKeywords)(void(*)(void)) - PyCFunction_GET_FUNCTION(callable); + (_PyCFunctionFastWithKeywords)(void(*)(void)) + PyCFunction_GET_FUNCTION(callable); res = cfunc(PyCFunction_GET_SELF(callable), args, total_args, NULL); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - /* Free the arguments. */ for (int i = 0; i < total_args; i++) { Py_DECREF(args[i]); } Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; CHECK_EVAL_BREAKER(); DISPATCH(); } @@ -1107,11 +1112,12 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_BUILTIN_O); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; PyObject *self_or_null; PyObject *callable; PyObject *res; - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; /* Builtin METH_O functions */ @@ -1134,13 +1140,11 @@ res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg); _Py_LeaveRecursiveCallTstate(tstate); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - Py_DECREF(arg); Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; CHECK_EVAL_BREAKER(); DISPATCH(); } @@ -1155,9 +1159,9 @@ PyObject *callargs; PyObject *func; PyObject *result; - if (oparg & 1) { kwargs = stack_pointer[-(oparg & 1 ? 1 : 0)]; } - callargs = stack_pointer[-1 - (oparg & 1 ? 1 : 0)]; - func = stack_pointer[-3 - (oparg & 1 ? 1 : 0)]; + if (oparg & 1) { kwargs = stack_pointer[-((oparg & 1))]; } + callargs = stack_pointer[-1 - ((oparg & 1))]; + func = stack_pointer[-3 - ((oparg & 1))]; // DICT_MERGE is called before this opcode if there are kwargs. // It converts all dict subtypes in kwargs into regular dicts. assert(kwargs == NULL || PyDict_CheckExact(kwargs)); @@ -1177,7 +1181,7 @@ !PyFunction_Check(func) && !PyMethod_Check(func) ) { PyObject *arg = PyTuple_GET_SIZE(callargs) > 0 ? - PyTuple_GET_ITEM(callargs, 0) : Py_None; + PyTuple_GET_ITEM(callargs, 0) : Py_None; int err = _Py_call_instrumentation_2args( tstate, PY_MONITORING_EVENT_CALL, frame, this_instr, func, arg); @@ -1205,10 +1209,9 @@ Py_ssize_t nargs = PyTuple_GET_SIZE(callargs); int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags; PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func)); - _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex(tstate, - (PyFunctionObject *)func, locals, - nargs, callargs, kwargs); + (PyFunctionObject *)func, locals, + nargs, callargs, kwargs); // Need to manually shrink the stack since we exit with DISPATCH_INLINED. STACK_SHRINK(oparg + 3); if (new_frame == NULL) { @@ -1224,10 +1227,9 @@ Py_DECREF(callargs); Py_XDECREF(kwargs); assert(PEEK(2 + (oparg & 1)) == NULL); - if (result == NULL) { STACK_SHRINK(((oparg & 1) ? 1 : 0)); goto pop_3_error; } - STACK_SHRINK(((oparg & 1) ? 1 : 0)); - STACK_SHRINK(2); - stack_pointer[-1] = result; + if (result == NULL) { stack_pointer += -3 - ((oparg & 1)); goto error; } + stack_pointer[-3 - ((oparg & 1))] = result; + stack_pointer += -2 - ((oparg & 1)); CHECK_EVAL_BREAKER(); DISPATCH(); } @@ -1261,8 +1263,8 @@ Py_DECREF(value2); Py_DECREF(value1); if (res == NULL) goto pop_2_error; - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -1270,11 +1272,12 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_ISINSTANCE); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; PyObject *self_or_null; PyObject *callable; PyObject *res; - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; /* isinstance(o, o2) */ @@ -1295,14 +1298,12 @@ } res = PyBool_FromLong(retval); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - Py_DECREF(inst); Py_DECREF(cls); Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; DISPATCH(); } @@ -1318,7 +1319,7 @@ PyObject *callable; PyObject *res; kwnames = stack_pointer[-1]; - args = stack_pointer - 1 - oparg; + args = &stack_pointer[-1 - oparg]; self_or_null = stack_pointer[-2 - oparg]; callable = stack_pointer[-3 - oparg]; // oparg counts all of the args, but *not* self: @@ -1363,12 +1364,12 @@ } /* Callable is not a normal Python function */ res = PyObject_Vectorcall( - callable, args, - positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - kwnames); + callable, args, + positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, + kwnames); if (opcode == INSTRUMENTED_CALL_KW) { PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : args[0]; + &_PyInstrumentation_MISSING : args[0]; if (res == NULL) { _Py_call_instrumentation_exc2( tstate, PY_MONITORING_EVENT_C_RAISE, @@ -1389,10 +1390,9 @@ for (int i = 0; i < total_args; i++) { Py_DECREF(args[i]); } - if (res == NULL) { STACK_SHRINK(oparg); goto pop_3_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(2); - stack_pointer[-1] = res; + if (res == NULL) { stack_pointer += -3 - oparg; goto error; } + stack_pointer[-3 - oparg] = res; + stack_pointer += -2 - oparg; CHECK_EVAL_BREAKER(); DISPATCH(); } @@ -1401,11 +1401,12 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_LEN); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; PyObject *self_or_null; PyObject *callable; PyObject *res; - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; /* len(o) */ @@ -1425,13 +1426,11 @@ } res = PyLong_FromSsize_t(len_i); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - Py_DECREF(callable); Py_DECREF(arg); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; DISPATCH(); } @@ -1439,10 +1438,11 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_LIST_APPEND); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; PyObject *self; PyObject *callable; - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; self = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; assert(oparg == 1); @@ -1467,11 +1467,12 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_FAST); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; PyObject *self_or_null; PyObject *callable; PyObject *res; - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; int total_args = oparg; @@ -1488,7 +1489,7 @@ DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); STAT_INC(CALL, hit); _PyCFunctionFast cfunc = - (_PyCFunctionFast)(void(*)(void))meth->ml_meth; + (_PyCFunctionFast)(void(*)(void))meth->ml_meth; int nargs = total_args - 1; res = cfunc(self, args + 1, nargs); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); @@ -1497,10 +1498,9 @@ Py_DECREF(args[i]); } Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; CHECK_EVAL_BREAKER(); DISPATCH(); } @@ -1509,11 +1509,12 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; PyObject *self_or_null; PyObject *callable; PyObject *res; - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; int total_args = oparg; @@ -1531,19 +1532,17 @@ STAT_INC(CALL, hit); int nargs = total_args - 1; _PyCFunctionFastWithKeywords cfunc = - (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; + (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; res = cfunc(self, args + 1, nargs, NULL); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - /* Free the arguments. */ for (int i = 0; i < total_args; i++) { Py_DECREF(args[i]); } Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; CHECK_EVAL_BREAKER(); DISPATCH(); } @@ -1552,11 +1551,12 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_NOARGS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; PyObject *self_or_null; PyObject *callable; PyObject *res; - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; assert(oparg == 0 || oparg == 1); @@ -1584,10 +1584,9 @@ assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(self); Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; CHECK_EVAL_BREAKER(); DISPATCH(); } @@ -1596,11 +1595,12 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_O); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; PyObject *self_or_null; PyObject *callable; PyObject *res; - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; int total_args = oparg; @@ -1629,10 +1629,9 @@ Py_DECREF(self); Py_DECREF(arg); Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; CHECK_EVAL_BREAKER(); DISPATCH(); } @@ -1641,10 +1640,12 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_PY_EXACT_ARGS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject *self_or_null; PyObject *callable; PyObject **args; _PyInterpreterFrame *new_frame; + /* Skip 1 cache entry */ // _CHECK_PEP_523 { DEOPT_IF(tstate->interp->eval_frame, CALL); @@ -1668,7 +1669,8 @@ DEOPT_IF(tstate->py_recursion_remaining <= 1, CALL); } // _INIT_CALL_PY_EXACT_ARGS - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; { int argcount = oparg; if (self_or_null != NULL) { @@ -1692,26 +1694,26 @@ #endif } // _PUSH_FRAME - STACK_SHRINK(oparg); - STACK_SHRINK(2); { // Write it out explicitly because it's subtly different. // Eventually this should be the only occurrence of this code. assert(tstate->interp->eval_frame == NULL); - STORE_SP(); + stack_pointer += -2 - oparg; + _PyFrame_SetStackPointer(frame, stack_pointer); new_frame->previous = frame; CALL_STAT_INC(inlined_py_calls); frame = tstate->current_frame = new_frame; tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); - #if LLTRACE && TIER_ONE + #if LLTRACE && TIER_ONE lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); if (lltrace < 0) { goto exit_unwind; } - #endif + #endif } + stack_pointer += (((0) ? 1 : 0)); DISPATCH(); } @@ -1719,10 +1721,11 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_PY_WITH_DEFAULTS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; PyObject *self_or_null; PyObject *callable; - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; uint32_t func_version = read_u32(&this_instr[2].cache); @@ -1763,11 +1766,12 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_STR_1); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; PyObject *null; PyObject *callable; PyObject *res; - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; assert(oparg == 1); @@ -1778,10 +1782,9 @@ res = PyObject_Str(arg); Py_DECREF(arg); Py_DECREF(&PyUnicode_Type); // I.e., callable - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; CHECK_EVAL_BREAKER(); DISPATCH(); } @@ -1790,11 +1793,12 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_TUPLE_1); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; PyObject *null; PyObject *callable; PyObject *res; - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; assert(oparg == 1); @@ -1805,10 +1809,9 @@ res = PySequence_Tuple(arg); Py_DECREF(arg); Py_DECREF(&PyTuple_Type); // I.e., tuple - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; CHECK_EVAL_BREAKER(); DISPATCH(); } @@ -1817,11 +1820,12 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_TYPE_1); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; PyObject *null; PyObject *callable; PyObject *res; - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; assert(oparg == 1); @@ -1832,9 +1836,8 @@ res = Py_NewRef(Py_TYPE(obj)); Py_DECREF(obj); Py_DECREF(&PyType_Type); // I.e., callable - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; DISPATCH(); } @@ -1853,18 +1856,15 @@ Py_DECREF(match_type); if (true) goto pop_2_error; } - match = NULL; rest = NULL; int res = _PyEval_ExceptionGroupMatch(exc_value, match_type, - &match, &rest); + &match, &rest); Py_DECREF(exc_value); Py_DECREF(match_type); if (res < 0) goto pop_2_error; - assert((match == NULL) == (rest == NULL)); if (match == NULL) goto pop_2_error; - if (!Py_IsNone(match)) { PyErr_SetHandledException(match); } @@ -1884,10 +1884,9 @@ left = stack_pointer[-2]; assert(PyExceptionInstance_Check(left)); if (_PyEval_CheckExceptTypeValid(tstate, right) < 0) { - Py_DECREF(right); - if (true) goto pop_1_error; + Py_DECREF(right); + if (true) goto pop_1_error; } - int res = PyErr_GivenExceptionMatches(left, right); Py_DECREF(right); b = res ? Py_True : Py_False; @@ -1922,9 +1921,9 @@ monitor_reraise(tstate, frame, this_instr); goto exception_unwind; } - STACK_SHRINK(1); - stack_pointer[-2] = none; - stack_pointer[-1] = value; + stack_pointer[-3] = none; + stack_pointer[-2] = value; + stack_pointer += -1; DISPATCH(); } @@ -1934,7 +1933,6 @@ INSTRUCTION_STATS(COMPARE_OP); PREDICTED(COMPARE_OP); _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_COMPARE_OP == 1, "incorrect cache size"); PyObject *right; PyObject *left; PyObject *res; @@ -1968,8 +1966,8 @@ res = res_bool ? Py_True : Py_False; } } - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -1977,6 +1975,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(COMPARE_OP_FLOAT); + static_assert(INLINE_CACHE_ENTRIES_COMPARE_OP == 1, "incorrect cache size"); PyObject *right; PyObject *left; PyObject *res; @@ -1993,8 +1992,8 @@ _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); res = (sign_ish & oparg) ? Py_True : Py_False; // It's always a bool, so we don't care about oparg & 16. - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -2002,6 +2001,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(COMPARE_OP_INT); + static_assert(INLINE_CACHE_ENTRIES_COMPARE_OP == 1, "incorrect cache size"); PyObject *right; PyObject *left; PyObject *res; @@ -2022,8 +2022,8 @@ _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); res = (sign_ish & oparg) ? Py_True : Py_False; // It's always a bool, so we don't care about oparg & 16. - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -2031,6 +2031,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(COMPARE_OP_STR); + static_assert(INLINE_CACHE_ENTRIES_COMPARE_OP == 1, "incorrect cache size"); PyObject *right; PyObject *left; PyObject *res; @@ -2048,8 +2049,8 @@ assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); res = ((COMPARISON_NOT_EQUALS + eq) & oparg) ? Py_True : Py_False; // It's always a bool, so we don't care about oparg & 16. - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -2067,8 +2068,8 @@ Py_DECREF(right); if (res < 0) goto pop_2_error; b = (res ^ oparg) ? Py_True : Py_False; - STACK_SHRINK(1); - stack_pointer[-1] = b; + stack_pointer[-2] = b; + stack_pointer += -1; DISPATCH(); } @@ -2098,8 +2099,8 @@ bottom = stack_pointer[-1 - (oparg-1)]; assert(oparg > 0); top = Py_NewRef(bottom); - STACK_GROW(1); - stack_pointer[-1] = top; + stack_pointer[0] = top; + stack_pointer += 1; DISPATCH(); } @@ -2130,7 +2131,7 @@ int err = PyObject_DelAttr(owner, name); Py_DECREF(owner); if (err) goto pop_1_error; - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -2172,7 +2173,7 @@ if (err != 0) { if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, name); + NAME_ERROR_MSG, name); } GOTO_ERROR(error); } @@ -2195,8 +2196,8 @@ // Can't use ERROR_IF here. if (err != 0) { _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, - name); + NAME_ERROR_MSG, + name); GOTO_ERROR(error); } DISPATCH(); @@ -2215,7 +2216,7 @@ Py_DECREF(container); Py_DECREF(sub); if (err) goto pop_2_error; - STACK_SHRINK(2); + stack_pointer += -2; DISPATCH(); } @@ -2235,7 +2236,7 @@ if (true) goto pop_1_error; } Py_DECREF(update); - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -2250,14 +2251,14 @@ if (PyDict_Update(dict, update) < 0) { if (_PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) { _PyErr_Format(tstate, PyExc_TypeError, - "'%.200s' object is not a mapping", - Py_TYPE(update)->tp_name); + "'%.200s' object is not a mapping", + Py_TYPE(update)->tp_name); } Py_DECREF(update); if (true) goto pop_1_error; } Py_DECREF(update); - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -2281,7 +2282,7 @@ monitor_reraise(tstate, frame, this_instr); goto exception_unwind; } - STACK_SHRINK(2); + stack_pointer += -2; DISPATCH(); } @@ -2290,17 +2291,17 @@ next_instr += 1; INSTRUCTION_STATS(END_FOR); PyObject *value; - // POP_TOP + // _POP_TOP value = stack_pointer[-1]; { Py_DECREF(value); } - // POP_TOP + // _POP_TOP value = stack_pointer[-2]; { Py_DECREF(value); } - STACK_SHRINK(2); + stack_pointer += -2; DISPATCH(); } @@ -2313,8 +2314,8 @@ value = stack_pointer[-1]; receiver = stack_pointer[-2]; Py_DECREF(receiver); - STACK_SHRINK(1); - stack_pointer[-1] = value; + stack_pointer[-2] = value; + stack_pointer += -1; DISPATCH(); } @@ -2324,7 +2325,6 @@ INSTRUCTION_STATS(ENTER_EXECUTOR); TIER_ONE_ONLY CHECK_EVAL_BREAKER(); - PyCodeObject *code = _PyFrame_GetCode(frame); _PyExecutorObject *executor = (_PyExecutorObject *)code->co_executors->executors[oparg&255]; int original_oparg = executor->vm_data.oparg | (oparg & 0xfffff00); @@ -2352,11 +2352,11 @@ assert(STACK_LEVEL() == 2); if (should_be_none != Py_None) { PyErr_Format(PyExc_TypeError, - "__init__() should return None, not '%.200s'", - Py_TYPE(should_be_none)->tp_name); + "__init__() should return None, not '%.200s'", + Py_TYPE(should_be_none)->tp_name); GOTO_ERROR(error); } - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -2405,8 +2405,8 @@ Py_DECREF(value); Py_DECREF(fmt_spec); if (res == NULL) goto pop_2_error; - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -2416,7 +2416,6 @@ INSTRUCTION_STATS(FOR_ITER); PREDICTED(FOR_ITER); _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); PyObject *iter; PyObject *next; // _SPECIALIZE_FOR_ITER @@ -2448,7 +2447,7 @@ } /* iterator ended normally */ assert(next_instr[oparg].op.code == END_FOR || - next_instr[oparg].op.code == INSTRUMENTED_END_FOR); + next_instr[oparg].op.code == INSTRUMENTED_END_FOR); Py_DECREF(iter); STACK_SHRINK(1); /* Jump forward oparg, then skip following END_FOR instruction */ @@ -2457,8 +2456,8 @@ } // Common case: no jump, leave it to the code generator } - STACK_GROW(1); - stack_pointer[-1] = next; + stack_pointer[0] = next; + stack_pointer += 1; DISPATCH(); } @@ -2466,6 +2465,7 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(FOR_ITER_GEN); + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); PyObject *iter; iter = stack_pointer[-1]; DEOPT_IF(tstate->interp->eval_frame, FOR_ITER); @@ -2489,8 +2489,10 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(FOR_ITER_LIST); + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); PyObject *iter; PyObject *next; + /* Skip 1 cache entry */ // _ITER_CHECK_LIST iter = stack_pointer[-1]; { @@ -2523,8 +2525,8 @@ assert(it->it_index < PyList_GET_SIZE(seq)); next = Py_NewRef(PyList_GET_ITEM(seq, it->it_index++)); } - STACK_GROW(1); - stack_pointer[-1] = next; + stack_pointer[0] = next; + stack_pointer += 1; DISPATCH(); } @@ -2532,8 +2534,10 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(FOR_ITER_RANGE); + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); PyObject *iter; PyObject *next; + /* Skip 1 cache entry */ // _ITER_CHECK_RANGE iter = stack_pointer[-1]; { @@ -2564,8 +2568,8 @@ next = PyLong_FromLong(value); if (next == NULL) goto error; } - STACK_GROW(1); - stack_pointer[-1] = next; + stack_pointer[0] = next; + stack_pointer += 1; DISPATCH(); } @@ -2573,8 +2577,10 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(FOR_ITER_TUPLE); + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); PyObject *iter; PyObject *next; + /* Skip 1 cache entry */ // _ITER_CHECK_TUPLE iter = stack_pointer[-1]; { @@ -2607,8 +2613,8 @@ assert(it->it_index < PyTuple_GET_SIZE(seq)); next = Py_NewRef(PyTuple_GET_ITEM(seq, it->it_index++)); } - STACK_GROW(1); - stack_pointer[-1] = next; + stack_pointer[0] = next; + stack_pointer += 1; DISPATCH(); } @@ -2621,11 +2627,9 @@ obj = stack_pointer[-1]; unaryfunc getter = NULL; PyTypeObject *type = Py_TYPE(obj); - if (type->tp_as_async != NULL) { getter = type->tp_as_async->am_aiter; } - if (getter == NULL) { _PyErr_Format(tstate, PyExc_TypeError, "'async for' requires an object with " @@ -2634,14 +2638,11 @@ Py_DECREF(obj); if (true) goto pop_1_error; } - iter = (*getter)(obj); Py_DECREF(obj); if (iter == NULL) goto pop_1_error; - if (Py_TYPE(iter)->tp_as_async == NULL || - Py_TYPE(iter)->tp_as_async->am_anext == NULL) { - + Py_TYPE(iter)->tp_as_async->am_anext == NULL) { _PyErr_Format(tstate, PyExc_TypeError, "'async for' received an object from __aiter__ " "that does not implement __anext__: %.100s", @@ -2663,7 +2664,6 @@ unaryfunc getter = NULL; PyObject *next_iter = NULL; PyTypeObject *type = Py_TYPE(aiter); - if (PyAsyncGen_CheckExact(aiter)) { awaitable = type->tp_as_async->am_anext(aiter); if (awaitable == NULL) { @@ -2673,7 +2673,6 @@ if (type->tp_as_async != NULL){ getter = type->tp_as_async->am_anext; } - if (getter != NULL) { next_iter = (*getter)(aiter); if (next_iter == NULL) { @@ -2687,7 +2686,6 @@ type->tp_name); GOTO_ERROR(error); } - awaitable = _PyCoro_GetAwaitableIter(next_iter); if (awaitable == NULL) { _PyErr_FormatFromCause( @@ -2695,15 +2693,14 @@ "'async for' received an invalid object " "from __anext__: %.100s", Py_TYPE(next_iter)->tp_name); - Py_DECREF(next_iter); GOTO_ERROR(error); } else { Py_DECREF(next_iter); } } - STACK_GROW(1); - stack_pointer[-1] = awaitable; + stack_pointer[0] = awaitable; + stack_pointer += 1; DISPATCH(); } @@ -2715,13 +2712,10 @@ PyObject *iter; iterable = stack_pointer[-1]; iter = _PyCoro_GetAwaitableIter(iterable); - if (iter == NULL) { _PyEval_FormatAwaitableError(tstate, Py_TYPE(iterable), oparg); } - Py_DECREF(iterable); - if (iter != NULL && PyCoro_CheckExact(iter)) { PyObject *yf = _PyGen_yf((PyGenObject*)iter); if (yf != NULL) { @@ -2735,7 +2729,6 @@ /* The code below jumps to `error` if `iter` is NULL. */ } } - if (iter == NULL) goto pop_1_error; stack_pointer[-1] = iter; DISPATCH(); @@ -2768,8 +2761,8 @@ if (len_i < 0) goto error; len_o = PyLong_FromSsize_t(len_i); if (len_o == NULL) goto error; - STACK_GROW(1); - stack_pointer[-1] = len_o; + stack_pointer[0] = len_o; + stack_pointer += 1; DISPATCH(); } @@ -2819,8 +2812,8 @@ PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); res = import_from(tstate, from, name); if (res == NULL) goto error; - STACK_GROW(1); - stack_pointer[-1] = res; + stack_pointer[0] = res; + stack_pointer += 1; DISPATCH(); } @@ -2839,8 +2832,8 @@ Py_DECREF(level); Py_DECREF(fromlist); if (res == NULL) goto pop_2_error; - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } @@ -2852,10 +2845,10 @@ int total_args = oparg + is_meth; PyObject *function = PEEK(oparg + 2); PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : PEEK(total_args); + &_PyInstrumentation_MISSING : PEEK(total_args); int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_CALL, - frame, this_instr, function, arg); + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, function, arg); if (err) goto error; INCREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); GO_TO_INSTRUCTION(CALL); @@ -2876,10 +2869,10 @@ int total_args = oparg + is_meth; PyObject *function = PEEK(oparg + 3); PyObject *arg = total_args == 0 ? &_PyInstrumentation_MISSING - : PEEK(total_args + 1); + : PEEK(total_args + 1); int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_CALL, - frame, this_instr, function, arg); + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, function, arg); if (err) goto error; GO_TO_INSTRUCTION(CALL_KW); } @@ -2904,7 +2897,7 @@ } Py_DECREF(receiver); Py_DECREF(value); - STACK_SHRINK(2); + stack_pointer += -2; DISPATCH(); } @@ -2925,8 +2918,8 @@ PyErr_SetRaisedException(NULL); } Py_DECREF(receiver); - STACK_SHRINK(1); - stack_pointer[-1] = value; + stack_pointer[-2] = value; + stack_pointer += -1; DISPATCH(); } @@ -3094,7 +3087,7 @@ } _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_call_instrumentation( - tstate, oparg > 0, frame, this_instr); + tstate, oparg > 0, frame, this_instr); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) goto error; if (frame->instr_ptr != this_instr) { @@ -3112,8 +3105,8 @@ INSTRUCTION_STATS(INSTRUMENTED_RETURN_CONST); PyObject *retval = GETITEM(FRAME_CO_CONSTS, oparg); int err = _Py_call_instrumentation_arg( - tstate, PY_MONITORING_EVENT_PY_RETURN, - frame, this_instr, retval); + tstate, PY_MONITORING_EVENT_PY_RETURN, + frame, this_instr, retval); if (err) GOTO_ERROR(error); Py_INCREF(retval); assert(EMPTY()); @@ -3136,8 +3129,8 @@ PyObject *retval; retval = stack_pointer[-1]; int err = _Py_call_instrumentation_arg( - tstate, PY_MONITORING_EVENT_PY_RETURN, - frame, this_instr, retval); + tstate, PY_MONITORING_EVENT_PY_RETURN, + frame, this_instr, retval); if (err) GOTO_ERROR(error); STACK_SHRINK(1); assert(EMPTY()); @@ -3167,8 +3160,8 @@ gen->gi_frame_state = FRAME_SUSPENDED + oparg; _PyFrame_SetStackPointer(frame, stack_pointer - 1); int err = _Py_call_instrumentation_arg( - tstate, PY_MONITORING_EVENT_PY_YIELD, - frame, this_instr, retval); + tstate, PY_MONITORING_EVENT_PY_YIELD, + frame, this_instr, retval); if (err) GOTO_ERROR(error); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; @@ -3211,8 +3204,8 @@ Py_DECREF(left); Py_DECREF(right); b = res ? Py_True : Py_False; - STACK_SHRINK(1); - stack_pointer[-1] = b; + stack_pointer[-2] = b; + stack_pointer += -1; DISPATCH(); } @@ -3286,7 +3279,7 @@ v = stack_pointer[-1]; list = stack_pointer[-2 - (oparg-1)]; if (_PyList_AppendTakeRef((PyListObject *)list, v) < 0) goto pop_1_error; - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -3301,19 +3294,19 @@ PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable); if (none_val == NULL) { if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError) && - (Py_TYPE(iterable)->tp_iter == NULL && !PySequence_Check(iterable))) + (Py_TYPE(iterable)->tp_iter == NULL && !PySequence_Check(iterable))) { _PyErr_Clear(tstate); _PyErr_Format(tstate, PyExc_TypeError, - "Value after * must be an iterable, not %.200s", - Py_TYPE(iterable)->tp_name); + "Value after * must be an iterable, not %.200s", + Py_TYPE(iterable)->tp_name); } Py_DECREF(iterable); if (true) goto pop_1_error; } assert(Py_IsNone(none_val)); Py_DECREF(iterable); - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -3323,8 +3316,8 @@ INSTRUCTION_STATS(LOAD_ASSERTION_ERROR); PyObject *value; value = Py_NewRef(PyExc_AssertionError); - STACK_GROW(1); - stack_pointer[-1] = value; + stack_pointer[0] = value; + stack_pointer += 1; DISPATCH(); } @@ -3334,7 +3327,6 @@ INSTRUCTION_STATS(LOAD_ATTR); PREDICTED(LOAD_ATTR); _Py_CODEUNIT *this_instr = next_instr - 10; - static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); PyObject *owner; PyObject *attr; PyObject *self_or_null = NULL; @@ -3374,7 +3366,7 @@ the second element of the stack to NULL, to signal CALL that it's not a method call. NULL | meth | arg1 | ... | argN - */ + */ Py_DECREF(owner); if (attr == NULL) goto pop_1_error; self_or_null = NULL; @@ -3387,9 +3379,9 @@ if (attr == NULL) goto pop_1_error; } } - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = self_or_null; } + stack_pointer[-1] = attr; + if (oparg & 1) stack_pointer[0] = self_or_null; + stack_pointer += ((oparg & 1)); DISPATCH(); } @@ -3397,9 +3389,11 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_CLASS); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); PyObject *owner; PyObject *attr; PyObject *null = NULL; + /* Skip 1 cache entry */ // _CHECK_ATTR_CLASS owner = stack_pointer[-1]; { @@ -3408,6 +3402,7 @@ assert(type_version != 0); DEOPT_IF(((PyTypeObject *)owner)->tp_version_tag != type_version, LOAD_ATTR); } + /* Skip 2 cache entries */ // _LOAD_ATTR_CLASS { PyObject *descr = read_obj(&this_instr[6].cache); @@ -3417,9 +3412,9 @@ null = NULL; Py_DECREF(owner); } - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + stack_pointer[-1] = attr; + if (oparg & 1) stack_pointer[0] = null; + stack_pointer += ((oparg & 1)); DISPATCH(); } @@ -3427,6 +3422,7 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); PyObject *owner; owner = stack_pointer[-1]; uint32_t type_version = read_u32(&this_instr[2].cache); @@ -3445,7 +3441,6 @@ assert(code->co_argcount == 2); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); Py_INCREF(f); _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 2); @@ -3461,9 +3456,11 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_INSTANCE_VALUE); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); PyObject *owner; PyObject *attr; PyObject *null = NULL; + /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; { @@ -3490,9 +3487,10 @@ null = NULL; Py_DECREF(owner); } - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + /* Skip 5 cache entries */ + stack_pointer[-1] = attr; + if (oparg & 1) stack_pointer[0] = null; + stack_pointer += ((oparg & 1)); DISPATCH(); } @@ -3500,9 +3498,11 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_METHOD_LAZY_DICT); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); PyObject *owner; PyObject *attr; - PyObject *self; + PyObject *self = NULL; + /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; { @@ -3519,6 +3519,7 @@ /* This object has a __dict__, just not yet created */ DEOPT_IF(dict != NULL, LOAD_ATTR); } + /* Skip 2 cache entries */ // _LOAD_ATTR_METHOD_LAZY_DICT { PyObject *descr = read_obj(&this_instr[6].cache); @@ -3529,9 +3530,9 @@ attr = Py_NewRef(descr); self = owner; } - STACK_GROW(1); - stack_pointer[-2] = attr; - stack_pointer[-1] = self; + stack_pointer[-1] = attr; + if (1) stack_pointer[0] = self; + stack_pointer += (((1) ? 1 : 0)); DISPATCH(); } @@ -3539,9 +3540,11 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_METHOD_NO_DICT); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); PyObject *owner; PyObject *attr; - PyObject *self; + PyObject *self = NULL; + /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; { @@ -3550,6 +3553,7 @@ assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); } + /* Skip 2 cache entries */ // _LOAD_ATTR_METHOD_NO_DICT { PyObject *descr = read_obj(&this_instr[6].cache); @@ -3561,9 +3565,9 @@ attr = Py_NewRef(descr); self = owner; } - STACK_GROW(1); - stack_pointer[-2] = attr; - stack_pointer[-1] = self; + stack_pointer[-1] = attr; + if (1) stack_pointer[0] = self; + stack_pointer += (((1) ? 1 : 0)); DISPATCH(); } @@ -3571,9 +3575,11 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_METHOD_WITH_VALUES); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); PyObject *owner; PyObject *attr; - PyObject *self; + PyObject *self = NULL; + /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; { @@ -3606,9 +3612,9 @@ assert(_PyType_HasFeature(Py_TYPE(attr), Py_TPFLAGS_METHOD_DESCRIPTOR)); self = owner; } - STACK_GROW(1); - stack_pointer[-2] = attr; - stack_pointer[-1] = self; + stack_pointer[-1] = attr; + if (1) stack_pointer[0] = self; + stack_pointer += (((1) ? 1 : 0)); DISPATCH(); } @@ -3616,9 +3622,11 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_MODULE); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); PyObject *owner; PyObject *attr; PyObject *null = NULL; + /* Skip 1 cache entry */ // _CHECK_ATTR_MODULE owner = stack_pointer[-1]; { @@ -3642,9 +3650,10 @@ null = NULL; Py_DECREF(owner); } - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + /* Skip 5 cache entries */ + stack_pointer[-1] = attr; + if (oparg & 1) stack_pointer[0] = null; + stack_pointer += ((oparg & 1)); DISPATCH(); } @@ -3652,8 +3661,10 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_NONDESCRIPTOR_NO_DICT); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); PyObject *owner; PyObject *attr; + /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; { @@ -3662,6 +3673,7 @@ assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); } + /* Skip 2 cache entries */ // _LOAD_ATTR_NONDESCRIPTOR_NO_DICT { PyObject *descr = read_obj(&this_instr[6].cache); @@ -3673,6 +3685,7 @@ attr = Py_NewRef(descr); } stack_pointer[-1] = attr; + stack_pointer += (((0) ? 1 : 0)); DISPATCH(); } @@ -3680,8 +3693,10 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); PyObject *owner; PyObject *attr; + /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; { @@ -3713,6 +3728,7 @@ attr = Py_NewRef(descr); } stack_pointer[-1] = attr; + stack_pointer += (((0) ? 1 : 0)); DISPATCH(); } @@ -3720,6 +3736,7 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_PROPERTY); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); PyObject *owner; owner = stack_pointer[-1]; uint32_t type_version = read_u32(&this_instr[2].cache); @@ -3727,7 +3744,6 @@ PyObject *fget = read_obj(&this_instr[6].cache); assert((oparg & 1) == 0); DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); - PyTypeObject *cls = Py_TYPE(owner); assert(type_version != 0); DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR); @@ -3752,9 +3768,11 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_SLOT); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); PyObject *owner; PyObject *attr; PyObject *null = NULL; + /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; { @@ -3774,9 +3792,10 @@ null = NULL; Py_DECREF(owner); } - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + /* Skip 5 cache entries */ + stack_pointer[-1] = attr; + if (oparg & 1) stack_pointer[0] = null; + stack_pointer += ((oparg & 1)); DISPATCH(); } @@ -3784,9 +3803,11 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_WITH_HINT); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); PyObject *owner; PyObject *attr; PyObject *null = NULL; + /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; { @@ -3827,9 +3848,10 @@ null = NULL; Py_DECREF(owner); } - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + /* Skip 5 cache entries */ + stack_pointer[-1] = attr; + if (oparg & 1) stack_pointer[0] = null; + stack_pointer += ((oparg & 1)); DISPATCH(); } @@ -3844,8 +3866,8 @@ "__build_class__ not found"); if (true) goto error; } - STACK_GROW(1); - stack_pointer[-1] = bc; + stack_pointer[0] = bc; + stack_pointer += 1; DISPATCH(); } @@ -3856,8 +3878,8 @@ PyObject *value; value = GETITEM(FRAME_CO_CONSTS, oparg); Py_INCREF(value); - STACK_GROW(1); - stack_pointer[-1] = value; + stack_pointer[0] = value; + stack_pointer += 1; DISPATCH(); } @@ -3873,8 +3895,8 @@ if (true) goto error; } Py_INCREF(value); - STACK_GROW(1); - stack_pointer[-1] = value; + stack_pointer[0] = value; + stack_pointer += 1; DISPATCH(); } @@ -3886,8 +3908,8 @@ value = GETLOCAL(oparg); assert(value != NULL); Py_INCREF(value); - STACK_GROW(1); - stack_pointer[-1] = value; + stack_pointer[0] = value; + stack_pointer += 1; DISPATCH(); } @@ -3899,8 +3921,8 @@ value = GETLOCAL(oparg); // do not use SETLOCAL here, it decrefs the old value GETLOCAL(oparg) = NULL; - STACK_GROW(1); - stack_pointer[-1] = value; + stack_pointer[0] = value; + stack_pointer += 1; DISPATCH(); } @@ -3912,8 +3934,8 @@ value = GETLOCAL(oparg); if (value == NULL) goto unbound_local_error; Py_INCREF(value); - STACK_GROW(1); - stack_pointer[-1] = value; + stack_pointer[0] = value; + stack_pointer += 1; DISPATCH(); } @@ -3929,9 +3951,9 @@ value2 = GETLOCAL(oparg2); Py_INCREF(value1); Py_INCREF(value2); - STACK_GROW(2); - stack_pointer[-2] = value1; - stack_pointer[-1] = value2; + stack_pointer[0] = value1; + stack_pointer[1] = value2; + stack_pointer += 2; DISPATCH(); } @@ -3984,8 +4006,8 @@ } if (v == NULL) { _PyEval_FormatExcCheckArg( - tstate, PyExc_NameError, - NAME_ERROR_MSG, name); + tstate, PyExc_NameError, + NAME_ERROR_MSG, name); GOTO_ERROR(error); } } @@ -4001,7 +4023,6 @@ INSTRUCTION_STATS(LOAD_GLOBAL); PREDICTED(LOAD_GLOBAL); _Py_CODEUNIT *this_instr = next_instr - 5; - static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size"); PyObject *res; PyObject *null = NULL; // _SPECIALIZE_LOAD_GLOBAL @@ -4026,14 +4047,14 @@ && PyDict_CheckExact(BUILTINS())) { res = _PyDict_LoadGlobal((PyDictObject *)GLOBALS(), - (PyDictObject *)BUILTINS(), - name); + (PyDictObject *)BUILTINS(), + name); if (res == NULL) { if (!_PyErr_Occurred(tstate)) { /* _PyDict_LoadGlobal() returns NULL without raising * an exception if the key doesn't exist */ _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, name); + NAME_ERROR_MSG, name); } if (true) goto error; } @@ -4048,18 +4069,17 @@ if (PyMapping_GetOptionalItem(BUILTINS(), name, &res) < 0) goto error; if (res == NULL) { _PyEval_FormatExcCheckArg( - tstate, PyExc_NameError, - NAME_ERROR_MSG, name); + tstate, PyExc_NameError, + NAME_ERROR_MSG, name); if (true) goto error; } } } null = NULL; } - STACK_GROW(1); - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = res; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + stack_pointer[0] = res; + if (oparg & 1) stack_pointer[1] = null; + stack_pointer += 1 + ((oparg & 1)); DISPATCH(); } @@ -4067,8 +4087,10 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 5; INSTRUCTION_STATS(LOAD_GLOBAL_BUILTIN); + static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size"); PyObject *res; PyObject *null = NULL; + /* Skip 1 cache entry */ // _GUARD_GLOBALS_VERSION { uint16_t version = read_u16(&this_instr[2].cache); @@ -4096,10 +4118,9 @@ STAT_INC(LOAD_GLOBAL, hit); null = NULL; } - STACK_GROW(1); - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = res; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + stack_pointer[0] = res; + if (oparg & 1) stack_pointer[1] = null; + stack_pointer += 1 + ((oparg & 1)); DISPATCH(); } @@ -4107,8 +4128,10 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 5; INSTRUCTION_STATS(LOAD_GLOBAL_MODULE); + static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size"); PyObject *res; PyObject *null = NULL; + /* Skip 1 cache entry */ // _GUARD_GLOBALS_VERSION { uint16_t version = read_u16(&this_instr[2].cache); @@ -4117,6 +4140,7 @@ DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL); assert(DK_IS_UNICODE(dict->ma_keys)); } + /* Skip 1 cache entry */ // _LOAD_GLOBAL_MODULE { uint16_t index = read_u16(&this_instr[4].cache); @@ -4128,10 +4152,9 @@ STAT_INC(LOAD_GLOBAL, hit); null = NULL; } - STACK_GROW(1); - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = res; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + stack_pointer[0] = res; + if (oparg & 1) stack_pointer[1] = null; + stack_pointer += 1 + ((oparg & 1)); DISPATCH(); } @@ -4147,8 +4170,8 @@ if (true) goto error; } Py_INCREF(locals); - STACK_GROW(1); - stack_pointer[-1] = locals; + stack_pointer[0] = locals; + stack_pointer += 1; DISPATCH(); } @@ -4177,14 +4200,14 @@ } if (v == NULL) { _PyEval_FormatExcCheckArg( - tstate, PyExc_NameError, - NAME_ERROR_MSG, name); + tstate, PyExc_NameError, + NAME_ERROR_MSG, name); GOTO_ERROR(error); } } } - STACK_GROW(1); - stack_pointer[-1] = v; + stack_pointer[0] = v; + stack_pointer += 1; DISPATCH(); } @@ -4194,7 +4217,6 @@ INSTRUCTION_STATS(LOAD_SUPER_ATTR); PREDICTED(LOAD_SUPER_ATTR); _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR == 1, "incorrect cache size"); PyObject *class; PyObject *global_super; PyObject *self; @@ -4224,8 +4246,8 @@ if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_CALL, - frame, this_instr, global_super, arg); + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, global_super, arg); if (err) goto pop_3_error; } // we make no attempt to optimize here; specializations should @@ -4258,10 +4280,9 @@ if (attr == NULL) goto pop_3_error; null = NULL; } - STACK_SHRINK(2); - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + stack_pointer[-3] = attr; + if (oparg & 1) stack_pointer[-2] = null; + stack_pointer += -2 + ((oparg & 1)); DISPATCH(); } @@ -4269,6 +4290,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(LOAD_SUPER_ATTR_ATTR); + static_assert(INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR == 1, "incorrect cache size"); PyObject *self; PyObject *class; PyObject *global_super; @@ -4286,8 +4308,8 @@ Py_DECREF(class); Py_DECREF(self); if (attr == NULL) goto pop_3_error; - STACK_SHRINK(2); - stack_pointer[-1] = attr; + stack_pointer[-3] = attr; + stack_pointer += -2 + (((0) ? 1 : 0)); DISPATCH(); } @@ -4295,6 +4317,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(LOAD_SUPER_ATTR_METHOD); + static_assert(INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR == 1, "incorrect cache size"); PyObject *self; PyObject *class; PyObject *global_super; @@ -4324,9 +4347,9 @@ Py_DECREF(self); self_or_null = NULL; } - STACK_SHRINK(1); - stack_pointer[-2] = attr; - stack_pointer[-1] = self_or_null; + stack_pointer[-3] = attr; + stack_pointer[-2] = self_or_null; + stack_pointer += -1; DISPATCH(); } @@ -4352,17 +4375,14 @@ PyObject *codeobj; PyObject *func; codeobj = stack_pointer[-1]; - PyFunctionObject *func_obj = (PyFunctionObject *) - PyFunction_New(codeobj, GLOBALS()); - + PyFunction_New(codeobj, GLOBALS()); Py_DECREF(codeobj); if (func_obj == NULL) { GOTO_ERROR(error); } - _PyFunction_SetVersion( - func_obj, ((PyCodeObject *)codeobj)->co_version); + func_obj, ((PyCodeObject *)codeobj)->co_version); func = (PyObject *)func_obj; stack_pointer[-1] = func; DISPATCH(); @@ -4382,7 +4402,7 @@ /* dict[key] = value */ // Do not DECREF INPUTS because the function steals the references if (_PyDict_SetItem_Take2((PyDictObject *)dict, key, value) != 0) goto pop_2_error; - STACK_SHRINK(2); + stack_pointer += -2; DISPATCH(); } @@ -4409,10 +4429,11 @@ } else { if (_PyErr_Occurred(tstate)) goto pop_3_error; + // Error! attrs = Py_None; // Failure! } - STACK_SHRINK(2); - stack_pointer[-1] = attrs; + stack_pointer[-3] = attrs; + stack_pointer += -2; DISPATCH(); } @@ -4428,8 +4449,8 @@ // On successful match, PUSH(values). Otherwise, PUSH(None). values_or_none = _PyEval_MatchKeys(tstate, subject, keys); if (values_or_none == NULL) goto error; - STACK_GROW(1); - stack_pointer[-1] = values_or_none; + stack_pointer[0] = values_or_none; + stack_pointer += 1; DISPATCH(); } @@ -4442,8 +4463,8 @@ subject = stack_pointer[-1]; int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; res = match ? Py_True : Py_False; - STACK_GROW(1); - stack_pointer[-1] = res; + stack_pointer[0] = res; + stack_pointer += 1; DISPATCH(); } @@ -4456,8 +4477,8 @@ subject = stack_pointer[-1]; int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; res = match ? Py_True : Py_False; - STACK_GROW(1); - stack_pointer[-1] = res; + stack_pointer[0] = res; + stack_pointer += 1; DISPATCH(); } @@ -4476,7 +4497,7 @@ exc_value = stack_pointer[-1]; _PyErr_StackItem *exc_info = tstate->exc_info; Py_XSETREF(exc_info->exc_value, exc_value); - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -4492,7 +4513,7 @@ this_instr[1].cache = (this_instr[1].cache << 1) | flag; #endif JUMPBY(oparg * flag); - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -4524,7 +4545,7 @@ #endif JUMPBY(oparg * flag); } - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -4556,7 +4577,7 @@ #endif JUMPBY(oparg * flag); } - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -4572,7 +4593,7 @@ this_instr[1].cache = (this_instr[1].cache << 1) | flag; #endif JUMPBY(oparg * flag); - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -4583,7 +4604,7 @@ PyObject *value; value = stack_pointer[-1]; Py_DECREF(value); - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -4603,9 +4624,9 @@ } assert(PyExceptionInstance_Check(new_exc)); exc_info->exc_value = Py_NewRef(new_exc); - STACK_GROW(1); - stack_pointer[-2] = prev_exc; - stack_pointer[-1] = new_exc; + stack_pointer[-1] = prev_exc; + stack_pointer[0] = new_exc; + stack_pointer += 1; DISPATCH(); } @@ -4615,8 +4636,8 @@ INSTRUCTION_STATS(PUSH_NULL); PyObject *res; res = NULL; - STACK_GROW(1); - stack_pointer[-1] = res; + stack_pointer[0] = res; + stack_pointer += 1; DISPATCH(); } @@ -4625,29 +4646,29 @@ next_instr += 1; INSTRUCTION_STATS(RAISE_VARARGS); PyObject **args; - args = stack_pointer - oparg; + args = &stack_pointer[-oparg]; TIER_ONE_ONLY PyObject *cause = NULL, *exc = NULL; switch (oparg) { - case 2: + case 2: cause = args[1]; /* fall through */ - case 1: + case 1: exc = args[0]; /* fall through */ - case 0: + case 0: if (do_raise(tstate, exc, cause)) { assert(oparg == 0); monitor_reraise(tstate, frame, this_instr); goto exception_unwind; } break; - default: + default: _PyErr_SetString(tstate, PyExc_SystemError, "bad RAISE_VARARGS oparg"); break; } - if (true) { STACK_SHRINK(oparg); goto error; } + if (true) { stack_pointer += -oparg; goto error; } } TARGET(RERAISE) { @@ -4657,7 +4678,7 @@ PyObject *exc; PyObject **values; exc = stack_pointer[-1]; - values = stack_pointer - 1 - oparg; + values = &stack_pointer[-1 - oparg]; TIER_ONE_ONLY assert(oparg >= 0 && oparg <= 2); if (oparg) { @@ -4693,12 +4714,11 @@ INSTRUCTION_STATS(RESUME); PREDICTED(RESUME); _Py_CODEUNIT *this_instr = next_instr - 1; - static_assert(0 == 0, "incorrect cache size"); TIER_ONE_ONLY assert(frame == tstate->current_frame); uintptr_t global_version = - _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker) & - ~_PY_EVAL_EVENTS_MASK; + _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker) & + ~_PY_EVAL_EVENTS_MASK; uintptr_t code_version = _PyFrame_GetCode(frame)->_co_instrumentation_version; assert((code_version & 255) == 0); if (code_version != global_version) { @@ -4719,10 +4739,11 @@ frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(RESUME_CHECK); -#if defined(__EMSCRIPTEN__) + static_assert(0 == 0, "incorrect cache size"); + #if defined(__EMSCRIPTEN__) DEOPT_IF(_Py_emscripten_signal_clock == 0, RESUME); _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; -#endif + #endif uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker); uintptr_t version = _PyFrame_GetCode(frame)->_co_instrumentation_version; assert((version & _PY_EVAL_EVENTS_MASK) == 0); @@ -4736,7 +4757,7 @@ INSTRUCTION_STATS(RETURN_CONST); PyObject *value; PyObject *retval; - // LOAD_CONST + // _LOAD_CONST { value = GETITEM(FRAME_CO_CONSTS, oparg); Py_INCREF(value); @@ -4744,11 +4765,11 @@ // _POP_FRAME retval = value; { - assert(EMPTY()); #if TIER_ONE assert(frame != &entry_frame); #endif - STORE_SP(); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(EMPTY()); _Py_LeaveRecursiveCallPy(tstate); // GH-99729: We need to unlink the frame *before* clearing it: _PyInterpreterFrame *dying = frame; @@ -4757,12 +4778,12 @@ _PyFrame_StackPush(frame, retval); LOAD_SP(); LOAD_IP(frame->return_offset); - #if LLTRACE && TIER_ONE + #if LLTRACE && TIER_ONE lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); if (lltrace < 0) { goto exit_unwind; } - #endif + #endif } DISPATCH(); } @@ -4801,12 +4822,12 @@ INSTRUCTION_STATS(RETURN_VALUE); PyObject *retval; retval = stack_pointer[-1]; - STACK_SHRINK(1); - assert(EMPTY()); #if TIER_ONE assert(frame != &entry_frame); #endif - STORE_SP(); + stack_pointer += -1; + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(EMPTY()); _Py_LeaveRecursiveCallPy(tstate); // GH-99729: We need to unlink the frame *before* clearing it: _PyInterpreterFrame *dying = frame; @@ -4815,12 +4836,12 @@ _PyFrame_StackPush(frame, retval); LOAD_SP(); LOAD_IP(frame->return_offset); -#if LLTRACE && TIER_ONE + #if LLTRACE && TIER_ONE lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); if (lltrace < 0) { goto exit_unwind; } -#endif + #endif DISPATCH(); } @@ -4830,7 +4851,6 @@ INSTRUCTION_STATS(SEND); PREDICTED(SEND); _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_SEND == 1, "incorrect cache size"); PyObject *receiver; PyObject *v; PyObject *retval; @@ -4897,6 +4917,7 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(SEND_GEN); + static_assert(INLINE_CACHE_ENTRIES_SEND == 1, "incorrect cache size"); PyObject *v; PyObject *receiver; v = stack_pointer[-1]; @@ -4955,7 +4976,7 @@ int err = PySet_Add(set, v); Py_DECREF(v); if (err) goto pop_1_error; - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -4971,28 +4992,28 @@ PyFunctionObject *func_obj = (PyFunctionObject *)func; switch(oparg) { case MAKE_FUNCTION_CLOSURE: - assert(func_obj->func_closure == NULL); - func_obj->func_closure = attr; - break; + assert(func_obj->func_closure == NULL); + func_obj->func_closure = attr; + break; case MAKE_FUNCTION_ANNOTATIONS: - assert(func_obj->func_annotations == NULL); - func_obj->func_annotations = attr; - break; + assert(func_obj->func_annotations == NULL); + func_obj->func_annotations = attr; + break; case MAKE_FUNCTION_KWDEFAULTS: - assert(PyDict_CheckExact(attr)); - assert(func_obj->func_kwdefaults == NULL); - func_obj->func_kwdefaults = attr; - break; + assert(PyDict_CheckExact(attr)); + assert(func_obj->func_kwdefaults == NULL); + func_obj->func_kwdefaults = attr; + break; case MAKE_FUNCTION_DEFAULTS: - assert(PyTuple_CheckExact(attr)); - assert(func_obj->func_defaults == NULL); - func_obj->func_defaults = attr; - break; + assert(PyTuple_CheckExact(attr)); + assert(func_obj->func_defaults == NULL); + func_obj->func_defaults = attr; + break; default: - Py_UNREACHABLE(); + Py_UNREACHABLE(); } - STACK_SHRINK(1); - stack_pointer[-1] = func; + stack_pointer[-2] = func; + stack_pointer += -1; DISPATCH(); } @@ -5007,7 +5028,7 @@ int err = _PySet_Update(set, iterable); Py_DECREF(iterable); if (err < 0) goto pop_1_error; - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -5017,7 +5038,6 @@ INSTRUCTION_STATS(STORE_ATTR); PREDICTED(STORE_ATTR); _Py_CODEUNIT *this_instr = next_instr - 5; - static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); PyObject *owner; PyObject *v; // _SPECIALIZE_STORE_ATTR @@ -5045,7 +5065,7 @@ Py_DECREF(owner); if (err) goto pop_2_error; } - STACK_SHRINK(2); + stack_pointer += -2; DISPATCH(); } @@ -5053,8 +5073,10 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 5; INSTRUCTION_STATS(STORE_ATTR_INSTANCE_VALUE); + static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); PyObject *owner; PyObject *value; + /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; { @@ -5086,7 +5108,7 @@ } Py_DECREF(owner); } - STACK_SHRINK(2); + stack_pointer += -2; DISPATCH(); } @@ -5094,8 +5116,10 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 5; INSTRUCTION_STATS(STORE_ATTR_SLOT); + static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); PyObject *owner; PyObject *value; + /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; { @@ -5115,7 +5139,7 @@ Py_XDECREF(old_value); Py_DECREF(owner); } - STACK_SHRINK(2); + stack_pointer += -2; DISPATCH(); } @@ -5123,6 +5147,7 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 5; INSTRUCTION_STATS(STORE_ATTR_WITH_HINT); + static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); PyObject *owner; PyObject *value; owner = stack_pointer[-1]; @@ -5167,7 +5192,7 @@ /* PEP 509 */ dict->ma_version_tag = new_version; Py_DECREF(owner); - STACK_SHRINK(2); + stack_pointer += -2; DISPATCH(); } @@ -5181,7 +5206,7 @@ PyObject *oldobj = PyCell_GET(cell); PyCell_SET(cell, v); Py_XDECREF(oldobj); - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -5192,7 +5217,7 @@ PyObject *value; value = stack_pointer[-1]; SETLOCAL(oparg, value); - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -5224,7 +5249,7 @@ uint32_t oparg2 = oparg & 15; SETLOCAL(oparg1, value1); SETLOCAL(oparg2, value2); - STACK_SHRINK(2); + stack_pointer += -2; DISPATCH(); } @@ -5238,7 +5263,7 @@ int err = PyDict_SetItem(GLOBALS(), name, v); Py_DECREF(v); if (err) goto pop_1_error; - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -5258,12 +5283,12 @@ if (true) goto pop_1_error; } if (PyDict_CheckExact(ns)) - err = PyDict_SetItem(ns, name, v); + err = PyDict_SetItem(ns, name, v); else - err = PyObject_SetItem(ns, name, v); + err = PyObject_SetItem(ns, name, v); Py_DECREF(v); if (err) goto pop_1_error; - STACK_SHRINK(1); + stack_pointer += -1; DISPATCH(); } @@ -5291,7 +5316,7 @@ Py_DECREF(v); Py_DECREF(container); if (err) goto pop_4_error; - STACK_SHRINK(4); + stack_pointer += -4; DISPATCH(); } @@ -5301,7 +5326,6 @@ INSTRUCTION_STATS(STORE_SUBSCR); PREDICTED(STORE_SUBSCR); _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_STORE_SUBSCR == 1, "incorrect cache size"); PyObject *sub; PyObject *container; PyObject *v; @@ -5331,7 +5355,7 @@ Py_DECREF(sub); if (err) goto pop_3_error; } - STACK_SHRINK(3); + stack_pointer += -3; DISPATCH(); } @@ -5339,6 +5363,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(STORE_SUBSCR_DICT); + static_assert(INLINE_CACHE_ENTRIES_STORE_SUBSCR == 1, "incorrect cache size"); PyObject *sub; PyObject *dict; PyObject *value; @@ -5350,7 +5375,7 @@ int err = _PyDict_SetItem_Take2((PyDictObject *)dict, sub, value); Py_DECREF(dict); if (err) goto pop_3_error; - STACK_SHRINK(3); + stack_pointer += -3; DISPATCH(); } @@ -5358,6 +5383,7 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(STORE_SUBSCR_LIST_INT); + static_assert(INLINE_CACHE_ENTRIES_STORE_SUBSCR == 1, "incorrect cache size"); PyObject *sub; PyObject *list; PyObject *value; @@ -5366,21 +5392,19 @@ value = stack_pointer[-3]; DEOPT_IF(!PyLong_CheckExact(sub), STORE_SUBSCR); DEOPT_IF(!PyList_CheckExact(list), STORE_SUBSCR); - // Ensure nonnegative, zero-or-one-digit ints. DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), STORE_SUBSCR); Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; // Ensure index < len(list) DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR); STAT_INC(STORE_SUBSCR, hit); - PyObject *old_value = PyList_GET_ITEM(list, index); PyList_SET_ITEM(list, index, value); assert(old_value != NULL); Py_DECREF(old_value); _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); Py_DECREF(list); - STACK_SHRINK(3); + stack_pointer += -3; DISPATCH(); } @@ -5404,7 +5428,6 @@ INSTRUCTION_STATS(TO_BOOL); PREDICTED(TO_BOOL); _Py_CODEUNIT *this_instr = next_instr - 4; - static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); PyObject *value; PyObject *res; // _SPECIALIZE_TO_BOOL @@ -5437,6 +5460,7 @@ _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(TO_BOOL_ALWAYS_TRUE); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); PyObject *value; PyObject *res; value = stack_pointer[-1]; @@ -5455,6 +5479,7 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(TO_BOOL_BOOL); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); PyObject *value; value = stack_pointer[-1]; DEOPT_IF(!PyBool_Check(value), TO_BOOL); @@ -5466,6 +5491,7 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(TO_BOOL_INT); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); PyObject *value; PyObject *res; value = stack_pointer[-1]; @@ -5487,6 +5513,7 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(TO_BOOL_LIST); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); PyObject *value; PyObject *res; value = stack_pointer[-1]; @@ -5502,6 +5529,7 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(TO_BOOL_NONE); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); PyObject *value; PyObject *res; value = stack_pointer[-1]; @@ -5517,6 +5545,7 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(TO_BOOL_STR); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); PyObject *value; PyObject *res; value = stack_pointer[-1]; @@ -5587,7 +5616,7 @@ int res = _PyEval_UnpackIterable(tstate, seq, oparg & 0xFF, oparg >> 8, top); Py_DECREF(seq); if (res == 0) goto pop_1_error; - STACK_GROW((oparg & 0xFF) + (oparg >> 8)); + stack_pointer += (oparg >> 8) + (oparg & 0xFF); DISPATCH(); } @@ -5597,7 +5626,6 @@ INSTRUCTION_STATS(UNPACK_SEQUENCE); PREDICTED(UNPACK_SEQUENCE); _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE == 1, "incorrect cache size"); PyObject *seq; // _SPECIALIZE_UNPACK_SEQUENCE seq = stack_pointer[-1]; @@ -5623,8 +5651,7 @@ Py_DECREF(seq); if (res == 0) goto pop_1_error; } - STACK_SHRINK(1); - STACK_GROW(oparg); + stack_pointer += -1 + oparg; DISPATCH(); } @@ -5632,10 +5659,11 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(UNPACK_SEQUENCE_LIST); + static_assert(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE == 1, "incorrect cache size"); PyObject *seq; PyObject **values; seq = stack_pointer[-1]; - values = stack_pointer - 1; + values = &stack_pointer[-1]; DEOPT_IF(!PyList_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyList_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); STAT_INC(UNPACK_SEQUENCE, hit); @@ -5644,8 +5672,7 @@ *values++ = Py_NewRef(items[i]); } Py_DECREF(seq); - STACK_SHRINK(1); - STACK_GROW(oparg); + stack_pointer += -1 + oparg; DISPATCH(); } @@ -5653,10 +5680,11 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(UNPACK_SEQUENCE_TUPLE); + static_assert(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE == 1, "incorrect cache size"); PyObject *seq; PyObject **values; seq = stack_pointer[-1]; - values = stack_pointer - 1; + values = &stack_pointer[-1]; DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyTuple_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); STAT_INC(UNPACK_SEQUENCE, hit); @@ -5665,8 +5693,7 @@ *values++ = Py_NewRef(items[i]); } Py_DECREF(seq); - STACK_SHRINK(1); - STACK_GROW(oparg); + stack_pointer += -1 + oparg; DISPATCH(); } @@ -5674,10 +5701,11 @@ frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(UNPACK_SEQUENCE_TWO_TUPLE); + static_assert(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE == 1, "incorrect cache size"); PyObject *seq; PyObject **values; seq = stack_pointer[-1]; - values = stack_pointer - 1; + values = &stack_pointer[-1]; DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE); assert(oparg == 2); @@ -5685,8 +5713,7 @@ values[0] = Py_NewRef(PyTuple_GET_ITEM(seq, 1)); values[1] = Py_NewRef(PyTuple_GET_ITEM(seq, 0)); Py_DECREF(seq); - STACK_SHRINK(1); - STACK_GROW(oparg); + stack_pointer += -1 + oparg; DISPATCH(); } @@ -5708,9 +5735,8 @@ - exit_func: FOURTH = the context.__exit__ bound method We call FOURTH(type(TOP), TOP, GetTraceback(TOP)). Then we push the __exit__ return value. - */ + */ PyObject *exc, *tb; - assert(val && PyExceptionInstance_Check(val)); exc = PyExceptionInstance_Class(val); tb = PyException_GetTraceback(val); @@ -5724,10 +5750,10 @@ (void)lasti; // Shut up compiler warning if asserts are off PyObject *stack[4] = {NULL, exc, val, tb}; res = PyObject_Vectorcall(exit_func, stack + 1, - 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); + 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); if (res == NULL) goto error; - STACK_GROW(1); - stack_pointer[-1] = res; + stack_pointer[0] = res; + stack_pointer += 1; DISPATCH(); } @@ -5759,5 +5785,4 @@ LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); goto resume_frame; } - #undef TIER_ONE diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py new file mode 100644 index 00000000000000..027f9861a1c0eb --- /dev/null +++ b/Tools/cases_generator/analyzer.py @@ -0,0 +1,456 @@ +from dataclasses import dataclass +import lexer +import parser +from typing import Optional + + +@dataclass +class Properties: + escapes: bool + infallible: bool + deopts: bool + oparg: bool + jumps: bool + ends_with_eval_breaker: bool + needs_this: bool + always_exits: bool + stores_sp: bool + + def dump(self, indent: str) -> None: + print(indent, end="") + text = ", ".join([f"{key}: {value}" for (key, value) in self.__dict__.items()]) + print(indent, text, sep="") + + @staticmethod + def from_list(properties: list["Properties"]) -> "Properties": + return Properties( + escapes=any(p.escapes for p in properties), + infallible=all(p.infallible for p in properties), + deopts=any(p.deopts for p in properties), + oparg=any(p.oparg for p in properties), + jumps=any(p.jumps for p in properties), + ends_with_eval_breaker=any(p.ends_with_eval_breaker for p in properties), + needs_this=any(p.needs_this for p in properties), + always_exits=any(p.always_exits for p in properties), + stores_sp=any(p.stores_sp for p in properties), + ) + + +SKIP_PROPERTIES = Properties( + escapes=False, + infallible=True, + deopts=False, + oparg=False, + jumps=False, + ends_with_eval_breaker=False, + needs_this=False, + always_exits=False, + stores_sp=False, +) + + +@dataclass +class Skip: + "Unused cache entry" + size: int + + @property + def name(self) -> str: + return f"unused/{self.size}" + + @property + def properties(self) -> Properties: + return SKIP_PROPERTIES + + +@dataclass +class StackItem: + name: str + type: str | None + condition: str | None + size: str + peek: bool = False + + def __str__(self) -> str: + cond = f" if ({self.condition})" if self.condition else "" + size = f"[{self.size}]" if self.size != "1" else "" + type = "" if self.type is None else f"{self.type} " + return f"{type}{self.name}{size}{cond} {self.peek}" + + def is_array(self) -> bool: + return self.type == "PyObject **" + + +@dataclass +class StackEffect: + inputs: list[StackItem] + outputs: list[StackItem] + + def __str__(self) -> str: + return f"({', '.join([str(i) for i in self.inputs])} -- {', '.join([str(i) for i in self.outputs])})" + + +@dataclass +class CacheEntry: + name: str + size: int + + def __str__(self) -> str: + return f"{self.name}/{self.size}" + + +@dataclass +class Uop: + name: str + context: parser.Context | None + annotations: list[str] + stack: StackEffect + caches: list[CacheEntry] + body: list[lexer.Token] + properties: Properties + _size: int = -1 + + def dump(self, indent: str) -> None: + print( + indent, self.name, ", ".join(self.annotations) if self.annotations else "" + ) + print(indent, self.stack, ", ".join([str(c) for c in self.caches])) + self.properties.dump(" " + indent) + + @property + def size(self) -> int: + if self._size < 0: + self._size = sum(c.size for c in self.caches) + return self._size + + +Part = Uop | Skip + + +@dataclass +class Instruction: + name: str + parts: list[Part] + _properties: Properties | None + is_target: bool = False + family: Optional["Family"] = None + + @property + def properties(self) -> Properties: + if self._properties is None: + self._properties = self._compute_properties() + return self._properties + + def _compute_properties(self) -> Properties: + return Properties.from_list([part.properties for part in self.parts]) + + def dump(self, indent: str) -> None: + print(indent, self.name, "=", ", ".join([part.name for part in self.parts])) + self.properties.dump(" " + indent) + + @property + def size(self) -> int: + return 1 + sum(part.size for part in self.parts) + + +@dataclass +class PseudoInstruction: + name: str + targets: list[Instruction] + flags: list[str] + + def dump(self, indent: str) -> None: + print(indent, self.name, "->", " or ".join([t.name for t in self.targets])) + + +@dataclass +class Family: + name: str + size: str + members: list[Instruction] + + def dump(self, indent: str) -> None: + print(indent, self.name, "= ", ", ".join([m.name for m in self.members])) + + +@dataclass +class Analysis: + instructions: dict[str, Instruction] + uops: dict[str, Uop] + families: dict[str, Family] + pseudos: dict[str, PseudoInstruction] + + +def analysis_error(message: str, tkn: lexer.Token) -> SyntaxError: + # To do -- support file and line output + # Construct a SyntaxError instance from message and token + return lexer.make_syntax_error(message, "", tkn.line, tkn.column, "") + + +def override_error( + name: str, + context: parser.Context | None, + prev_context: parser.Context | None, + token: lexer.Token, +) -> SyntaxError: + return analysis_error( + f"Duplicate definition of '{name}' @ {context} " + f"previous definition @ {prev_context}", + token, + ) + + +def convert_stack_item(item: parser.StackEffect) -> StackItem: + return StackItem(item.name, item.type, item.cond, (item.size or "1")) + + +def analyze_stack(op: parser.InstDef) -> StackEffect: + inputs: list[StackItem] = [ + convert_stack_item(i) for i in op.inputs if isinstance(i, parser.StackEffect) + ] + outputs: list[StackItem] = [convert_stack_item(i) for i in op.outputs] + for input, output in zip(inputs, outputs): + if input.name == output.name: + input.peek = output.peek = True + return StackEffect(inputs, outputs) + + +def analyze_caches(op: parser.InstDef) -> list[CacheEntry]: + caches: list[parser.CacheEffect] = [ + i for i in op.inputs if isinstance(i, parser.CacheEffect) + ] + return [CacheEntry(i.name, int(i.size)) for i in caches] + + +def variable_used(node: parser.InstDef, name: str) -> bool: + """Determine whether a variable with a given name is used in a node.""" + return any( + token.kind == "IDENTIFIER" and token.text == name for token in node.tokens + ) + + +def is_infallible(op: parser.InstDef) -> bool: + return not ( + variable_used(op, "ERROR_IF") + or variable_used(op, "error") + or variable_used(op, "pop_1_error") + or variable_used(op, "exception_unwind") + or variable_used(op, "resume_with_error") + ) + + +from flags import makes_escaping_api_call + +EXITS = { + "DISPATCH", + "GO_TO_INSTRUCTION", + "Py_UNREACHABLE", + "DISPATCH_INLINED", + "DISPATCH_GOTO", +} + + +def eval_breaker_at_end(op: parser.InstDef) -> bool: + return op.tokens[-5].text == "CHECK_EVAL_BREAKER" + + +def always_exits(op: parser.InstDef) -> bool: + depth = 0 + tkn_iter = iter(op.tokens) + for tkn in tkn_iter: + if tkn.kind == "LBRACE": + depth += 1 + elif tkn.kind == "RBRACE": + depth -= 1 + elif depth > 1: + continue + elif tkn.kind == "GOTO" or tkn.kind == "RETURN": + return True + elif tkn.kind == "KEYWORD": + if tkn.text in EXITS: + return True + elif tkn.kind == "IDENTIFIER": + if tkn.text in EXITS: + return True + if tkn.text == "DEOPT_IF" or tkn.text == "ERROR_IF": + next(tkn_iter) # '(' + t = next(tkn_iter) + if t.text == "true": + return True + return False + + +def compute_properties(op: parser.InstDef) -> Properties: + return Properties( + escapes=makes_escaping_api_call(op), + infallible=is_infallible(op), + deopts=variable_used(op, "DEOPT_IF"), + oparg=variable_used(op, "oparg"), + jumps=variable_used(op, "JUMPBY"), + ends_with_eval_breaker=eval_breaker_at_end(op), + needs_this=variable_used(op, "this_instr"), + always_exits=always_exits(op), + stores_sp=variable_used(op, "STORE_SP"), + ) + + +def make_uop(name: str, op: parser.InstDef) -> Uop: + return Uop( + name=name, + context=op.context, + annotations=op.annotations, + stack=analyze_stack(op), + caches=analyze_caches(op), + body=op.block.tokens, + properties=compute_properties(op), + ) + + +def add_op(op: parser.InstDef, uops: dict[str, Uop]) -> None: + assert op.kind == "op" + if op.name in uops: + if "override" not in op.annotations: + raise override_error( + op.name, op.context, uops[op.name].context, op.tokens[0] + ) + uops[op.name] = make_uop(op.name, op) + + +def add_instruction( + name: str, parts: list[Part], instructions: dict[str, Instruction] +) -> None: + instructions[name] = Instruction(name, parts, None) + + +def desugar_inst( + inst: parser.InstDef, instructions: dict[str, Instruction], uops: dict[str, Uop] +) -> None: + assert inst.kind == "inst" + name = inst.name + uop = make_uop("_" + inst.name, inst) + uops[inst.name] = uop + add_instruction(name, [uop], instructions) + + +def add_macro( + macro: parser.Macro, instructions: dict[str, Instruction], uops: dict[str, Uop] +) -> None: + parts: list[Uop | Skip] = [] + for part in macro.uops: + match part: + case parser.OpName(): + if part.name not in uops: + analysis_error(f"No Uop named {part.name}", macro.tokens[0]) + parts.append(uops[part.name]) + case parser.CacheEffect(): + parts.append(Skip(part.size)) + case _: + assert False + assert parts + add_instruction(macro.name, parts, instructions) + + +def add_family( + pfamily: parser.Family, + instructions: dict[str, Instruction], + families: dict[str, Family], +) -> None: + family = Family( + pfamily.name, + pfamily.size, + [instructions[member_name] for member_name in pfamily.members], + ) + for member in family.members: + member.family = family + # The head of the family is an implicit jump target for DEOPTs + instructions[family.name].is_target = True + families[family.name] = family + + +def add_pseudo( + pseudo: parser.Pseudo, + instructions: dict[str, Instruction], + pseudos: dict[str, PseudoInstruction], +) -> None: + pseudos[pseudo.name] = PseudoInstruction( + pseudo.name, + [instructions[target] for target in pseudo.targets], + pseudo.flags, + ) + + +def analyze_forest(forest: list[parser.AstNode]) -> Analysis: + instructions: dict[str, Instruction] = {} + uops: dict[str, Uop] = {} + families: dict[str, Family] = {} + pseudos: dict[str, PseudoInstruction] = {} + for node in forest: + match node: + case parser.InstDef(name): + if node.kind == "inst": + desugar_inst(node, instructions, uops) + else: + assert node.kind == "op" + add_op(node, uops) + case parser.Macro(): + pass + case parser.Family(): + pass + case parser.Pseudo(): + pass + case _: + assert False + for node in forest: + if isinstance(node, parser.Macro): + add_macro(node, instructions, uops) + for node in forest: + match node: + case parser.Family(): + add_family(node, instructions, families) + case parser.Pseudo(): + add_pseudo(node, instructions, pseudos) + case _: + pass + for uop in uops.values(): + tkn_iter = iter(uop.body) + for tkn in tkn_iter: + if tkn.kind == "IDENTIFIER" and tkn.text == "GO_TO_INSTRUCTION": + if next(tkn_iter).kind != "LPAREN": + continue + target = next(tkn_iter) + if target.kind != "IDENTIFIER": + continue + if target.text in instructions: + instructions[target.text].is_target = True + # Hack + instructions["BINARY_OP_INPLACE_ADD_UNICODE"].family = families["BINARY_OP"] + return Analysis(instructions, uops, families, pseudos) + + +def analyze_files(filenames: list[str]) -> Analysis: + return analyze_forest(parser.parse_files(filenames)) + + +def dump_analysis(analysis: Analysis) -> None: + print("Uops:") + for u in analysis.uops.values(): + u.dump(" ") + print("Instructions:") + for i in analysis.instructions.values(): + i.dump(" ") + print("Families:") + for f in analysis.families.values(): + f.dump(" ") + print("Pseudos:") + for p in analysis.pseudos.values(): + p.dump(" ") + + +if __name__ == "__main__": + import sys + + if len(sys.argv) < 2: + print("No input") + else: + filenames = sys.argv[1:] + dump_analysis(analyze_files(filenames)) diff --git a/Tools/cases_generator/cwriter.py b/Tools/cases_generator/cwriter.py new file mode 100644 index 00000000000000..0b7edd03fd9e47 --- /dev/null +++ b/Tools/cases_generator/cwriter.py @@ -0,0 +1,111 @@ +from lexer import Token +from typing import TextIO + + +class CWriter: + "A writer that understands tokens and how to format C code" + + last_token: Token | None + + def __init__(self, out: TextIO, indent: int, line_directives: bool): + self.out = out + self.base_column = indent * 4 + self.indents = [i * 4 for i in range(indent + 1)] + self.line_directives = line_directives + self.last_token = None + self.newline = True + + def set_position(self, tkn: Token) -> None: + if self.last_token is not None: + if self.last_token.line < tkn.line: + self.out.write("\n") + if self.line_directives: + self.out.write(f'#line {tkn.line} "{tkn.filename}"\n') + self.out.write(" " * self.indents[-1]) + else: + gap = tkn.column - self.last_token.end_column + self.out.write(" " * gap) + elif self.newline: + self.out.write(" " * self.indents[-1]) + self.last_token = tkn + self.newline = False + + def emit_at(self, txt: str, where: Token) -> None: + self.set_position(where) + self.out.write(txt) + + def maybe_dedent(self, txt: str) -> None: + parens = txt.count("(") - txt.count(")") + if parens < 0: + self.indents.pop() + elif "}" in txt or is_label(txt): + self.indents.pop() + + def maybe_indent(self, txt: str) -> None: + parens = txt.count("(") - txt.count(")") + if parens > 0 and self.last_token: + offset = self.last_token.end_column - 1 + if offset <= self.indents[-1] or offset > 40: + offset = self.indents[-1] + 4 + self.indents.append(offset) + elif "{" in txt or is_label(txt): + self.indents.append(self.indents[-1] + 4) + + def emit_text(self, txt: str) -> None: + self.out.write(txt) + + def emit_multiline_comment(self, tkn: Token) -> None: + self.set_position(tkn) + lines = tkn.text.splitlines(True) + first = True + for line in lines: + text = line.lstrip() + if first: + spaces = 0 + else: + spaces = self.indents[-1] + if text.startswith("*"): + spaces += 1 + else: + spaces += 3 + first = False + self.out.write(" " * spaces) + self.out.write(text) + + def emit_token(self, tkn: Token) -> None: + if tkn.kind == "COMMENT" and "\n" in tkn.text: + return self.emit_multiline_comment(tkn) + self.maybe_dedent(tkn.text) + self.set_position(tkn) + self.emit_text(tkn.text) + self.maybe_indent(tkn.text) + + def emit_str(self, txt: str) -> None: + self.maybe_dedent(txt) + if self.newline and txt: + if txt[0] != "\n": + self.out.write(" " * self.indents[-1]) + self.newline = False + self.emit_text(txt) + if txt.endswith("\n"): + self.newline = True + self.maybe_indent(txt) + self.last_token = None + + def emit(self, txt: str | Token) -> None: + if isinstance(txt, Token): + self.emit_token(txt) + elif isinstance(txt, str): + self.emit_str(txt) + else: + assert False + + def start_line(self) -> None: + if not self.newline: + self.out.write("\n") + self.newline = True + self.last_token = None + + +def is_label(txt: str) -> bool: + return not txt.startswith("//") and txt.endswith(":") diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 1f94c1fedb2ac7..4b7f028970bd0c 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -883,7 +883,6 @@ def main() -> None: return # These raise OSError if output can't be written - a.write_instructions(args.output, args.emit_line_directives) a.assign_opcode_ids() a.write_opcode_ids(args.opcode_ids_h, args.opcode_targets_h) diff --git a/Tools/cases_generator/lexer.py b/Tools/cases_generator/lexer.py index 1185c855785939..c3c2954a42083f 100644 --- a/Tools/cases_generator/lexer.py +++ b/Tools/cases_generator/lexer.py @@ -112,7 +112,7 @@ def choice(*opts: str) -> str: char = r"\'.\'" # TODO: escape sequence CHARACTER = "CHARACTER" -comment_re = r"//.*|/\*([^*]|\*[^/])*\*/" +comment_re = r"(//.*)|/\*([^*]|\*[^/])*\*/" COMMENT = "COMMENT" newline = r"\n" @@ -234,6 +234,7 @@ def make_syntax_error( @dataclass(slots=True) class Token: + filename: str kind: str text: str begin: tuple[int, int] @@ -261,7 +262,7 @@ def width(self) -> int: def replaceText(self, txt: str) -> "Token": assert isinstance(txt, str) - return Token(self.kind, txt, self.begin, self.end) + return Token(self.filename, self.kind, txt, self.begin, self.end) def __repr__(self) -> str: b0, b1 = self.begin @@ -272,7 +273,7 @@ def __repr__(self) -> str: return f"{self.kind}({self.text!r}, {b0}:{b1}, {e0}:{e1})" -def tokenize(src: str, line: int = 1, filename: str | None = None) -> Iterator[Token]: +def tokenize(src: str, line: int = 1, filename: str = "") -> Iterator[Token]: linestart = -1 for m in matcher.finditer(src): start, end = m.span() @@ -323,7 +324,7 @@ def tokenize(src: str, line: int = 1, filename: str | None = None) -> Iterator[T else: begin = line, start - linestart if kind != "\n": - yield Token(kind, text, begin, (line, start - linestart + len(text))) + yield Token(filename, kind, text, begin, (line, start - linestart + len(text))) def to_text(tkns: list[Token], dedent: int = 0) -> str: diff --git a/Tools/cases_generator/mypy.ini b/Tools/cases_generator/mypy.ini index e7175e263350b2..8e5a31851c596e 100644 --- a/Tools/cases_generator/mypy.ini +++ b/Tools/cases_generator/mypy.ini @@ -11,3 +11,5 @@ strict = True strict_concatenate = True enable_error_code = ignore-without-code,redundant-expr,truthy-bool,possibly-undefined warn_unreachable = True +allow_redefinition = True +implicit_reexport = True diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py new file mode 100644 index 00000000000000..12173a61199700 --- /dev/null +++ b/Tools/cases_generator/parser.py @@ -0,0 +1,55 @@ +from parsing import ( + InstDef, + Macro, + Pseudo, + Family, + Parser, + Context, + CacheEffect, + StackEffect, + OpName, + AstNode, +) +from formatting import prettify_filename + + +BEGIN_MARKER = "// BEGIN BYTECODES //" +END_MARKER = "// END BYTECODES //" + + +def parse_files(filenames: list[str]) -> list[AstNode]: + result: list[AstNode] = [] + for filename in filenames: + with open(filename) as file: + src = file.read() + + psr = Parser(src, filename=prettify_filename(filename)) + + # Skip until begin marker + while tkn := psr.next(raw=True): + if tkn.text == BEGIN_MARKER: + break + else: + raise psr.make_syntax_error( + f"Couldn't find {BEGIN_MARKER!r} in {psr.filename}" + ) + start = psr.getpos() + + # Find end marker, then delete everything after it + while tkn := psr.next(raw=True): + if tkn.text == END_MARKER: + break + del psr.tokens[psr.getpos() - 1 :] + + # Parse from start + psr.setpos(start) + thing_first_token = psr.peek() + while node := psr.definition(): + assert node is not None + result.append(node) # type: ignore[arg-type] + if not psr.eof(): + psr.backup() + raise psr.make_syntax_error( + f"Extra stuff at the end of {filename}", psr.next(True) + ) + return result diff --git a/Tools/cases_generator/parsing.py b/Tools/cases_generator/parsing.py index 7800adf16794bb..60c185dcef58e9 100644 --- a/Tools/cases_generator/parsing.py +++ b/Tools/cases_generator/parsing.py @@ -141,10 +141,11 @@ class Pseudo(Node): flags: list[str] # instr flags to set on the pseudo instruction targets: list[str] # opcodes this can be replaced by +AstNode = InstDef | Macro | Pseudo | Family class Parser(PLexer): @contextual - def definition(self) -> InstDef | Macro | Pseudo | Family | None: + def definition(self) -> AstNode | None: if macro := self.macro_def(): return macro if family := self.family_def(): diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py new file mode 100644 index 00000000000000..9cd8e1a3b2edf8 --- /dev/null +++ b/Tools/cases_generator/stack.py @@ -0,0 +1,81 @@ +import sys +from analyzer import StackItem +from dataclasses import dataclass +from formatting import maybe_parenthesize + + +def var_size(var: StackItem) -> str: + if var.condition: + # Special case simplification + if var.condition == "oparg & 1" and var.size == "1": + return f"({var.condition})" + else: + return f"(({var.condition}) ? {var.size} : 0)" + else: + return var.size + + +class StackOffset: + "The stack offset of the virtual base of the stack from the physical stack pointer" + + def __init__(self) -> None: + self.popped: list[str] = [] + self.pushed: list[str] = [] + + def pop(self, item: StackItem) -> None: + self.popped.append(var_size(item)) + + def push(self, item: StackItem) -> None: + self.pushed.append(var_size(item)) + + def simplify(self) -> None: + "Remove matching values from both the popped and pushed list" + if not self.popped or not self.pushed: + return + # Sort the list so the lexically largest element is last. + popped = sorted(self.popped) + pushed = sorted(self.pushed) + self.popped = [] + self.pushed = [] + while popped and pushed: + pop = popped.pop() + push = pushed.pop() + if pop == push: + pass + elif pop > push: + # if pop > push, there can be no element in pushed matching pop. + self.popped.append(pop) + pushed.append(push) + else: + self.pushed.append(push) + popped.append(pop) + self.popped.extend(popped) + self.pushed.extend(pushed) + + def to_c(self) -> str: + self.simplify() + int_offset = 0 + symbol_offset = "" + for item in self.popped: + try: + int_offset -= int(item) + except ValueError: + symbol_offset += f" - {maybe_parenthesize(item)}" + for item in self.pushed: + try: + int_offset += int(item) + except ValueError: + symbol_offset += f" + {maybe_parenthesize(item)}" + if symbol_offset and not int_offset: + res = symbol_offset + else: + res = f"{int_offset}{symbol_offset}" + if res.startswith(" + "): + res = res[3:] + if res.startswith(" - "): + res = "-" + res[3:] + return res + + def clear(self) -> None: + self.popped = [] + self.pushed = [] diff --git a/Tools/cases_generator/tier1_generator.py b/Tools/cases_generator/tier1_generator.py new file mode 100644 index 00000000000000..eba926435d2415 --- /dev/null +++ b/Tools/cases_generator/tier1_generator.py @@ -0,0 +1,417 @@ +"""Generate the main interpreter switch. +Reads the instruction definitions from bytecodes.c. +Writes the cases to generated_cases.c.h, which is #included in ceval.c. +""" + +import argparse +import os.path +import sys + +from analyzer import ( + Analysis, + Instruction, + Uop, + Part, + analyze_files, + Skip, + StackItem, + analysis_error, +) +from cwriter import CWriter +from typing import TextIO, Iterator +from lexer import Token +from stack import StackOffset + + +HERE = os.path.dirname(__file__) +ROOT = os.path.join(HERE, "../..") +THIS = os.path.relpath(__file__, ROOT).replace(os.path.sep, "/") + +DEFAULT_INPUT = os.path.relpath(os.path.join(ROOT, "Python/bytecodes.c")) +DEFAULT_OUTPUT = os.path.relpath(os.path.join(ROOT, "Python/generated_cases.c.h")) + + +def write_header(filename: str, outfile: TextIO) -> None: + outfile.write( + f"""// This file is generated by {THIS} +// from: +// {filename} +// Do not edit! + +#ifdef TIER_TWO + #error "This file is for Tier 1 only" +#endif +#define TIER_ONE 1 +""" + ) + + +FOOTER = "#undef TIER_ONE\n" + + +class SizeMismatch(Exception): + pass + + +class Stack: + def __init__(self) -> None: + self.top_offset = StackOffset() + self.base_offset = StackOffset() + self.peek_offset = StackOffset() + self.variables: list[StackItem] = [] + self.defined: set[str] = set() + + def pop(self, var: StackItem) -> str: + self.top_offset.pop(var) + if not var.peek: + self.peek_offset.pop(var) + indirect = "&" if var.is_array() else "" + if self.variables: + popped = self.variables.pop() + if popped.size != var.size: + raise SizeMismatch( + f"Size mismatch when popping '{popped.name}' from stack to assign to {var.name}. " + f"Expected {var.size} got {popped.size}" + ) + if popped.name == var.name: + return "" + elif popped.name == "unused": + self.defined.add(var.name) + return ( + f"{var.name} = {indirect}stack_pointer[{self.top_offset.to_c()}];\n" + ) + elif var.name == "unused": + return "" + else: + self.defined.add(var.name) + return f"{var.name} = {popped.name};\n" + self.base_offset.pop(var) + if var.name == "unused": + return "" + else: + self.defined.add(var.name) + assign = f"{var.name} = {indirect}stack_pointer[{self.base_offset.to_c()}];" + if var.condition: + return f"if ({var.condition}) {{ {assign} }}\n" + return f"{assign}\n" + + def push(self, var: StackItem) -> str: + self.variables.append(var) + if var.is_array() and var.name not in self.defined and var.name != "unused": + c_offset = self.top_offset.to_c() + self.top_offset.push(var) + self.defined.add(var.name) + return f"{var.name} = &stack_pointer[{c_offset}];\n" + else: + self.top_offset.push(var) + return "" + + def flush(self, out: CWriter) -> None: + for var in self.variables: + if not var.peek: + if var.name != "unused" and not var.is_array(): + if var.condition: + out.emit(f" if ({var.condition}) ") + out.emit( + f"stack_pointer[{self.base_offset.to_c()}] = {var.name};\n" + ) + self.base_offset.push(var) + if self.base_offset.to_c() != self.top_offset.to_c(): + print("base", self.base_offset.to_c(), "top", self.top_offset.to_c()) + assert False + number = self.base_offset.to_c() + if number != "0": + out.emit(f"stack_pointer += {number};\n") + self.variables = [] + self.base_offset.clear() + self.top_offset.clear() + self.peek_offset.clear() + + def as_comment(self) -> str: + return f"/* Variables: {[v.name for v in self.variables]}. Base offset: {self.base_offset.to_c()}. Top offset: {self.top_offset.to_c()} */" + + +def declare_variables(inst: Instruction, out: CWriter) -> None: + variables = {"unused"} + for uop in inst.parts: + if isinstance(uop, Uop): + for var in reversed(uop.stack.inputs): + if var.name not in variables: + type = var.type if var.type else "PyObject *" + variables.add(var.name) + if var.condition: + out.emit(f"{type}{var.name} = NULL;\n") + else: + out.emit(f"{type}{var.name};\n") + for var in uop.stack.outputs: + if var.name not in variables: + variables.add(var.name) + type = var.type if var.type else "PyObject *" + if var.condition: + out.emit(f"{type}{var.name} = NULL;\n") + else: + out.emit(f"{type}{var.name};\n") + + +def emit_to(out: CWriter, tkn_iter: Iterator[Token], end: str) -> None: + parens = 0 + for tkn in tkn_iter: + if tkn.kind == end and parens == 0: + return + if tkn.kind == "LPAREN": + parens += 1 + if tkn.kind == "RPAREN": + parens -= 1 + out.emit(tkn) + + +def replace_deopt( + out: CWriter, + tkn: Token, + tkn_iter: Iterator[Token], + uop: Uop, + unused: Stack, + inst: Instruction, +) -> None: + out.emit_at("DEOPT_IF", tkn) + out.emit(next(tkn_iter)) + emit_to(out, tkn_iter, "RPAREN") + next(tkn_iter) # Semi colon + out.emit(", ") + assert inst.family is not None + out.emit(inst.family.name) + out.emit(");\n") + + +def replace_error( + out: CWriter, + tkn: Token, + tkn_iter: Iterator[Token], + uop: Uop, + stack: Stack, + inst: Instruction, +) -> None: + out.emit_at("if ", tkn) + out.emit(next(tkn_iter)) + emit_to(out, tkn_iter, "COMMA") + label = next(tkn_iter).text + next(tkn_iter) # RPAREN + next(tkn_iter) # Semi colon + out.emit(") ") + c_offset = stack.peek_offset.to_c() + try: + offset = -int(c_offset) + close = ";\n" + except ValueError: + offset = None + out.emit(f"{{ stack_pointer += {c_offset}; ") + close = "; }\n" + out.emit("goto ") + if offset: + out.emit(f"pop_{offset}_") + out.emit(label) + out.emit(close) + + +def replace_decrefs( + out: CWriter, + tkn: Token, + tkn_iter: Iterator[Token], + uop: Uop, + stack: Stack, + inst: Instruction, +) -> None: + next(tkn_iter) + next(tkn_iter) + next(tkn_iter) + out.emit_at("", tkn) + for var in uop.stack.inputs: + if var.name == "unused" or var.name == "null" or var.peek: + continue + if var.size != "1": + out.emit(f"for (int _i = {var.size}; --_i >= 0;) {{\n") + out.emit(f"Py_DECREF({var.name}[_i]);\n") + out.emit("}\n") + elif var.condition: + out.emit(f"Py_XDECREF({var.name});\n") + else: + out.emit(f"Py_DECREF({var.name});\n") + + +def replace_store_sp( + out: CWriter, + tkn: Token, + tkn_iter: Iterator[Token], + uop: Uop, + stack: Stack, + inst: Instruction, +) -> None: + next(tkn_iter) + next(tkn_iter) + next(tkn_iter) + out.emit_at("", tkn) + stack.flush(out) + out.emit("_PyFrame_SetStackPointer(frame, stack_pointer);\n") + + +def replace_check_eval_breaker( + out: CWriter, + tkn: Token, + tkn_iter: Iterator[Token], + uop: Uop, + stack: Stack, + inst: Instruction, +) -> None: + next(tkn_iter) + next(tkn_iter) + next(tkn_iter) + if not uop.properties.ends_with_eval_breaker: + out.emit_at("CHECK_EVAL_BREAKER();", tkn) + + +REPLACEMENT_FUNCTIONS = { + "DEOPT_IF": replace_deopt, + "ERROR_IF": replace_error, + "DECREF_INPUTS": replace_decrefs, + "CHECK_EVAL_BREAKER": replace_check_eval_breaker, + "STORE_SP": replace_store_sp, +} + + +# Move this to formatter +def emit_tokens(out: CWriter, uop: Uop, stack: Stack, inst: Instruction) -> None: + tkns = uop.body[1:-1] + if not tkns: + return + tkn_iter = iter(tkns) + out.start_line() + for tkn in tkn_iter: + if tkn.kind == "IDENTIFIER" and tkn.text in REPLACEMENT_FUNCTIONS: + REPLACEMENT_FUNCTIONS[tkn.text](out, tkn, tkn_iter, uop, stack, inst) + else: + out.emit(tkn) + + +def write_uop( + uop: Part, out: CWriter, offset: int, stack: Stack, inst: Instruction, braces: bool +) -> int: + # out.emit(stack.as_comment() + "\n") + if isinstance(uop, Skip): + entries = "entries" if uop.size > 1 else "entry" + out.emit(f"/* Skip {uop.size} cache {entries} */\n") + return offset + uop.size + try: + out.start_line() + if braces: + out.emit(f"// {uop.name}\n") + for var in reversed(uop.stack.inputs): + out.emit(stack.pop(var)) + if braces: + out.emit("{\n") + if not uop.properties.stores_sp: + for i, var in enumerate(uop.stack.outputs): + out.emit(stack.push(var)) + for cache in uop.caches: + if cache.name != "unused": + if cache.size == 4: + type = "PyObject *" + reader = "read_obj" + else: + type = f"uint{cache.size*16}_t " + reader = f"read_u{cache.size*16}" + out.emit( + f"{type}{cache.name} = {reader}(&this_instr[{offset}].cache);\n" + ) + offset += cache.size + emit_tokens(out, uop, stack, inst) + if uop.properties.stores_sp: + for i, var in enumerate(uop.stack.outputs): + out.emit(stack.push(var)) + if braces: + out.start_line() + out.emit("}\n") + # out.emit(stack.as_comment() + "\n") + return offset + except SizeMismatch as ex: + raise analysis_error(ex.args[0], uop.body[0]) + + +def uses_this(inst: Instruction) -> bool: + if inst.properties.needs_this: + return True + for uop in inst.parts: + if isinstance(uop, Skip): + continue + for cache in uop.caches: + if cache.name != "unused": + return True + return False + + +def generate_tier1( + filenames: str, analysis: Analysis, outfile: TextIO, lines: bool +) -> None: + write_header(filenames, outfile) + out = CWriter(outfile, 2, lines) + out.emit("\n") + for name, inst in sorted(analysis.instructions.items()): + needs_this = uses_this(inst) + out.emit("\n") + out.emit(f"TARGET({name}) {{\n") + if needs_this and not inst.is_target: + out.emit(f"_Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr;\n") + else: + out.emit(f"frame->instr_ptr = next_instr;\n") + out.emit(f"next_instr += {inst.size};\n") + out.emit(f"INSTRUCTION_STATS({name});\n") + if inst.is_target: + out.emit(f"PREDICTED({name});\n") + if needs_this: + out.emit(f"_Py_CODEUNIT *this_instr = next_instr - {inst.size};\n") + if inst.family is not None: + out.emit( + f"static_assert({inst.family.size} == {inst.size-1}" + ', "incorrect cache size");\n' + ) + declare_variables(inst, out) + offset = 1 # The instruction itself + stack = Stack() + for part in inst.parts: + # Only emit braces if more than one uop + offset = write_uop(part, out, offset, stack, inst, len(inst.parts) > 1) + out.start_line() + if not inst.parts[-1].properties.always_exits: + stack.flush(out) + if inst.parts[-1].properties.ends_with_eval_breaker: + out.emit("CHECK_EVAL_BREAKER();\n") + out.emit("DISPATCH();\n") + out.start_line() + out.emit("}") + out.emit("\n") + outfile.write(FOOTER) + + +arg_parser = argparse.ArgumentParser( + description="Generate the code for the interpreter switch.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, +) + +arg_parser.add_argument( + "-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT +) + +arg_parser.add_argument( + "-l", "--emit-line-directives", help="Emit #line directives", action="store_true" +) + +arg_parser.add_argument( + "input", nargs=argparse.REMAINDER, help="Instruction definition file(s)" +) + +if __name__ == "__main__": + args = arg_parser.parse_args() + if len(args.input) == 0: + args.input.append(DEFAULT_INPUT) + data = analyze_files(args.input) + with open(args.output, "w") as outfile: + generate_tier1(args.input, data, outfile, args.emit_line_directives) From 9c3458e05865093dd55d7608810a9d0ef0765978 Mon Sep 17 00:00:00 2001 From: andrewluotechnologies <44252973+andrewluotechnologies@users.noreply.github.com> Date: Thu, 7 Dec 2023 04:56:01 -0800 Subject: [PATCH 208/228] gh-112125: Fix None.__ne__(None) returning NotImplemented instead of False (#112504) --- Include/internal/pycore_typeobject.h | 2 ++ Lib/test/test_builtin.py | 5 +++++ .../2023-12-07-13-19-55.gh-issue-112125.4ADN7i.rst | 1 + Objects/object.c | 2 +- Objects/typeobject.c | 6 ++++++ 5 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-12-07-13-19-55.gh-issue-112125.4ADN7i.rst diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index bbf8544b09f0fb..f983de56049631 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -135,6 +135,8 @@ extern PyObject* _Py_type_getattro_impl(PyTypeObject *type, PyObject *name, int *suppress_missing_attribute); extern PyObject* _Py_type_getattro(PyTypeObject *type, PyObject *name); +extern PyObject* _Py_BaseObject_RichCompare(PyObject* self, PyObject* other, int op); + extern PyObject* _Py_slot_tp_getattro(PyObject *self, PyObject *name); extern PyObject* _Py_slot_tp_getattr_hook(PyObject *self, PyObject *name); diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 558715383c82ee..5e66d58fd2cb18 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -627,6 +627,11 @@ def __dir__(self): # test that object has a __dir__() self.assertEqual(sorted([].__dir__()), dir([])) + def test___ne__(self): + self.assertFalse(None.__ne__(None)) + self.assertTrue(None.__ne__(0)) + self.assertTrue(None.__ne__("abc")) + def test_divmod(self): self.assertEqual(divmod(12, 7), (1, 5)) self.assertEqual(divmod(-12, 7), (-2, 2)) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-07-13-19-55.gh-issue-112125.4ADN7i.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-07-13-19-55.gh-issue-112125.4ADN7i.rst new file mode 100644 index 00000000000000..52cd45029fb8c7 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-07-13-19-55.gh-issue-112125.4ADN7i.rst @@ -0,0 +1 @@ +Fix None.__ne__(None) returning NotImplemented instead of False diff --git a/Objects/object.c b/Objects/object.c index d145674cb3ba34..cdb7a08a7828fb 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2026,7 +2026,7 @@ PyTypeObject _PyNone_Type = { 0, /*tp_doc */ 0, /*tp_traverse */ 0, /*tp_clear */ - 0, /*tp_richcompare */ + _Py_BaseObject_RichCompare, /*tp_richcompare */ 0, /*tp_weaklistoffset */ 0, /*tp_iter */ 0, /*tp_iternext */ diff --git a/Objects/typeobject.c b/Objects/typeobject.c index aa00e04ad5e11b..08f5f47d586729 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5625,6 +5625,12 @@ object_richcompare(PyObject *self, PyObject *other, int op) return res; } +PyObject* +_Py_BaseObject_RichCompare(PyObject* self, PyObject* other, int op) +{ + return object_richcompare(self, other, op); +} + static PyObject * object_get_class(PyObject *self, void *closure) { From 7576534f4a230cc8f9b0f13ef2e521eeaa9e9ead Mon Sep 17 00:00:00 2001 From: Christopher Chavez Date: Thu, 7 Dec 2023 07:24:11 -0600 Subject: [PATCH 209/228] bpo-42519: Remove outdated sentence in comment (#112822) Update objimpl.h --- Include/objimpl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/objimpl.h b/Include/objimpl.h index 967e2776767756..ff5fa7a8c1d3d8 100644 --- a/Include/objimpl.h +++ b/Include/objimpl.h @@ -35,7 +35,7 @@ Functions and macros for modules that implement new object types. fields, this also fills in the ob_size field. - PyObject_Free(op) releases the memory allocated for an object. It does not - run a destructor -- it only frees the memory. PyObject_Free is identical. + run a destructor -- it only frees the memory. - PyObject_Init(op, typeobj) and PyObject_InitVar(op, typeobj, n) don't allocate memory. Instead of a 'type' parameter, they take a pointer to a From 9f67042f28bf886a9bf30fed6795d26cff255f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Thu, 7 Dec 2023 14:29:15 +0100 Subject: [PATCH 210/228] gh-110190: Temporarily skip new test introduced in gh-112604 on PPC64LE (#112818) --- Lib/test/test_ctypes/test_structures.py | 98 +++++++++++++------------ 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/Lib/test/test_ctypes/test_structures.py b/Lib/test/test_ctypes/test_structures.py index 771205ffb32ecc..57ae9240b3c165 100644 --- a/Lib/test/test_ctypes/test_structures.py +++ b/Lib/test/test_ctypes/test_structures.py @@ -1,4 +1,5 @@ import _ctypes_test +import platform import struct import sys import unittest @@ -494,51 +495,6 @@ class Test3B(Test3A): ('more_data', c_float * 2), ] - class Test3C1(Structure): - _fields_ = [ - ("data", c_double * 4) - ] - - class DataType4(Array): - _type_ = c_double - _length_ = 4 - - class Test3C2(Structure): - _fields_ = [ - ("data", DataType4) - ] - - class Test3C3(Structure): - _fields_ = [ - ("x", c_double), - ("y", c_double), - ("z", c_double), - ("t", c_double) - ] - - class Test3D1(Structure): - _fields_ = [ - ("data", c_double * 5) - ] - - class DataType5(Array): - _type_ = c_double - _length_ = 5 - - class Test3D2(Structure): - _fields_ = [ - ("data", DataType5) - ] - - class Test3D3(Structure): - _fields_ = [ - ("x", c_double), - ("y", c_double), - ("z", c_double), - ("t", c_double), - ("u", c_double) - ] - # Load the shared library dll = CDLL(_ctypes_test.__file__) @@ -587,6 +543,58 @@ class Test3D3(Structure): self.assertAlmostEqual(s.more_data[0], -3.0, places=6) self.assertAlmostEqual(s.more_data[1], -2.0, places=6) + @unittest.skipIf( + 'ppc64le' in platform.uname().machine, + "gh-110190: currently fails on ppc64le", + ) + def test_array_in_struct_registers(self): + dll = CDLL(_ctypes_test.__file__) + + class Test3C1(Structure): + _fields_ = [ + ("data", c_double * 4) + ] + + class DataType4(Array): + _type_ = c_double + _length_ = 4 + + class Test3C2(Structure): + _fields_ = [ + ("data", DataType4) + ] + + class Test3C3(Structure): + _fields_ = [ + ("x", c_double), + ("y", c_double), + ("z", c_double), + ("t", c_double) + ] + + class Test3D1(Structure): + _fields_ = [ + ("data", c_double * 5) + ] + + class DataType5(Array): + _type_ = c_double + _length_ = 5 + + class Test3D2(Structure): + _fields_ = [ + ("data", DataType5) + ] + + class Test3D3(Structure): + _fields_ = [ + ("x", c_double), + ("y", c_double), + ("z", c_double), + ("t", c_double), + ("u", c_double) + ] + # Tests for struct Test3C expected = (1.0, 2.0, 3.0, 4.0) func = dll._testfunc_array_in_struct_set_defaults_3C From 2d76be251d0aee89f76e6fa5a63fa1ad3f2b76cf Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Thu, 7 Dec 2023 08:47:55 -0500 Subject: [PATCH 211/228] gh-111962: Make dtoa thread-safe in `--disable-gil` builds. (#112049) This updates `dtoa.c` to avoid using the Bigint free-list in --disable-gil builds and to pre-computes the needed powers of 5 during interpreter initialization. * gh-111962: Make dtoa thread-safe in `--disable-gil` builds. This avoids using the Bigint free-list in `--disable-gil` builds and pre-computes the needed powers of 5 during interpreter initialization. * Fix size of cached powers of 5 array. We need the powers of 5 up to 5**512 because we only jump straight to underflow when the exponent is less than -512 (or larger than 308). * Rename Py_NOGIL to Py_GIL_DISABLED * Changes from review * Fix assertion placement --- Include/internal/pycore_dtoa.h | 16 ++++--- Python/dtoa.c | 78 +++++++++++++++++++++++----------- Python/pylifecycle.c | 6 +++ 3 files changed, 70 insertions(+), 30 deletions(-) diff --git a/Include/internal/pycore_dtoa.h b/Include/internal/pycore_dtoa.h index ac62a4d300720a..c5cfdf4ce8f823 100644 --- a/Include/internal/pycore_dtoa.h +++ b/Include/internal/pycore_dtoa.h @@ -35,6 +35,9 @@ struct _dtoa_state { /* The size of the Bigint freelist */ #define Bigint_Kmax 7 +/* The size of the cached powers of 5 array */ +#define Bigint_Pow5size 8 + #ifndef PRIVATE_MEM #define PRIVATE_MEM 2304 #endif @@ -42,9 +45,10 @@ struct _dtoa_state { ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) struct _dtoa_state { - /* p5s is a linked list of powers of 5 of the form 5**(2**i), i >= 2 */ + // p5s is an array of powers of 5 of the form: + // 5**(2**(i+2)) for 0 <= i < Bigint_Pow5size + struct Bigint *p5s[Bigint_Pow5size]; // XXX This should be freed during runtime fini. - struct Bigint *p5s; struct Bigint *freelist[Bigint_Kmax+1]; double preallocated[Bigint_PREALLOC_SIZE]; double *preallocated_next; @@ -57,9 +61,6 @@ struct _dtoa_state { #endif // !Py_USING_MEMORY_DEBUGGER -/* These functions are used by modules compiled as C extension like math: - they must be exported. */ - extern double _Py_dg_strtod(const char *str, char **ptr); extern char* _Py_dg_dtoa(double d, int mode, int ndigits, int *decpt, int *sign, char **rve); @@ -67,6 +68,11 @@ extern void _Py_dg_freedtoa(char *s); #endif // _PY_SHORT_FLOAT_REPR == 1 + +extern PyStatus _PyDtoa_Init(PyInterpreterState *interp); +extern void _PyDtoa_Fini(PyInterpreterState *interp); + + #ifdef __cplusplus } #endif diff --git a/Python/dtoa.c b/Python/dtoa.c index 5dfc0e179cbc34..6e3162f80bdae1 100644 --- a/Python/dtoa.c +++ b/Python/dtoa.c @@ -309,7 +309,7 @@ BCinfo { // struct Bigint is defined in pycore_dtoa.h. typedef struct Bigint Bigint; -#ifndef Py_USING_MEMORY_DEBUGGER +#if !defined(Py_GIL_DISABLED) && !defined(Py_USING_MEMORY_DEBUGGER) /* Memory management: memory is allocated from, and returned to, Kmax+1 pools of memory, where pool k (0 <= k <= Kmax) is for Bigints b with b->maxwds == @@ -428,7 +428,7 @@ Bfree(Bigint *v) } } -#endif /* Py_USING_MEMORY_DEBUGGER */ +#endif /* !defined(Py_GIL_DISABLED) && !defined(Py_USING_MEMORY_DEBUGGER) */ #define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ y->wds*sizeof(Long) + 2*sizeof(int)) @@ -673,10 +673,17 @@ mult(Bigint *a, Bigint *b) static Bigint * pow5mult(Bigint *b, int k) { - Bigint *b1, *p5, *p51; + Bigint *b1, *p5, **p5s; int i; static const int p05[3] = { 5, 25, 125 }; + // For double-to-string conversion, the maximum value of k is limited by + // DBL_MAX_10_EXP (308), the maximum decimal base-10 exponent for binary64. + // For string-to-double conversion, the extreme case is constrained by our + // hardcoded exponent limit before we underflow of -512, adjusted by + // STRTOD_DIGLIM-DBL_DIG-1, giving a maximum of k=535. + assert(0 <= k && k < 1024); + if ((i = k & 3)) { b = multadd(b, p05[i-1], 0); if (b == NULL) @@ -686,18 +693,11 @@ pow5mult(Bigint *b, int k) if (!(k >>= 2)) return b; PyInterpreterState *interp = _PyInterpreterState_GET(); - p5 = interp->dtoa.p5s; - if (!p5) { - /* first time */ - p5 = i2b(625); - if (p5 == NULL) { - Bfree(b); - return NULL; - } - interp->dtoa.p5s = p5; - p5->next = 0; - } + p5s = interp->dtoa.p5s; for(;;) { + assert(p5s != interp->dtoa.p5s + Bigint_Pow5size); + p5 = *p5s; + p5s++; if (k & 1) { b1 = mult(b, p5); Bfree(b); @@ -707,17 +707,6 @@ pow5mult(Bigint *b, int k) } if (!(k >>= 1)) break; - p51 = p5->next; - if (!p51) { - p51 = mult(p5,p5); - if (p51 == NULL) { - Bfree(b); - return NULL; - } - p51->next = 0; - p5->next = p51; - } - p5 = p51; } return b; } @@ -2811,3 +2800,42 @@ _Py_dg_dtoa(double dd, int mode, int ndigits, } #endif // _PY_SHORT_FLOAT_REPR == 1 + +PyStatus +_PyDtoa_Init(PyInterpreterState *interp) +{ +#if _PY_SHORT_FLOAT_REPR == 1 && !defined(Py_USING_MEMORY_DEBUGGER) + Bigint **p5s = interp->dtoa.p5s; + + // 5**4 = 625 + Bigint *p5 = i2b(625); + if (p5 == NULL) { + return PyStatus_NoMemory(); + } + p5s[0] = p5; + + // compute 5**8, 5**16, 5**32, ..., 5**512 + for (Py_ssize_t i = 1; i < Bigint_Pow5size; i++) { + p5 = mult(p5, p5); + if (p5 == NULL) { + return PyStatus_NoMemory(); + } + p5s[i] = p5; + } + +#endif + return PyStatus_Ok(); +} + +void +_PyDtoa_Fini(PyInterpreterState *interp) +{ +#if _PY_SHORT_FLOAT_REPR == 1 && !defined(Py_USING_MEMORY_DEBUGGER) + Bigint **p5s = interp->dtoa.p5s; + for (Py_ssize_t i = 0; i < Bigint_Pow5size; i++) { + Bigint *p5 = p5s[i]; + p5s[i] = NULL; + Bfree(p5); + } +#endif +} diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 95a72eb47048f2..20bfe1a0b75b29 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -820,6 +820,11 @@ pycore_interp_init(PyThreadState *tstate) return status; } + status = _PyDtoa_Init(interp); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + // The GC must be initialized before the first GC collection. status = _PyGC_Init(interp); if (_PyStatus_EXCEPTION(status)) { @@ -1776,6 +1781,7 @@ finalize_interp_clear(PyThreadState *tstate) _PyXI_Fini(tstate->interp); _PyExc_ClearExceptionGroupType(tstate->interp); _Py_clear_generic_types(tstate->interp); + _PyDtoa_Fini(tstate->interp); /* Clear interpreter state and all thread states */ _PyInterpreterState_Clear(tstate); From 21221c398f6d89b2d9295895d8a2fd71d28138fa Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Thu, 7 Dec 2023 10:01:58 -0600 Subject: [PATCH 212/228] gh-112302: Add Software Bill-of-Materials (SBOM) tracking for dependencies (#112303) --- .github/CODEOWNERS | 4 + .github/workflows/mypy.yml | 2 + Makefile.pre.in | 6 +- ...-12-06-14-06-59.gh-issue-112302.3bl20f.rst | 2 + Misc/sbom.spdx.json | 2294 +++++++++++++++++ Tools/build/generate_sbom.py | 179 ++ Tools/build/mypy.ini | 13 + 7 files changed, 2499 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Security/2023-12-06-14-06-59.gh-issue-112302.3bl20f.rst create mode 100644 Misc/sbom.spdx.json create mode 100644 Tools/build/generate_sbom.py create mode 100644 Tools/build/mypy.ini diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1925363cbeb46e..aa6d937d9cbc31 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -190,3 +190,7 @@ Doc/howto/clinic.rst @erlend-aasland # WebAssembly /Tools/wasm/ @brettcannon + +# SBOM +/Misc/sbom.spdx.json @sethmlarson +/Tools/build/generate_sbom.py @sethmlarson diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 72ae67aa02aa96..792903a90a4880 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -9,6 +9,7 @@ on: paths: - ".github/workflows/mypy.yml" - "Lib/test/libregrtest/**" + - "Tools/build/generate_sbom.py" - "Tools/cases_generator/**" - "Tools/clinic/**" - "Tools/peg_generator/**" @@ -34,6 +35,7 @@ jobs: matrix: target: [ "Lib/test/libregrtest", + "Tools/build/", "Tools/cases_generator", "Tools/clinic", "Tools/peg_generator", diff --git a/Makefile.pre.in b/Makefile.pre.in index 6ac68d59c8c47f..db85b11ef01b2c 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1359,7 +1359,7 @@ regen-unicodedata: regen-all: regen-cases regen-typeslots \ regen-token regen-ast regen-keyword regen-sre regen-frozen \ regen-pegen-metaparser regen-pegen regen-test-frozenmain \ - regen-test-levenshtein regen-global-objects + regen-test-levenshtein regen-global-objects regen-sbom @echo @echo "Note: make regen-stdlib-module-names, make regen-limited-abi, " @echo "make regen-configure and make regen-unicodedata should be run manually" @@ -2651,6 +2651,10 @@ autoconf: regen-configure: $(srcdir)/Tools/build/regen-configure.sh +.PHONY: regen-sbom +regen-sbom: + $(PYTHON_FOR_REGEN) $(srcdir)/Tools/build/generate_sbom.py + # Create a tags file for vi tags:: ctags -w $(srcdir)/Include/*.h $(srcdir)/Include/cpython/*.h $(srcdir)/Include/internal/*.h diff --git a/Misc/NEWS.d/next/Security/2023-12-06-14-06-59.gh-issue-112302.3bl20f.rst b/Misc/NEWS.d/next/Security/2023-12-06-14-06-59.gh-issue-112302.3bl20f.rst new file mode 100644 index 00000000000000..65e4dc3762d3c0 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2023-12-06-14-06-59.gh-issue-112302.3bl20f.rst @@ -0,0 +1,2 @@ +Created a Software Bill-of-Materials document and tooling for tracking +dependencies. diff --git a/Misc/sbom.spdx.json b/Misc/sbom.spdx.json new file mode 100644 index 00000000000000..09355640db888e --- /dev/null +++ b/Misc/sbom.spdx.json @@ -0,0 +1,2294 @@ +{ + "SPDXID": "SPDXRef-DOCUMENT", + "files": [ + { + "SPDXID": "SPDXRef-FILE-Modules-expat-COPYING", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "39e6f567a10e36b2e77727e98e60bbcb3eb3af0b" + }, + { + "algorithm": "SHA256", + "checksumValue": "122f2c27000472a201d337b9b31f7eb2b52d091b02857061a8880371612d9534" + } + ], + "fileName": "Modules/expat/COPYING" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-ascii.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "b0235fa3cf845a7d68e8e66dd344d5e32e8951b5" + }, + { + "algorithm": "SHA256", + "checksumValue": "42f8b392c70366743eacbc60ce021389ccaa333598dd49eef6ee5c93698ca205" + } + ], + "fileName": "Modules/expat/ascii.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-asciitab.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "cbb53d16ca1f35ee9c9e296116efd222ae611ed9" + }, + { + "algorithm": "SHA256", + "checksumValue": "1cc0ae749019fc0e488cd1cf245f6beaa6d4f7c55a1fc797e5aa40a408bc266b" + } + ], + "fileName": "Modules/expat/asciitab.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-expat.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "ab7bb32514d170592dfb3f76e41bbdc075a4e7e0" + }, + { + "algorithm": "SHA256", + "checksumValue": "f521acdad222644365b0e81a33bcd6939a98c91b225c47582cc84bd73d96febc" + } + ], + "fileName": "Modules/expat/expat.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-expat-config.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "73627287302ee3e84347c4fe21f37a9cb828bc3b" + }, + { + "algorithm": "SHA256", + "checksumValue": "f17e59f9d95eeb05694c02508aa284d332616c22cbe2e6a802d8a0710310eaab" + } + ], + "fileName": "Modules/expat/expat_config.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-expat-external.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "b70ce53fdc25ae482681ae2f6623c3c8edc9c1b7" + }, + { + "algorithm": "SHA256", + "checksumValue": "86afb425ec9999eb4f1ec9ab2fb41c58c4aa5cb9bf934b8c94264670fc5a961d" + } + ], + "fileName": "Modules/expat/expat_external.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-iasciitab.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "1b0e9014c0baa4c6254d2b5e6a67c70148309c34" + }, + { + "algorithm": "SHA256", + "checksumValue": "ad8b01e9f323cc4208bcd22241df383d7e8641fe3c8b3415aa513de82531f89f" + } + ], + "fileName": "Modules/expat/iasciitab.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-internal.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "2790d37e7de2f13dccc4f4fb352cbdf9ed6abaa2" + }, + { + "algorithm": "SHA256", + "checksumValue": "d2efe5a1018449968a689f444cca432e3d5875aba6ad08ee18ca235d64f41bb9" + } + ], + "fileName": "Modules/expat/internal.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-latin1tab.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "d335ecca380e331a0ea7dc33838a4decd93ec1e4" + }, + { + "algorithm": "SHA256", + "checksumValue": "eab66226da100372e01e42e1cbcd8ac2bbbb5c1b5f95d735289cc85c7a8fc2ba" + } + ], + "fileName": "Modules/expat/latin1tab.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-nametab.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "cf2bc9626c945826602ba9170786e9a2a44645e4" + }, + { + "algorithm": "SHA256", + "checksumValue": "67dcf415d37a4b692a6a8bb46f990c02d83f2ef3d01a65cd61c8594a084246f2" + } + ], + "fileName": "Modules/expat/nametab.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-pyexpatns.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "baa44fe4581895d42e8d5e83d8ce6a69b1c34dbe" + }, + { + "algorithm": "SHA256", + "checksumValue": "33a7b9ac8bf4571e23272cdf644c6f9808bd44c66b149e3c41ab3870d1888609" + } + ], + "fileName": "Modules/expat/pyexpatns.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-siphash.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "2b984f806f10fbfbf72d8d1b7ba2992413c15299" + }, + { + "algorithm": "SHA256", + "checksumValue": "fbce56cd680e690043bbf572188cc2d0a25dbfc0d47ac8cb98eb3de768d4e694" + } + ], + "fileName": "Modules/expat/siphash.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-utf8tab.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "b77c8fcfb551553c81d6fbd94c798c8aa04ad021" + }, + { + "algorithm": "SHA256", + "checksumValue": "8cd26bd461d334d5e1caedb3af4518d401749f2fc66d56208542b29085159c18" + } + ], + "fileName": "Modules/expat/utf8tab.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-winconfig.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "e774ae6ee9391aa6ffb8f775fb74e48f4b428959" + }, + { + "algorithm": "SHA256", + "checksumValue": "3c71cea9a6174718542331971a35db317902b2433be9d8dd1cb24239b635c0cc" + } + ], + "fileName": "Modules/expat/winconfig.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-xmlparse.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "b580e827e16baa6b035586ffcd4d90301e5a353f" + }, + { + "algorithm": "SHA256", + "checksumValue": "483518bbd69338eefc706cd7fc0b6039df2d3e347f64097989059ed6d2385a1e" + } + ], + "fileName": "Modules/expat/xmlparse.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-xmlrole.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "5ef21312af73deb2428be3fe97a65244608e76de" + }, + { + "algorithm": "SHA256", + "checksumValue": "6fcf8c72ac0112c1b98bd2039c632a66b4c3dc516ce7c1f981390951121ef3c0" + } + ], + "fileName": "Modules/expat/xmlrole.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-xmlrole.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "c1a4ea6356643d0820edb9c024c20ad2aaf562dc" + }, + { + "algorithm": "SHA256", + "checksumValue": "2b5d674be6ef20c7e3f69295176d75e68c5616e4dfce0a186fdd5e2ed8315f7a" + } + ], + "fileName": "Modules/expat/xmlrole.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-xmltok.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "e6d66ae9fd61d7950c62c5d87693c30a707e8577" + }, + { + "algorithm": "SHA256", + "checksumValue": "1110f651bdccfa765ad3d6f3857a35887ab35fc0fe7f3f3488fde2b238b482e3" + } + ], + "fileName": "Modules/expat/xmltok.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-xmltok.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "9c2a544875fd08ba9c2397296c97263518a410aa" + }, + { + "algorithm": "SHA256", + "checksumValue": "4299a03828b98bfe47ec6809f6e279252954a9a911dc7e0f19551bd74e3af971" + } + ], + "fileName": "Modules/expat/xmltok.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-xmltok-impl.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "aa96882de8e3d1d3083124b595aa911efe44e5ad" + }, + { + "algorithm": "SHA256", + "checksumValue": "0fbcba7931707c60301305dab78d2298d96447d0a5513926d8b18135228c0818" + } + ], + "fileName": "Modules/expat/xmltok_impl.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-xmltok-impl.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "788332fe8040bed71172cddedb69abd848cc62f7" + }, + { + "algorithm": "SHA256", + "checksumValue": "f05ad4fe5e98429a7349ff04f57192cac58c324601f2a2e5e697ab0bc05d36d5" + } + ], + "fileName": "Modules/expat/xmltok_impl.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-xmltok-ns.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "2d82d0a1201f78d478b30d108ff8fc27ee3e2672" + }, + { + "algorithm": "SHA256", + "checksumValue": "6ce6d03193279078d55280150fe91e7370370b504a6c123a79182f28341f3e90" + } + ], + "fileName": "Modules/expat/xmltok_ns.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-MD5.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "f77449b2b4eb99f1da0938633cc558baf9c444fb" + }, + { + "algorithm": "SHA256", + "checksumValue": "0f252967debca5b35362ca53951ea16ca8bb97a19a1d24f6695f44d50010859e" + } + ], + "fileName": "Modules/_hacl/Hacl_Hash_MD5.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-MD5.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "c24e6779a91c840f3d65d24abbce225b608b676e" + }, + { + "algorithm": "SHA256", + "checksumValue": "9cd062e782801013e3cacaba583e44e1b5e682e217d20208d5323354d42011f1" + } + ], + "fileName": "Modules/_hacl/Hacl_Hash_MD5.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA1.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "560f6ff541b5eff480ea047b147f4212bb0db7ed" + }, + { + "algorithm": "SHA256", + "checksumValue": "0ade3ab264e912d7b4e5cdcf773db8c63e4440540d295922d74b06bcfc74c77a" + } + ], + "fileName": "Modules/_hacl/Hacl_Hash_SHA1.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA1.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "853b77d45379146faaeac5fe899b28db386ad13c" + }, + { + "algorithm": "SHA256", + "checksumValue": "b13eb14f91582703819235ea7c8f807bb93e4f1e6b695499dc1d86021dc39e72" + } + ], + "fileName": "Modules/_hacl/Hacl_Hash_SHA1.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA2.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "667120b6100c946cdaa442f1173c723339923071" + }, + { + "algorithm": "SHA256", + "checksumValue": "b189459b863341a3a9c5c78c0208b6554a2f2ac26e0748fbd4432a91db21fae6" + } + ], + "fileName": "Modules/_hacl/Hacl_Hash_SHA2.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA2.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "81db38b0b920e63ec33c7109d1144c35cf091da0" + }, + { + "algorithm": "SHA256", + "checksumValue": "631c9ba19c1c2c835bb63d3f2f22b8d76fb535edfed3c254ff2a52f12af3fe61" + } + ], + "fileName": "Modules/_hacl/Hacl_Hash_SHA2.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA3.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "9c832b98a2f2a68202d2da016fb718965d7b7602" + }, + { + "algorithm": "SHA256", + "checksumValue": "38d350d1184238966cfa821a59ae00343f362182b6c2fbea7f2651763d757fb7" + } + ], + "fileName": "Modules/_hacl/Hacl_Hash_SHA3.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA3.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "ecc766fb6f7ee85e902b593b61b41e5a728fca34" + }, + { + "algorithm": "SHA256", + "checksumValue": "bae290a94366a2460f51e8468144baaade91d9048db111e10d2e2ffddc3f98cf" + } + ], + "fileName": "Modules/_hacl/Hacl_Hash_SHA3.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Streaming-Types.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "ab7b4d9465a2765a07f8d5bccace7182b28ed1b8" + }, + { + "algorithm": "SHA256", + "checksumValue": "26913613f3b4f8ffff0a3e211a5ebc849159094e5e11de0a31fcb95b6105b74c" + } + ], + "fileName": "Modules/_hacl/Hacl_Streaming_Types.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-include-krml-FStar-UInt128-Verified.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "2ea61d6a236147462045f65c20311819d74db80c" + }, + { + "algorithm": "SHA256", + "checksumValue": "2c22b4d49ba06d6a3053cdc66405bd5ae953a28fcfed1ab164e8f5e0f6e2fb8b" + } + ], + "fileName": "Modules/_hacl/include/krml/FStar_UInt128_Verified.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-include-krml-FStar-UInt-8-16-32-64.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "1a647d841180ac8ca667afa968c353425e81ad0d" + }, + { + "algorithm": "SHA256", + "checksumValue": "e5d1c5854833bec7ea02e227ec35bd7b49c5fb9e0f339efa0dd83e1595f722d4" + } + ], + "fileName": "Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-include-krml-fstar-uint128-struct-endianness.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "1987119a563a8fdc5966286e274f716dbcea77ee" + }, + { + "algorithm": "SHA256", + "checksumValue": "fe57e1bc5ce3224d106e36cb8829b5399c63a68a70b0ccd0c91d82a4565c8869" + } + ], + "fileName": "Modules/_hacl/include/krml/fstar_uint128_struct_endianness.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-include-krml-internal-target.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "903c9eb76b01f3a95c04c3bc841c2fb71dea5403" + }, + { + "algorithm": "SHA256", + "checksumValue": "08ec602c7f90a1540389c0cfc95769fa7fec251e7ca143ef83c0b9f7afcf89a7" + } + ], + "fileName": "Modules/_hacl/include/krml/internal/target.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-include-krml-lowstar-endianness.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "964e09bd99ff2366afd6193b59863fc925e7fb05" + }, + { + "algorithm": "SHA256", + "checksumValue": "3734c7942bec9a434e16df069fa45bdcb84b130f14417bc5f7bfe8546272d9f5" + } + ], + "fileName": "Modules/_hacl/include/krml/lowstar_endianness.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-include-krml-types.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "df8e0ed74a5970d09d3cc4c6e7c6c7a4c4e5015c" + }, + { + "algorithm": "SHA256", + "checksumValue": "de7444c345caa4c47902c4380500356a3ee7e199d2aab84fd8c4960410154f3d" + } + ], + "fileName": "Modules/_hacl/include/krml/types.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-internal-Hacl-Hash-MD5.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "5dd4ee3c835a0d176a6e9fecbe9752fd1474ff41" + }, + { + "algorithm": "SHA256", + "checksumValue": "d82ef594cba44203576d67b047240316bb3c542912ebb7034afa1e07888cec56" + } + ], + "fileName": "Modules/_hacl/internal/Hacl_Hash_MD5.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-internal-Hacl-Hash-SHA1.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "515b3082eb7c30597773e1c63ec46688f6da3634" + }, + { + "algorithm": "SHA256", + "checksumValue": "10aacf847006b8e0dfb64d5c327443f954db6718b4aec712fb3268230df6a752" + } + ], + "fileName": "Modules/_hacl/internal/Hacl_Hash_SHA1.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-internal-Hacl-Hash-SHA2.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "a044ec12b70ba97b67e9a312827d6270452a20ca" + }, + { + "algorithm": "SHA256", + "checksumValue": "a1426b54fa7273ba5b50817c25b2b26fc85c4d1befb14092cd27dc4c99439463" + } + ], + "fileName": "Modules/_hacl/internal/Hacl_Hash_SHA2.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-internal-Hacl-Hash-SHA3.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "cfb7b520c39a73cb84c541d370455f92b998781f" + }, + { + "algorithm": "SHA256", + "checksumValue": "fd41997f9e96b3c9a3337b1b51fab965a1e21b0c16f353d156f1a1fa00709fbf" + } + ], + "fileName": "Modules/_hacl/internal/Hacl_Hash_SHA3.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-python-hacl-namespaces.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "f5c7b3ed911af6c8d582e8b3714b0c36195dc994" + }, + { + "algorithm": "SHA256", + "checksumValue": "07de72398b12957e014e97b9ac197bceef12d6d6505c2bfe8b23ee17b94ec5fa" + } + ], + "fileName": "Modules/_hacl/python_hacl_namespaces.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2-config.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "ff5e3ae2360adf7279a9c54d12a1d32e16a1f223" + }, + { + "algorithm": "SHA256", + "checksumValue": "1eb919e885244e43cdf7b2104ad30dc9271513478c0026f6bfb4bad6e2f0ab42" + } + ], + "fileName": "Modules/_blake2/impl/blake2-config.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2-impl.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "28b947b43bdc680b9f4335712bb2a5f2d5d32623" + }, + { + "algorithm": "SHA256", + "checksumValue": "4277092643b289f1d36d32cf0fd2efc30ead8bdd99342e5da3b3609dd8ea7d86" + } + ], + "fileName": "Modules/_blake2/impl/blake2-impl.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "caa3da7953109d0d2961e3b686d2d285c484b901" + }, + { + "algorithm": "SHA256", + "checksumValue": "2f6c9d0ecf70be474f2853b52394993625a32960e0a64eae147ef97a3a5c1460" + } + ], + "fileName": "Modules/_blake2/impl/blake2.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2b-load-sse2.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "029a98f87a178936d9e5211c7798b3e0fc622f94" + }, + { + "algorithm": "SHA256", + "checksumValue": "b392a6e7b43813a05609e994db5fc3552c5912bd482efc781daa0778eb56ab4e" + } + ], + "fileName": "Modules/_blake2/impl/blake2b-load-sse2.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2b-load-sse41.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "fb466dd72344170d09e311e5ea12de99ce071357" + }, + { + "algorithm": "SHA256", + "checksumValue": "cc3072c92164142bf2f9dda4e6c08db61be68ec15a95442415e861090d08f6a2" + } + ], + "fileName": "Modules/_blake2/impl/blake2b-load-sse41.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2b-ref.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "4c0d79128cf891a95b1f668031d55c0c6d2e0270" + }, + { + "algorithm": "SHA256", + "checksumValue": "07b257d44e9cc2d95d4911629c92138feafd16d63fef0a5fa7b38914dfd82349" + } + ], + "fileName": "Modules/_blake2/impl/blake2b-ref.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2b-round.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "4c7418e2026417c9c6736fcd305a31f23e05a661" + }, + { + "algorithm": "SHA256", + "checksumValue": "fa34a60c2d198a0585033f43fd4003f4ba279c9ebcabdf5d6650def0e6d1e914" + } + ], + "fileName": "Modules/_blake2/impl/blake2b-round.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2b.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "6fa074693aa7305018dfa8db48010a8ef1050ad4" + }, + { + "algorithm": "SHA256", + "checksumValue": "c8c6dd861ac193d4a0e836242ff44900f83423f86d2c2940c8c4c1e41fbd5812" + } + ], + "fileName": "Modules/_blake2/impl/blake2b.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2s-load-sse2.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "ad3f79b6cbe3fd812722114a0d5d08064e69e4d0" + }, + { + "algorithm": "SHA256", + "checksumValue": "57f1ac6c09f4a50d95811529062220eab4f29cec3805bc6081dec00426c6df62" + } + ], + "fileName": "Modules/_blake2/impl/blake2s-load-sse2.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2s-load-sse41.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "51c32d79f419f3d2eb9875cd9a7f5c0d7892f8a8" + }, + { + "algorithm": "SHA256", + "checksumValue": "ecc9e09adcbe098629eafd305596bed8d7004be1d83f326995def42bbde93b23" + } + ], + "fileName": "Modules/_blake2/impl/blake2s-load-sse41.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2s-load-xop.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "2749a7ba0104b765d4f56f13faf70b6eb89cf203" + }, + { + "algorithm": "SHA256", + "checksumValue": "8bc95595cec4c50f5d70f2b330d3798de07cc784e8890791b3328890e602d5c5" + } + ], + "fileName": "Modules/_blake2/impl/blake2s-load-xop.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2s-ref.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "883fcfe85f9063819f21b1100296d1f9eb55bac1" + }, + { + "algorithm": "SHA256", + "checksumValue": "9715c00d0f11587a139b07fa26678e6d26e44d3d4910b96158d158da2b022bfb" + } + ], + "fileName": "Modules/_blake2/impl/blake2s-ref.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2s-round.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "5d9f69adda40ed163b287b9ed4cedb35b88f2daa" + }, + { + "algorithm": "SHA256", + "checksumValue": "65d90111c89c43bb18a9e1d1a4fdbd9f85bebd1ff00129335b85995d0f30ee8b" + } + ], + "fileName": "Modules/_blake2/impl/blake2s-round.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2s.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "d2691353fa54ac6ffcd7c0a294984dc9d7968ef7" + }, + { + "algorithm": "SHA256", + "checksumValue": "cfd7948c9fd50e9f9c62f8a93b20a254d1d510a862d1092af4f187b7c1a859a3" + } + ], + "fileName": "Modules/_blake2/impl/blake2s.c" + }, + { + "SPDXID": "SPDXRef-FILE-Lib-ctypes-macholib-init-.py", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0fbc026a9771d9675e7094790b5b945334d3cb53" + }, + { + "algorithm": "SHA256", + "checksumValue": "1e77c01eec8f167ed10b754f153c0c743c8e5196ae9c81dffc08f129ab56dbfd" + } + ], + "fileName": "Lib/ctypes/macholib/__init__.py" + }, + { + "SPDXID": "SPDXRef-FILE-Lib-ctypes-macholib-dyld.py", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "4a78ebd73ce4167c722689781a15fe0b4578e967" + }, + { + "algorithm": "SHA256", + "checksumValue": "eb8e7b17f1533bc3e86e23e8695f7a5e4b7a99ef1b1575d10af54f389161b655" + } + ], + "fileName": "Lib/ctypes/macholib/dyld.py" + }, + { + "SPDXID": "SPDXRef-FILE-Lib-ctypes-macholib-dylib.py", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "f339420cc01bd01f8d0da19b6102f099075e8bcd" + }, + { + "algorithm": "SHA256", + "checksumValue": "f19ee056b18165cc6735efab0b4ca3508be9405b9646c38113316c15e8278a6f" + } + ], + "fileName": "Lib/ctypes/macholib/dylib.py" + }, + { + "SPDXID": "SPDXRef-FILE-Lib-ctypes-macholib-framework.py", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0b219f58467d7f193fa1de0c1b118485840d855b" + }, + { + "algorithm": "SHA256", + "checksumValue": "302439e40d9cbdd61b8b7cffd0b7e1278a6811b635044ee366a36e0d991f62da" + } + ], + "fileName": "Lib/ctypes/macholib/framework.py" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-README.txt", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "bda6e0bd6121f7069b420bdc0bc7c49414d948d1" + }, + { + "algorithm": "SHA256", + "checksumValue": "89926cd0fe6cfb33a2b5b7416c101e9b5d42b0d639d348e0871acf6ffc8258a3" + } + ], + "fileName": "Modules/_decimal/libmpdec/README.txt" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-basearith.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "33757ce2ec0c93c1b5e03c45a495563a00e498ae" + }, + { + "algorithm": "SHA256", + "checksumValue": "ad498362c31a5b99ab19fce320ac540cf14c5c4ec09478f0ad3858da1428113d" + } + ], + "fileName": "Modules/_decimal/libmpdec/basearith.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-basearith.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "bf03919412c068e6969e7ac48850f91bfcd3b2b1" + }, + { + "algorithm": "SHA256", + "checksumValue": "2eaac88a71b9bcf3144396c12dcfeced573e0e550a0050d75b9ed3903248596d" + } + ], + "fileName": "Modules/_decimal/libmpdec/basearith.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-bench.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "c925b7f26754ae182aaa461d51802e8b6a2bb5e9" + }, + { + "algorithm": "SHA256", + "checksumValue": "007e38542ec8d9d8805fe243b5390d79211b9360e2797a20079e833e68ad9e45" + } + ], + "fileName": "Modules/_decimal/libmpdec/bench.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-bench-full.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "cb22686269685a53a17afdea9ed984714e399d9d" + }, + { + "algorithm": "SHA256", + "checksumValue": "1b9e892d4b268deea835ec8906f20a1e5d25e037b2e698edcd34315613f3608c" + } + ], + "fileName": "Modules/_decimal/libmpdec/bench_full.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-bits.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "fc91c2450cdf1e785d1347411662294c3945eb27" + }, + { + "algorithm": "SHA256", + "checksumValue": "ce7741e58ea761a24250c0bfa10058cec8c4fd220dca70a41de3927a2e4f5376" + } + ], + "fileName": "Modules/_decimal/libmpdec/bits.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-constants.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "7187c18916b0a546ec19b4fc4bec43d0d9fb5fc2" + }, + { + "algorithm": "SHA256", + "checksumValue": "cd430b8657cf8a616916e02f9bd5ca044d5fc19e69333f5d427e1fdb90b0864b" + } + ], + "fileName": "Modules/_decimal/libmpdec/constants.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-constants.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "af9cbd016fb0ef0b30ced49c0aa4ce2ca3c20125" + }, + { + "algorithm": "SHA256", + "checksumValue": "19dc46df04abb7ee08e9a403f87c8aac8d4a077efcce314c597f8b73e22884f2" + } + ], + "fileName": "Modules/_decimal/libmpdec/constants.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-context.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "666162870230bebd3f2383020d908806fd03909e" + }, + { + "algorithm": "SHA256", + "checksumValue": "9a265d366f31894aad78bca7fcdc1457bc4a3aa3887ca231b7d78e41f79541c0" + } + ], + "fileName": "Modules/_decimal/libmpdec/context.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-convolute.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0545547a8b37b922fbe2574fbad8fc3bf16f1d33" + }, + { + "algorithm": "SHA256", + "checksumValue": "66fe27b9bb37039cad5be32b105ed509e5aefa15c1957a9058af8ee23cddc97a" + } + ], + "fileName": "Modules/_decimal/libmpdec/convolute.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-convolute.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "05ff0936c5bb08f40d460f5843004a1cc0751d9b" + }, + { + "algorithm": "SHA256", + "checksumValue": "c00d17450c2b8e1d7f1eb8a084f7e6a68f257a453f8701600e860bf357c531d7" + } + ], + "fileName": "Modules/_decimal/libmpdec/convolute.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-crt.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "fe8176849bc99a306332ba25caa4e91bfa3c6f7d" + }, + { + "algorithm": "SHA256", + "checksumValue": "1f4e65c44864c3e911a6e91f33adec76765293e90553459e3ebce35a58898dba" + } + ], + "fileName": "Modules/_decimal/libmpdec/crt.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-crt.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "1930b9e0910014b3479aec4e940f02118d9e4a08" + }, + { + "algorithm": "SHA256", + "checksumValue": "7d31f1d0dd73b62964dab0f7a1724473bf87f1f95d8febf0b40c15430ae9a47c" + } + ], + "fileName": "Modules/_decimal/libmpdec/crt.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-difradix2.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "415c51e7d7f517b6366bec2a809610d0d38ada14" + }, + { + "algorithm": "SHA256", + "checksumValue": "0a9fef8a374f55277e9f6000b7277bb037b9763c32b156c29950422b057498bd" + } + ], + "fileName": "Modules/_decimal/libmpdec/difradix2.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-difradix2.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "d8a998c3bee4c3d9059ba7bf9ae6a8b64649c2ba" + }, + { + "algorithm": "SHA256", + "checksumValue": "5c6766496224de657400995b58b64db3e7084004bf00daebdd7e08d0c5995243" + } + ], + "fileName": "Modules/_decimal/libmpdec/difradix2.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-README.txt", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "158f6ad18edf348efa4fdd7cf61114c77c1d22e9" + }, + { + "algorithm": "SHA256", + "checksumValue": "7b0da2758097a2688f06b3c7ca46b2ebc8329addbd28bb4f5fe95626cc81f8a9" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/README.txt" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-compare.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "ef80ba26847287fb351ab0df0a78b5f08ba0b5b7" + }, + { + "algorithm": "SHA256", + "checksumValue": "452666ee4eb10a8cf0a926cb3bcf5e95b5c361fa129dbdfe27b654e6d640417e" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/compare.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-div.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "6ca3a369b3d1e140fdc93c4fdbedb724f7daf969" + }, + { + "algorithm": "SHA256", + "checksumValue": "6d369f5a24d0bb1e7cb6a4f8b0e97a273260e7668c8a540a8fcc92e039f7af2e" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/div.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-divmod.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "3872a28b4f77e07e1760256067ea338a8dd183f8" + }, + { + "algorithm": "SHA256", + "checksumValue": "5db54bae75ac3d7fa12f1bb0f7ce1bf797df86a81030e8c3ce44d3b1f9b958b7" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/divmod.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-multiply.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "25dbc94fd4ee5dec21061d2d40dd5d0f88849cb1" + }, + { + "algorithm": "SHA256", + "checksumValue": "22ed39b18fa740a27aacfd29a7bb40066be24500ba49b9b1f24e2af1e039fcd9" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/multiply.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-pow.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "13d3b7657dc2dc5000fea428f57963d520792ef7" + }, + { + "algorithm": "SHA256", + "checksumValue": "cd8c037649b3d4d6897c9acd2f92f3f9d5390433061d5e48623a5d526a3f4f9c" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/pow.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-powmod.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "1f7e6c3d3e38df52bbcec0f5a180a8f328679618" + }, + { + "algorithm": "SHA256", + "checksumValue": "e29614b43abf1856b656a84d6b67c22cc5dc7af8cbae8ddc7acf17022220ee12" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/powmod.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-shift.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0bd9ce89c7987d1109eb7b0c8f1f9a1298e1422e" + }, + { + "algorithm": "SHA256", + "checksumValue": "203f2dbf11d115580cb3c7c524ac6ccca2a7b31d89545db1b6263381b5de2b6a" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/shift.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-sqrt.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "b401ba0814e17c9164c0df26e01cc0a355382f46" + }, + { + "algorithm": "SHA256", + "checksumValue": "f3dc2ce321833bbd4b3d1d9ea6fa2e0bcc1bfe1e39abb8d55be53e46c33949db" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/sqrt.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-fnt.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "060615ddef089a5a8f879a57e4968d920972a0e2" + }, + { + "algorithm": "SHA256", + "checksumValue": "a9f923524d53a9445769f27405375ec3d95fa804bb11db5ee249ae047f11cfce" + } + ], + "fileName": "Modules/_decimal/libmpdec/fnt.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-fnt.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "b205043ebeaf065b16505a299342a992654f19b0" + }, + { + "algorithm": "SHA256", + "checksumValue": "3b03e69adf78fde68c8f87d33595d557237581d33fc067e1039eed9e9f2cc44c" + } + ], + "fileName": "Modules/_decimal/libmpdec/fnt.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-fourstep.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "702c27599b43280c94906235d7e1a74193ba701b" + }, + { + "algorithm": "SHA256", + "checksumValue": "cf2e69b946ec14b087e523c0ff606553070d13c23e851fb0ba1df51a728017e6" + } + ], + "fileName": "Modules/_decimal/libmpdec/fourstep.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-fourstep.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "ee5291c265ef1f5ae373bc243a4d96975eb3e7b5" + }, + { + "algorithm": "SHA256", + "checksumValue": "dbaced03b52d0f880c377b86c943bcb36f24d557c99a5e9732df3ad5debb5917" + } + ], + "fileName": "Modules/_decimal/libmpdec/fourstep.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-io.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "12402bcf7f0161adb83f78163f41cc10a5e5de5f" + }, + { + "algorithm": "SHA256", + "checksumValue": "cba044c76b6bc3ae6cfa49df1121cad7552140157b9e61e11cbb6580cc5d74cf" + } + ], + "fileName": "Modules/_decimal/libmpdec/io.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-io.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "28c653cd40b1ce46575e41f5dbfda5f6dd0db4d1" + }, + { + "algorithm": "SHA256", + "checksumValue": "259eab89fe27914e0e39e61199094a357ac60d86b2aab613c909040ff64a4a0c" + } + ], + "fileName": "Modules/_decimal/libmpdec/io.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-REFERENCES.txt", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "218d1d7bedb335cd2c31eae89a15873c3139e13f" + }, + { + "algorithm": "SHA256", + "checksumValue": "a57e8bed93ded481ef264166aec2c49d1a7f3252f29a873ee41fff053cfd9c20" + } + ], + "fileName": "Modules/_decimal/libmpdec/literature/REFERENCES.txt" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-bignum.txt", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "f67eab2431336cf6eeafb30cdafd7e54c251def3" + }, + { + "algorithm": "SHA256", + "checksumValue": "dc34aa122c208ce79e3fc6baee8628094ffaf6a662862dd5647836241f6ebd79" + } + ], + "fileName": "Modules/_decimal/libmpdec/literature/bignum.txt" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-fnt.py", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "a58cfbcd8ea57d66ddfd11fb5a170138c8bbfb3a" + }, + { + "algorithm": "SHA256", + "checksumValue": "122de20eebf87274af2d02072251a94500e7df2d5ef29e81aeabeda991c079e3" + } + ], + "fileName": "Modules/_decimal/libmpdec/literature/fnt.py" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-matrix-transform.txt", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "9a947f6b660150cbd457c4458da2956a36c5824d" + }, + { + "algorithm": "SHA256", + "checksumValue": "592659e7192e3a939b797f5bc7455455834a285f5d8b643ccd780b5114914f73" + } + ], + "fileName": "Modules/_decimal/libmpdec/literature/matrix-transform.txt" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-mulmod-64.txt", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "69fe9afb8353b5a2b57917469c51c64ac518169d" + }, + { + "algorithm": "SHA256", + "checksumValue": "229a80ca940c594a32e3345412370cbc097043fe59c66a6153cbcf01e7837266" + } + ], + "fileName": "Modules/_decimal/libmpdec/literature/mulmod-64.txt" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-mulmod-ppro.txt", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "720d468a1f51098036c7a0c869810fff22ed9b79" + }, + { + "algorithm": "SHA256", + "checksumValue": "f3549fc73f697a087267c7b042e30a409e191cbba69a2c0902685e507fbae9f7" + } + ], + "fileName": "Modules/_decimal/libmpdec/literature/mulmod-ppro.txt" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-six-step.txt", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "6815ec3a39baebebe7b3f51d45d10c180a659f17" + }, + { + "algorithm": "SHA256", + "checksumValue": "bf15f73910a173c98fca9db56122b6cc71983668fa8b934c46ca21a57398ec54" + } + ], + "fileName": "Modules/_decimal/libmpdec/literature/six-step.txt" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-umodarith.lisp", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "c91ac4438e661ce78f86e981257546e5adff39ae" + }, + { + "algorithm": "SHA256", + "checksumValue": "783a1b4b9b7143677b0c3d30ffaf28aa0cb01956409031fa38ed8011970bdee0" + } + ], + "fileName": "Modules/_decimal/libmpdec/literature/umodarith.lisp" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-mpalloc.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "7e8dfb4b7a801b48c501969b001153203b14679e" + }, + { + "algorithm": "SHA256", + "checksumValue": "5ba2f4c80302e71fb216aa247c858e0bf6c8cfabffe7980ac17d4d023c0fef2b" + } + ], + "fileName": "Modules/_decimal/libmpdec/mpalloc.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-mpalloc.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "bccb6a6ae76fd7f6c8a9102a78958bcad7862950" + }, + { + "algorithm": "SHA256", + "checksumValue": "f7412521de38afb837fcabc2b1d48b971b86bfaa55f3f40d58ff9e46e92debd3" + } + ], + "fileName": "Modules/_decimal/libmpdec/mpalloc.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-mpdecimal.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "f4539afb1ace58c52d18ffd0cc7704f53ca55182" + }, + { + "algorithm": "SHA256", + "checksumValue": "4f89b8095e408a18deff79cfb605299e615bae747898eb105d8936064f7fb626" + } + ], + "fileName": "Modules/_decimal/libmpdec/mpdecimal.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-mpdecimal.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "4b80e25ac49b7e1ea0d1e84967ee32a3d111fc4c" + }, + { + "algorithm": "SHA256", + "checksumValue": "ea0b9c6b296c13aed6ecaa50b463e39a9c1bdc059b84f50507fd8247b2e660f9" + } + ], + "fileName": "Modules/_decimal/libmpdec/mpdecimal.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-mpsignal.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "5c7305a6db0fddf64c6d97e29d3b0c402e3d5d6e" + }, + { + "algorithm": "SHA256", + "checksumValue": "653171cf2549719478417db7e9800fa0f9d99c02dec6da6876329ccf2c07b93f" + } + ], + "fileName": "Modules/_decimal/libmpdec/mpsignal.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-numbertheory.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "d736b874c43777ca021dde5289ea718893f39219" + }, + { + "algorithm": "SHA256", + "checksumValue": "bdbf2e246f341a3ba3f6f9d8759e7cb222eb9b15f9ed1e7c9f6a59cbb9f8bc91" + } + ], + "fileName": "Modules/_decimal/libmpdec/numbertheory.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-numbertheory.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "d341508d8c6dd4c4cbd8b99afc8029945f9bbe0d" + }, + { + "algorithm": "SHA256", + "checksumValue": "2f7d5b40af508fa6ac86f5d62101fa3bf683c63b24aa87c9548e3fdd13abc57b" + } + ], + "fileName": "Modules/_decimal/libmpdec/numbertheory.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-sixstep.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "cbd05d68bb3940d0d7d0818b14cc03b090a4dd74" + }, + { + "algorithm": "SHA256", + "checksumValue": "7602aaf98ec9525bc4b3cab9631615e1be2efd9af894002ef4e3f5ec63924fcf" + } + ], + "fileName": "Modules/_decimal/libmpdec/sixstep.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-sixstep.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "4c059463ec4b4522562dab24760fc64c172c9eee" + }, + { + "algorithm": "SHA256", + "checksumValue": "a191366348b3d3dd49b9090ec5c77dbd77bb3a523c01ff32adafa137e5097ce7" + } + ], + "fileName": "Modules/_decimal/libmpdec/sixstep.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-transpose.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "cc5593ac9fdb60480cc23fc9d6f27d85670bd35f" + }, + { + "algorithm": "SHA256", + "checksumValue": "2d12fcae512143a9376c8a0d4c1ba3008e420e024497a7e7ec64c6bec23fcddc" + } + ], + "fileName": "Modules/_decimal/libmpdec/transpose.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-transpose.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "2f616425756b6cbdf7d189744870b98b613455bd" + }, + { + "algorithm": "SHA256", + "checksumValue": "fafeb2b901b2b41bf0df00be7d99b84df1a78e3cc1e582e09cbfc3b6d44d4abe" + } + ], + "fileName": "Modules/_decimal/libmpdec/transpose.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-typearith.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "b1e9341e173cc8e219ad4aa45fad36d92cce10d3" + }, + { + "algorithm": "SHA256", + "checksumValue": "25e0a0703b51744277834e6b2398d7b7d2c17f92bf30f8b6f949e0486ae2b346" + } + ], + "fileName": "Modules/_decimal/libmpdec/typearith.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-umodarith.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "46f6483fce136cd3cc2f7516ee119a487d86333e" + }, + { + "algorithm": "SHA256", + "checksumValue": "bfe1ddb2ca92906456b80745adcbe02c83cadac3ef69caa21bc09b7292cc152b" + } + ], + "fileName": "Modules/_decimal/libmpdec/umodarith.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-vcdiv64.asm", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "d0cc1052fcba08b773d935b0ae2dc6b80d0f2f68" + }, + { + "algorithm": "SHA256", + "checksumValue": "aacc3e47ea8f41e8840c6c67f64ec96d54696a16889903098fa1aab56949a00f" + } + ], + "fileName": "Modules/_decimal/libmpdec/vcdiv64.asm" + }, + { + "SPDXID": "SPDXRef-FILE-Lib-ensurepip-bundled-pip-23.3.1-py3-none-any.whl", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "4b2baddc0673f73017e531648a9ee27e47925e7a" + }, + { + "algorithm": "SHA256", + "checksumValue": "55eb67bb6171d37447e82213be585b75fe2b12b359e993773aca4de9247a052b" + } + ], + "fileName": "Lib/ensurepip/_bundled/pip-23.3.1-py3-none-any.whl" + } + ], + "packages": [ + { + "SPDXID": "SPDXRef-PACKAGE-expat", + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "6b902ab103843592be5e99504f846ec109c1abb692e85347587f237a4ffa1033" + } + ], + "downloadLocation": "https://github.com/libexpat/libexpat/releases/download/R_2_5_0/expat-2.5.0.tar.gz", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceLocator": "cpe:2.3:a:libexpat_project:libexpat:2.5.0:*:*:*:*:*:*:*", + "referenceType": "cpe23Type" + } + ], + "licenseConcluded": "MIT", + "name": "expat", + "originator": "Organization: Expat development team", + "primaryPackagePurpose": "SOURCE", + "versionInfo": "2.5.0" + }, + { + "SPDXID": "SPDXRef-PACKAGE-hacl-star", + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "c23ac158b238c368389dc86bfc315263e5c0e57785da74144aea2cab9a3d51a2" + } + ], + "downloadLocation": "https://github.com/hacl-star/hacl-star/archive/521af282fdf6d60227335120f18ae9309a4b8e8c.zip", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceLocator": "cpe:2.3:a:hacl-star:hacl-star:521af282fdf6d60227335120f18ae9309a4b8e8c:*:*:*:*:*:*:*", + "referenceType": "cpe23Type" + } + ], + "licenseConcluded": "Apache-2.0", + "name": "hacl-star", + "originator": "Organization: HACL* Developers", + "primaryPackagePurpose": "SOURCE", + "versionInfo": "521af282fdf6d60227335120f18ae9309a4b8e8c" + }, + { + "SPDXID": "SPDXRef-PACKAGE-libb2", + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "53626fddce753c454a3fea581cbbc7fe9bbcf0bc70416d48fdbbf5d87ef6c72e" + } + ], + "downloadLocation": "https://github.com/BLAKE2/libb2/releases/download/v0.98.1/libb2-0.98.1.tar.gz", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceLocator": "cpe:2.3:a:blake2:libb2:0.98.1:*:*:*:*:*:*:*", + "referenceType": "cpe23Type" + } + ], + "licenseConcluded": "CC0-1.0", + "name": "libb2", + "originator": "Organization: BLAKE2 - fast secure hashing", + "primaryPackagePurpose": "SOURCE", + "versionInfo": "0.98.1" + }, + { + "SPDXID": "SPDXRef-PACKAGE-macholib", + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "c76f268f5054024e962f2515a0e522baf85313064f6740d80375afc850787a38" + } + ], + "downloadLocation": "https://files.pythonhosted.org/packages/ec/57/f0a712efc3ed982cf4038a3cee172057303b9be914c32c93b2fbec27f785/macholib-1.0.tar.gz", + "externalRefs": [ + { + "referenceCategory": "PACKAGE_MANAGER", + "referenceLocator": "pkg:pypi/macholib@1.0", + "referenceType": "purl" + } + ], + "licenseConcluded": "MIT", + "name": "macholib", + "originator": "Person: Ronald Oussoren (ronaldoussoren@mac.com)", + "primaryPackagePurpose": "SOURCE", + "versionInfo": "1.0" + }, + { + "SPDXID": "SPDXRef-PACKAGE-mpdecimal", + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "9f9cd4c041f99b5c49ffb7b59d9f12d95b683d88585608aa56a6307667b2b21f" + } + ], + "downloadLocation": "https://www.bytereef.org/software/mpdecimal/releases/mpdecimal-2.5.1.tar.gz", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceLocator": "cpe:2.3:a:bytereef:mpdecimal:2.5.1:*:*:*:*:*:*:*", + "referenceType": "cpe23Type" + } + ], + "licenseConcluded": "BSD-2-Clause", + "name": "mpdecimal", + "originator": "Organization: bytereef.org", + "primaryPackagePurpose": "SOURCE", + "versionInfo": "2.5.1" + }, + { + "SPDXID": "SPDXRef-PACKAGE-pip", + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "7ccf472345f20d35bdc9d1841ff5f313260c2c33fe417f48c30ac46cccabf5be" + } + ], + "downloadLocation": "https://files.pythonhosted.org/packages/50/c2/e06851e8cc28dcad7c155f4753da8833ac06a5c704c109313b8d5a62968a/pip-23.2.1-py3-none-any.whl", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceLocator": "cpe:2.3:a:pypa:pip:23.2.1:*:*:*:*:*:*:*", + "referenceType": "cpe23Type" + }, + { + "referenceCategory": "PACKAGE_MANAGER", + "referenceLocator": "pkg:pypi/pip@23.2.1", + "referenceType": "purl" + } + ], + "licenseConcluded": "MIT", + "name": "pip", + "originator": "Organization: Python Packaging Authority", + "primaryPackagePurpose": "SOURCE", + "versionInfo": "23.2.1" + } + ], + "relationships": [ + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-COPYING", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-ascii.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-asciitab.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-expat.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-expat-config.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-expat-external.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-iasciitab.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-internal.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-latin1tab.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-nametab.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-pyexpatns.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-siphash.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-utf8tab.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-winconfig.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-xmlparse.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-xmlrole.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-xmlrole.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-xmltok.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-xmltok.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-xmltok-impl.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-xmltok-impl.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-xmltok-ns.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-MD5.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-MD5.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA1.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA1.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA2.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA2.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA3.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA3.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Streaming-Types.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-include-krml-FStar-UInt128-Verified.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-include-krml-FStar-UInt-8-16-32-64.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-include-krml-fstar-uint128-struct-endianness.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-include-krml-internal-target.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-include-krml-lowstar-endianness.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-include-krml-types.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-internal-Hacl-Hash-MD5.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-internal-Hacl-Hash-SHA1.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-internal-Hacl-Hash-SHA2.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-internal-Hacl-Hash-SHA3.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-python-hacl-namespaces.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2-config.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2-impl.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2b-load-sse2.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2b-load-sse41.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2b-ref.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2b-round.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2b.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2s-load-sse2.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2s-load-sse41.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2s-load-xop.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2s-ref.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2s-round.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2s.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Lib-ctypes-macholib-init-.py", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-macholib" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Lib-ctypes-macholib-dyld.py", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-macholib" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Lib-ctypes-macholib-dylib.py", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-macholib" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Lib-ctypes-macholib-framework.py", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-macholib" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-README.txt", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-basearith.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-basearith.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-bench.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-bench-full.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-bits.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-constants.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-constants.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-context.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-convolute.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-convolute.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-crt.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-crt.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-difradix2.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-difradix2.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-README.txt", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-compare.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-div.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-divmod.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-multiply.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-pow.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-powmod.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-shift.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-sqrt.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-fnt.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-fnt.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-fourstep.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-fourstep.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-io.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-io.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-REFERENCES.txt", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-bignum.txt", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-fnt.py", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-matrix-transform.txt", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-mulmod-64.txt", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-mulmod-ppro.txt", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-six-step.txt", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-umodarith.lisp", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-mpalloc.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-mpalloc.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-mpdecimal.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-mpdecimal.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-mpsignal.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-numbertheory.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-numbertheory.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-sixstep.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-sixstep.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-transpose.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-transpose.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-typearith.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-umodarith.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-vcdiv64.asm", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Lib-ensurepip-bundled-pip-23.3.1-py3-none-any.whl", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-pip" + } + ], + "spdxVersion": "SPDX-2.3" +} \ No newline at end of file diff --git a/Tools/build/generate_sbom.py b/Tools/build/generate_sbom.py new file mode 100644 index 00000000000000..0089db81af9b9d --- /dev/null +++ b/Tools/build/generate_sbom.py @@ -0,0 +1,179 @@ +"""Tool for generating Software Bill of Materials (SBOM) for Python's dependencies""" + +import re +import hashlib +import json +import glob +import pathlib +import subprocess +import typing + +# Before adding a new entry to this list, double check that +# the license expression is a valid SPDX license expression: +# See: https://spdx.org/licenses +ALLOWED_LICENSE_EXPRESSIONS = { + "MIT", + "CC0-1.0", + "Apache-2.0", + "BSD-2-Clause", +} + +# Properties which are required for our purposes. +REQUIRED_PROPERTIES_PACKAGE = frozenset([ + "SPDXID", + "name", + "versionInfo", + "downloadLocation", + "checksums", + "licenseConcluded", + "externalRefs", + "originator", + "primaryPackagePurpose", +]) + + +class PackageFiles(typing.NamedTuple): + """Structure for describing the files of a package""" + include: list[str] + exclude: list[str] | None = None + + +# SBOMS don't have a method to specify the sources of files +# so we need to do that external to the SBOM itself. Add new +# values to 'exclude' if we create new files within tracked +# directories that aren't sourced from third-party packages. +PACKAGE_TO_FILES = { + "mpdecimal": PackageFiles( + include=["Modules/_decimal/libmpdec/**"] + ), + "expat": PackageFiles( + include=["Modules/expat/**"] + ), + "pip": PackageFiles( + include=["Lib/ensurepip/_bundled/pip-23.3.1-py3-none-any.whl"] + ), + "macholib": PackageFiles( + include=["Lib/ctypes/macholib/**"], + exclude=[ + "Lib/ctypes/macholib/README.ctypes", + "Lib/ctypes/macholib/fetch_macholib", + "Lib/ctypes/macholib/fetch_macholib.bat", + ], + ), + "libb2": PackageFiles( + include=["Modules/_blake2/impl/**"] + ), + "hacl-star": PackageFiles( + include=["Modules/_hacl/**"], + exclude=[ + "Modules/_hacl/refresh.sh", + "Modules/_hacl/README.md", + "Modules/_hacl/python_hacl_namespace.h", + ] + ), +} + + +def spdx_id(value: str) -> str: + """Encode a value into characters that are valid in an SPDX ID""" + return re.sub(r"[^a-zA-Z0-9.\-]+", "-", value) + + +def filter_gitignored_paths(paths: list[str]) -> list[str]: + """ + Filter out paths excluded by the gitignore file. + The output of 'git check-ignore --non-matching --verbose' looks + like this for non-matching (included) files: + + '::' + + And looks like this for matching (excluded) files: + + '.gitignore:9:*.a Tools/lib.a' + """ + # Filter out files in gitignore. + # Non-matching files show up as '::' + git_check_ignore_proc = subprocess.run( + ["git", "check-ignore", "--verbose", "--non-matching", *paths], + check=False, + stdout=subprocess.PIPE, + ) + # 1 means matches, 0 means no matches. + assert git_check_ignore_proc.returncode in (0, 1) + + # Return the list of paths sorted + git_check_ignore_lines = git_check_ignore_proc.stdout.decode().splitlines() + return sorted([line.split()[-1] for line in git_check_ignore_lines if line.startswith("::")]) + + +def main() -> None: + root_dir = pathlib.Path(__file__).parent.parent.parent + sbom_path = root_dir / "Misc/sbom.spdx.json" + sbom_data = json.loads(sbom_path.read_bytes()) + + # Make a bunch of assertions about the SBOM data to ensure it's consistent. + assert {package["name"] for package in sbom_data["packages"]} == set(PACKAGE_TO_FILES) + for package in sbom_data["packages"]: + + # Properties and ID must be properly formed. + assert set(package.keys()) == REQUIRED_PROPERTIES_PACKAGE + assert package["SPDXID"] == spdx_id(f"SPDXRef-PACKAGE-{package['name']}") + + # Version must be in the download and external references. + version = package["versionInfo"] + assert version in package["downloadLocation"] + assert all(version in ref["referenceLocator"] for ref in package["externalRefs"]) + + # License must be on the approved list for SPDX. + assert package["licenseConcluded"] in ALLOWED_LICENSE_EXPRESSIONS, package["licenseConcluded"] + + # Regenerate file information from current data. + sbom_files = [] + sbom_relationships = [] + + # We call 'sorted()' here a lot to avoid filesystem scan order issues. + for name, files in sorted(PACKAGE_TO_FILES.items()): + package_spdx_id = spdx_id(f"SPDXRef-PACKAGE-{name}") + exclude = files.exclude or () + for include in sorted(files.include): + + # Find all the paths and then filter them through .gitignore. + paths = glob.glob(include, root_dir=root_dir, recursive=True) + paths = filter_gitignored_paths(paths) + assert paths, include # Make sure that every value returns something! + + for path in paths: + # Skip directories and excluded files + if not (root_dir / path).is_file() or path in exclude: + continue + + # SPDX requires SHA1 to be used for files, but we provide SHA256 too. + data = (root_dir / path).read_bytes() + checksum_sha1 = hashlib.sha1(data).hexdigest() + checksum_sha256 = hashlib.sha256(data).hexdigest() + + file_spdx_id = spdx_id(f"SPDXRef-FILE-{path}") + sbom_files.append({ + "SPDXID": file_spdx_id, + "fileName": path, + "checksums": [ + {"algorithm": "SHA1", "checksumValue": checksum_sha1}, + {"algorithm": "SHA256", "checksumValue": checksum_sha256}, + ], + }) + + # Tie each file back to its respective package. + sbom_relationships.append({ + "spdxElementId": package_spdx_id, + "relatedSpdxElement": file_spdx_id, + "relationshipType": "CONTAINS", + }) + + # Update the SBOM on disk + sbom_data["files"] = sbom_files + sbom_data["relationships"] = sbom_relationships + sbom_path.write_text(json.dumps(sbom_data, indent=2, sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/Tools/build/mypy.ini b/Tools/build/mypy.ini new file mode 100644 index 00000000000000..cf1dac7fde5ac5 --- /dev/null +++ b/Tools/build/mypy.ini @@ -0,0 +1,13 @@ +[mypy] +files = Tools/build/generate_sbom.py +pretty = True + +# Make sure Python can still be built +# using Python 3.10 for `PYTHON_FOR_REGEN`... +python_version = 3.10 + +# ...And be strict: +strict = True +strict_concatenate = True +enable_error_code = ignore-without-code,redundant-expr,truthy-bool,possibly-undefined +warn_unreachable = True From 81c16cd94ec38d61aa478b9a452436dc3b1b524d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20L=C3=B8vborg?= Date: Thu, 7 Dec 2023 17:04:06 +0100 Subject: [PATCH 213/228] gh-91133: tempfile.TemporaryDirectory: fix symlink bug in cleanup (GH-99930) Co-authored-by: Serhiy Storchaka --- Lib/tempfile.py | 27 +++-- Lib/test/test_tempfile.py | 111 +++++++++++++++++- ...2-12-01-16-57-44.gh-issue-91133.LKMVCV.rst | 2 + 3 files changed, 125 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst diff --git a/Lib/tempfile.py b/Lib/tempfile.py index 55403ad1faf46d..9a5e7d01c2379b 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -270,6 +270,22 @@ def _mkstemp_inner(dir, pre, suf, flags, output_type): raise FileExistsError(_errno.EEXIST, "No usable temporary file name found") +def _dont_follow_symlinks(func, path, *args): + # Pass follow_symlinks=False, unless not supported on this platform. + if func in _os.supports_follow_symlinks: + func(path, *args, follow_symlinks=False) + elif _os.name == 'nt' or not _os.path.islink(path): + func(path, *args) + +def _resetperms(path): + try: + chflags = _os.chflags + except AttributeError: + pass + else: + _dont_follow_symlinks(chflags, path, 0) + _dont_follow_symlinks(_os.chmod, path, 0o700) + # User visible interfaces. @@ -876,17 +892,10 @@ def __init__(self, suffix=None, prefix=None, dir=None, def _rmtree(cls, name, ignore_errors=False): def onexc(func, path, exc): if isinstance(exc, PermissionError): - def resetperms(path): - try: - _os.chflags(path, 0) - except AttributeError: - pass - _os.chmod(path, 0o700) - try: if path != name: - resetperms(_os.path.dirname(path)) - resetperms(path) + _resetperms(_os.path.dirname(path)) + _resetperms(path) try: _os.unlink(path) diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index f4aef887799ed4..2729bec7a21c71 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -1673,6 +1673,103 @@ def test_cleanup_with_symlink_to_a_directory(self): "were deleted") d2.cleanup() + @os_helper.skip_unless_symlink + def test_cleanup_with_symlink_modes(self): + # cleanup() should not follow symlinks when fixing mode bits (#91133) + with self.do_create(recurse=0) as d2: + file1 = os.path.join(d2, 'file1') + open(file1, 'wb').close() + dir1 = os.path.join(d2, 'dir1') + os.mkdir(dir1) + for mode in range(8): + mode <<= 6 + with self.subTest(mode=format(mode, '03o')): + def test(target, target_is_directory): + d1 = self.do_create(recurse=0) + symlink = os.path.join(d1.name, 'symlink') + os.symlink(target, symlink, + target_is_directory=target_is_directory) + try: + os.chmod(symlink, mode, follow_symlinks=False) + except NotImplementedError: + pass + try: + os.chmod(symlink, mode) + except FileNotFoundError: + pass + os.chmod(d1.name, mode) + d1.cleanup() + self.assertFalse(os.path.exists(d1.name)) + + with self.subTest('nonexisting file'): + test('nonexisting', target_is_directory=False) + with self.subTest('nonexisting dir'): + test('nonexisting', target_is_directory=True) + + with self.subTest('existing file'): + os.chmod(file1, mode) + old_mode = os.stat(file1).st_mode + test(file1, target_is_directory=False) + new_mode = os.stat(file1).st_mode + self.assertEqual(new_mode, old_mode, + '%03o != %03o' % (new_mode, old_mode)) + + with self.subTest('existing dir'): + os.chmod(dir1, mode) + old_mode = os.stat(dir1).st_mode + test(dir1, target_is_directory=True) + new_mode = os.stat(dir1).st_mode + self.assertEqual(new_mode, old_mode, + '%03o != %03o' % (new_mode, old_mode)) + + @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags') + @os_helper.skip_unless_symlink + def test_cleanup_with_symlink_flags(self): + # cleanup() should not follow symlinks when fixing flags (#91133) + flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK + self.check_flags(flags) + + with self.do_create(recurse=0) as d2: + file1 = os.path.join(d2, 'file1') + open(file1, 'wb').close() + dir1 = os.path.join(d2, 'dir1') + os.mkdir(dir1) + def test(target, target_is_directory): + d1 = self.do_create(recurse=0) + symlink = os.path.join(d1.name, 'symlink') + os.symlink(target, symlink, + target_is_directory=target_is_directory) + try: + os.chflags(symlink, flags, follow_symlinks=False) + except NotImplementedError: + pass + try: + os.chflags(symlink, flags) + except FileNotFoundError: + pass + os.chflags(d1.name, flags) + d1.cleanup() + self.assertFalse(os.path.exists(d1.name)) + + with self.subTest('nonexisting file'): + test('nonexisting', target_is_directory=False) + with self.subTest('nonexisting dir'): + test('nonexisting', target_is_directory=True) + + with self.subTest('existing file'): + os.chflags(file1, flags) + old_flags = os.stat(file1).st_flags + test(file1, target_is_directory=False) + new_flags = os.stat(file1).st_flags + self.assertEqual(new_flags, old_flags) + + with self.subTest('existing dir'): + os.chflags(dir1, flags) + old_flags = os.stat(dir1).st_flags + test(dir1, target_is_directory=True) + new_flags = os.stat(dir1).st_flags + self.assertEqual(new_flags, old_flags) + @support.cpython_only def test_del_on_collection(self): # A TemporaryDirectory is deleted when garbage collected @@ -1845,10 +1942,7 @@ def test_modes(self): d.cleanup() self.assertFalse(os.path.exists(d.name)) - @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags') - def test_flags(self): - flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK - + def check_flags(self, flags): # skip the test if these flags are not supported (ex: FreeBSD 13) filename = os_helper.TESTFN try: @@ -1857,13 +1951,18 @@ def test_flags(self): os.chflags(filename, flags) except OSError as exc: # "OSError: [Errno 45] Operation not supported" - self.skipTest(f"chflags() doesn't support " - f"UF_IMMUTABLE|UF_NOUNLINK: {exc}") + self.skipTest(f"chflags() doesn't support flags " + f"{flags:#b}: {exc}") else: os.chflags(filename, 0) finally: os_helper.unlink(filename) + @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags') + def test_flags(self): + flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK + self.check_flags(flags) + d = self.do_create(recurse=3, dirs=2, files=2) with d: # Change files and directories flags recursively. diff --git a/Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst b/Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst new file mode 100644 index 00000000000000..7991048fc48e03 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst @@ -0,0 +1,2 @@ +Fix a bug in :class:`tempfile.TemporaryDirectory` cleanup, which now no longer +dereferences symlinks when working around file system permission errors. From ba18893555bbf69b1da262aaf85d65e4b67e8955 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 7 Dec 2023 18:32:10 +0200 Subject: [PATCH 214/228] gh-87319: Simplify TemporaryDirectory cleanup using os.path.isjunction() (GH-112791) --- Lib/tempfile.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/Lib/tempfile.py b/Lib/tempfile.py index 9a5e7d01c2379b..4d99f91e1f53b7 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -41,7 +41,6 @@ import io as _io import os as _os import shutil as _shutil -import stat as _stat import errno as _errno from random import Random as _Random import sys as _sys @@ -909,18 +908,7 @@ def onexc(func, path, exc): # raise NotADirectoryError and mask the PermissionError. # So we must re-raise the current PermissionError if # path is not a directory. - try: - st = _os.lstat(path) - except OSError: - if ignore_errors: - return - raise - if (_stat.S_ISLNK(st.st_mode) or - not _stat.S_ISDIR(st.st_mode) or - (hasattr(st, 'st_file_attributes') and - st.st_file_attributes & _stat.FILE_ATTRIBUTE_REPARSE_POINT and - st.st_reparse_tag == _stat.IO_REPARSE_TAG_MOUNT_POINT) - ): + if not _os.path.isdir(path) or _os.path.isjunction(path): if ignore_errors: return raise From b2923a61a10dc2717f4662b590cc9f6d181c6983 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 7 Dec 2023 19:21:36 +0200 Subject: [PATCH 215/228] gh-79325: Fix recursion error in TemporaryDirectory cleanup on Windows (GH-112762) --- Lib/tempfile.py | 10 ++++++++-- Lib/test/test_tempfile.py | 11 +++++++++++ .../2023-12-05-18-57-53.gh-issue-79325.P2vMVK.rst | 2 ++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-12-05-18-57-53.gh-issue-79325.P2vMVK.rst diff --git a/Lib/tempfile.py b/Lib/tempfile.py index 4d99f91e1f53b7..cbfc172a789b25 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -888,9 +888,14 @@ def __init__(self, suffix=None, prefix=None, dir=None, ignore_errors=self._ignore_cleanup_errors, delete=self._delete) @classmethod - def _rmtree(cls, name, ignore_errors=False): + def _rmtree(cls, name, ignore_errors=False, repeated=False): def onexc(func, path, exc): if isinstance(exc, PermissionError): + if repeated and path == name: + if ignore_errors: + return + raise + try: if path != name: _resetperms(_os.path.dirname(path)) @@ -912,7 +917,8 @@ def onexc(func, path, exc): if ignore_errors: return raise - cls._rmtree(path, ignore_errors=ignore_errors) + cls._rmtree(path, ignore_errors=ignore_errors, + repeated=(path == name)) except FileNotFoundError: pass elif isinstance(exc, FileNotFoundError): diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index 2729bec7a21c71..b64b6a4f2baeb5 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -1651,6 +1651,17 @@ def test_explicit_cleanup_correct_error(self): with self.assertRaises(PermissionError): temp_dir.cleanup() + @unittest.skipUnless(os.name == "nt", "Only on Windows.") + def test_cleanup_with_used_directory(self): + with tempfile.TemporaryDirectory() as working_dir: + temp_dir = self.do_create(dir=working_dir) + subdir = os.path.join(temp_dir.name, "subdir") + os.mkdir(subdir) + with os_helper.change_cwd(subdir): + # Previously raised RecursionError on some OSes + # (e.g. Windows). See bpo-35144. + with self.assertRaises(PermissionError): + temp_dir.cleanup() @os_helper.skip_unless_symlink def test_cleanup_with_symlink_to_a_directory(self): diff --git a/Misc/NEWS.d/next/Library/2023-12-05-18-57-53.gh-issue-79325.P2vMVK.rst b/Misc/NEWS.d/next/Library/2023-12-05-18-57-53.gh-issue-79325.P2vMVK.rst new file mode 100644 index 00000000000000..f3c32d27b5fe66 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-05-18-57-53.gh-issue-79325.P2vMVK.rst @@ -0,0 +1,2 @@ +Fix an infinite recursion error in :func:`tempfile.TemporaryDirectory` +cleanup on Windows. From a955fd68d6451bd42199110c978e99b3d2959db2 Mon Sep 17 00:00:00 2001 From: AN Long Date: Fri, 8 Dec 2023 01:26:29 +0800 Subject: [PATCH 216/228] gh-112278: Disable WMI queries on Windows after they time out (GH-112658) --- Lib/platform.py | 32 +++++++++++-------- ...-12-03-19-22-37.gh-issue-112278.FiloCE.rst | 2 ++ PC/_wmimodule.cpp | 25 +++++++++++++-- 3 files changed, 44 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-12-03-19-22-37.gh-issue-112278.FiloCE.rst diff --git a/Lib/platform.py b/Lib/platform.py index 7bb222088d5061..75aa55510858fd 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -118,6 +118,10 @@ import sys import functools import itertools +try: + import _wmi +except ImportError: + _wmi = None ### Globals & Constants @@ -312,24 +316,26 @@ def _syscmd_ver(system='', release='', version='', version = _norm_version(version) return system, release, version -try: - import _wmi -except ImportError: - def _wmi_query(*keys): + +def _wmi_query(table, *keys): + global _wmi + if not _wmi: raise OSError("not supported") -else: - def _wmi_query(table, *keys): - table = { - "OS": "Win32_OperatingSystem", - "CPU": "Win32_Processor", - }[table] + table = { + "OS": "Win32_OperatingSystem", + "CPU": "Win32_Processor", + }[table] + try: data = _wmi.exec_query("SELECT {} FROM {}".format( ",".join(keys), table, )).split("\0") - split_data = (i.partition("=") for i in data) - dict_data = {i[0]: i[2] for i in split_data} - return (dict_data[k] for k in keys) + except OSError: + _wmi = None + raise OSError("not supported") + split_data = (i.partition("=") for i in data) + dict_data = {i[0]: i[2] for i in split_data} + return (dict_data[k] for k in keys) _WIN32_CLIENT_RELEASES = [ diff --git a/Misc/NEWS.d/next/Windows/2023-12-03-19-22-37.gh-issue-112278.FiloCE.rst b/Misc/NEWS.d/next/Windows/2023-12-03-19-22-37.gh-issue-112278.FiloCE.rst new file mode 100644 index 00000000000000..0350d105d97375 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-12-03-19-22-37.gh-issue-112278.FiloCE.rst @@ -0,0 +1,2 @@ +Reduce the time cost for some functions in :mod:`platform` on Windows if +current user has no permission to the WMI. diff --git a/PC/_wmimodule.cpp b/PC/_wmimodule.cpp index fdf09ec6ec6f63..215350acfb0d8e 100644 --- a/PC/_wmimodule.cpp +++ b/PC/_wmimodule.cpp @@ -44,6 +44,7 @@ struct _query_data { LPCWSTR query; HANDLE writePipe; HANDLE readPipe; + HANDLE connectEvent; }; @@ -86,6 +87,9 @@ _query_thread(LPVOID param) NULL, NULL, 0, NULL, 0, 0, &services ); } + if (!SetEvent(data->connectEvent)) { + hr = HRESULT_FROM_WIN32(GetLastError()); + } if (SUCCEEDED(hr)) { hr = CoSetProxyBlanket( services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, @@ -231,7 +235,8 @@ _wmi_exec_query_impl(PyObject *module, PyObject *query) Py_BEGIN_ALLOW_THREADS - if (!CreatePipe(&data.readPipe, &data.writePipe, NULL, 0)) { + data.connectEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!data.connectEvent || !CreatePipe(&data.readPipe, &data.writePipe, NULL, 0)) { err = GetLastError(); } else { hThread = CreateThread(NULL, 0, _query_thread, (LPVOID*)&data, 0, NULL); @@ -243,6 +248,21 @@ _wmi_exec_query_impl(PyObject *module, PyObject *query) } } + // gh-112278: If current user doesn't have permission to query the WMI, the + // function IWbemLocator::ConnectServer will hang for 5 seconds, and there + // is no way to specify the timeout. So we use an Event object to simulate + // a timeout. + switch (WaitForSingleObject(data.connectEvent, 100)) { + case WAIT_OBJECT_0: + break; + case WAIT_TIMEOUT: + err = WAIT_TIMEOUT; + break; + default: + err = GetLastError(); + break; + } + while (!err) { if (ReadFile( data.readPipe, @@ -265,7 +285,7 @@ _wmi_exec_query_impl(PyObject *module, PyObject *query) } // Allow the thread some time to clean up - switch (WaitForSingleObject(hThread, 1000)) { + switch (WaitForSingleObject(hThread, 100)) { case WAIT_OBJECT_0: // Thread ended cleanly if (!GetExitCodeThread(hThread, (LPDWORD)&err)) { @@ -286,6 +306,7 @@ _wmi_exec_query_impl(PyObject *module, PyObject *query) } CloseHandle(hThread); + CloseHandle(data.connectEvent); hThread = NULL; Py_END_ALLOW_THREADS From bf0beae6a05f3266606a21e22a4d803abbb8d731 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 7 Dec 2023 19:41:27 +0100 Subject: [PATCH 217/228] gh-110017: Disable test_signal.test_stress_modifying_handlers on macOS (#112834) Test test_stress_modifying_handlers in test_signal can crash the interpreter due to a bug in macOS. Filed as FB13453490 with Apple. --- Lib/test/test_signal.py | 1 + .../next/macOS/2023-12-07-15-53-16.gh-issue-110017.UMYzMR.rst | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/macOS/2023-12-07-15-53-16.gh-issue-110017.UMYzMR.rst diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index f2ae28c38dd72d..acb7e9d4c6074d 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -1318,6 +1318,7 @@ def handler(signum, frame): # Python handler self.assertEqual(len(sigs), N, "Some signals were lost") + @unittest.skipIf(sys.platform == "darwin", "crashes due to system bug (FB13453490)") @unittest.skipUnless(hasattr(signal, "SIGUSR1"), "test needs SIGUSR1") @threading_helper.requires_working_threading() diff --git a/Misc/NEWS.d/next/macOS/2023-12-07-15-53-16.gh-issue-110017.UMYzMR.rst b/Misc/NEWS.d/next/macOS/2023-12-07-15-53-16.gh-issue-110017.UMYzMR.rst new file mode 100644 index 00000000000000..eab1746f1ae3f7 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-12-07-15-53-16.gh-issue-110017.UMYzMR.rst @@ -0,0 +1,2 @@ +Disable a signal handling stress test on macOS due to a bug in macOS +(FB13453490). From db460735af7503984d1b7d878069722db44b11e8 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Thu, 7 Dec 2023 14:11:45 -0500 Subject: [PATCH 218/228] gh-112538: Add internal-only _PyThreadStateImpl "wrapper" for PyThreadState (gh-112560) Every PyThreadState instance is now actually a _PyThreadStateImpl. It is safe to cast from `PyThreadState*` to `_PyThreadStateImpl*` and back. The _PyThreadStateImpl will contain fields that we do not want to expose in the public C API. --- Include/internal/pycore_interp.h | 5 +++-- Include/internal/pycore_runtime_init.h | 7 ++++++- Include/internal/pycore_tstate.h | 26 ++++++++++++++++++++++++ Makefile.pre.in | 1 + PCbuild/pythoncore.vcxproj | 1 + PCbuild/pythoncore.vcxproj.filters | 3 +++ Python/pystate.c | 28 +++++++++++++------------- 7 files changed, 54 insertions(+), 17 deletions(-) create mode 100644 Include/internal/pycore_tstate.h diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 498db8becf114c..2a683196eeced3 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -29,6 +29,7 @@ extern "C" { #include "pycore_list.h" // struct _Py_list_state #include "pycore_object_state.h" // struct _py_object_state #include "pycore_obmalloc.h" // struct _obmalloc_state +#include "pycore_tstate.h" // _PyThreadStateImpl #include "pycore_tuple.h" // struct _Py_tuple_state #include "pycore_typeobject.h" // struct types_state #include "pycore_unicodeobject.h" // struct _Py_unicode_state @@ -210,8 +211,8 @@ struct _is { struct _Py_interp_cached_objects cached_objects; struct _Py_interp_static_objects static_objects; - /* the initial PyInterpreterState.threads.head */ - PyThreadState _initial_thread; + /* the initial PyInterpreterState.threads.head */ + _PyThreadStateImpl _initial_thread; Py_ssize_t _interactive_src_count; }; diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index fa5d8114abf0d7..d324a94278839c 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -186,7 +186,12 @@ extern PyTypeObject _PyExc_MemoryError; }, \ }, \ }, \ - ._initial_thread = _PyThreadState_INIT, \ + ._initial_thread = _PyThreadStateImpl_INIT, \ + } + +#define _PyThreadStateImpl_INIT \ + { \ + .base = _PyThreadState_INIT, \ } #define _PyThreadState_INIT \ diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h new file mode 100644 index 00000000000000..17f3e865641773 --- /dev/null +++ b/Include/internal/pycore_tstate.h @@ -0,0 +1,26 @@ +#ifndef Py_INTERNAL_TSTATE_H +#define Py_INTERNAL_TSTATE_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + + +// Every PyThreadState is actually allocated as a _PyThreadStateImpl. The +// PyThreadState fields are exposed as part of the C API, although most fields +// are intended to be private. The _PyThreadStateImpl fields not exposed. +typedef struct _PyThreadStateImpl { + // semi-public fields are in PyThreadState. + PyThreadState base; + + // TODO: add private fields here +} _PyThreadStateImpl; + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_TSTATE_H */ diff --git a/Makefile.pre.in b/Makefile.pre.in index db85b11ef01b2c..f57894a2118e74 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1874,6 +1874,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_token.h \ $(srcdir)/Include/internal/pycore_traceback.h \ $(srcdir)/Include/internal/pycore_tracemalloc.h \ + $(srcdir)/Include/internal/pycore_tstate.h \ $(srcdir)/Include/internal/pycore_tuple.h \ $(srcdir)/Include/internal/pycore_typeobject.h \ $(srcdir)/Include/internal/pycore_typevarobject.h \ diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index be1b98dba02fc5..278f1f5622543c 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -285,6 +285,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 4f0da8f35998b7..c9b34c64fbf75f 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -780,6 +780,9 @@ Include\internal + + Include\internal + Include\internal diff --git a/Python/pystate.c b/Python/pystate.c index 6196b15da0117a..c75991667869cf 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1353,20 +1353,19 @@ allocate_chunk(int size_in_bytes, _PyStackChunk* previous) return res; } -static PyThreadState * +static _PyThreadStateImpl * alloc_threadstate(void) { - return PyMem_RawCalloc(1, sizeof(PyThreadState)); + return PyMem_RawCalloc(1, sizeof(_PyThreadStateImpl)); } static void -free_threadstate(PyThreadState *tstate) +free_threadstate(_PyThreadStateImpl *tstate) { // The initial thread state of the interpreter is allocated // as part of the interpreter state so should not be freed. - if (tstate == &tstate->interp->_initial_thread) { + if (tstate == &tstate->base.interp->_initial_thread) { // Restore to _PyThreadState_INIT. - tstate = &tstate->interp->_initial_thread; memcpy(tstate, &initial._main_interpreter._initial_thread, sizeof(*tstate)); @@ -1385,9 +1384,10 @@ free_threadstate(PyThreadState *tstate) */ static void -init_threadstate(PyThreadState *tstate, +init_threadstate(_PyThreadStateImpl *_tstate, PyInterpreterState *interp, uint64_t id, int whence) { + PyThreadState *tstate = (PyThreadState *)_tstate; if (tstate->_status.initialized) { Py_FatalError("thread state already initialized"); } @@ -1444,13 +1444,13 @@ add_threadstate(PyInterpreterState *interp, PyThreadState *tstate, static PyThreadState * new_threadstate(PyInterpreterState *interp, int whence) { - PyThreadState *tstate; + _PyThreadStateImpl *tstate; _PyRuntimeState *runtime = interp->runtime; // We don't need to allocate a thread state for the main interpreter // (the common case), but doing it later for the other case revealed a // reentrancy problem (deadlock). So for now we always allocate before // taking the interpreters lock. See GH-96071. - PyThreadState *new_tstate = alloc_threadstate(); + _PyThreadStateImpl *new_tstate = alloc_threadstate(); int used_newtstate; if (new_tstate == NULL) { return NULL; @@ -1482,14 +1482,14 @@ new_threadstate(PyInterpreterState *interp, int whence) } init_threadstate(tstate, interp, id, whence); - add_threadstate(interp, tstate, old_head); + add_threadstate(interp, (PyThreadState *)tstate, old_head); HEAD_UNLOCK(runtime); if (!used_newtstate) { // Must be called with lock unlocked to avoid re-entrancy deadlock. PyMem_RawFree(new_tstate); } - return tstate; + return (PyThreadState *)tstate; } PyThreadState * @@ -1678,7 +1678,7 @@ zapthreads(PyInterpreterState *interp) while ((tstate = interp->threads.head) != NULL) { tstate_verify_not_active(tstate); tstate_delete_common(tstate); - free_threadstate(tstate); + free_threadstate((_PyThreadStateImpl *)tstate); } } @@ -1689,7 +1689,7 @@ PyThreadState_Delete(PyThreadState *tstate) _Py_EnsureTstateNotNULL(tstate); tstate_verify_not_active(tstate); tstate_delete_common(tstate); - free_threadstate(tstate); + free_threadstate((_PyThreadStateImpl *)tstate); } @@ -1701,7 +1701,7 @@ _PyThreadState_DeleteCurrent(PyThreadState *tstate) tstate_delete_common(tstate); current_fast_clear(tstate->interp->runtime); _PyEval_ReleaseLock(tstate->interp, NULL); - free_threadstate(tstate); + free_threadstate((_PyThreadStateImpl *)tstate); } void @@ -1751,7 +1751,7 @@ _PyThreadState_DeleteExcept(PyThreadState *tstate) for (p = list; p; p = next) { next = p->next; PyThreadState_Clear(p); - free_threadstate(p); + free_threadstate((_PyThreadStateImpl *)p); } } From cf6110ba1337cb67e5867d86e7c0e8d923a5bc8d Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Thu, 7 Dec 2023 14:33:40 -0500 Subject: [PATCH 219/228] gh-111924: Use PyMutex for Runtime-global Locks. (gh-112207) This replaces some usages of PyThread_type_lock with PyMutex, which does not require memory allocation to initialize. This simplifies some of the runtime initialization and is also one step towards avoiding changing the default raw memory allocator during initialize/finalization, which can be non-thread-safe in some circumstances. --- Include/internal/pycore_atexit.h | 5 +- Include/internal/pycore_ceval.h | 3 +- Include/internal/pycore_ceval_state.h | 3 +- Include/internal/pycore_crossinterp.h | 3 +- Include/internal/pycore_import.h | 3 +- Include/internal/pycore_lock.h | 17 ++++ Include/internal/pycore_pymem.h | 5 +- Include/internal/pycore_pystate.h | 4 +- Include/internal/pycore_runtime.h | 4 +- Include/internal/pycore_unicodeobject.h | 3 +- Objects/obmalloc.c | 61 ++++-------- Objects/unicodeobject.c | 4 +- Python/ceval_gil.c | 37 ++------ Python/crossinterp.c | 29 +----- Python/import.c | 10 +- Python/pylifecycle.c | 14 +-- Python/pystate.c | 121 +++--------------------- Python/sysmodule.c | 12 +-- 18 files changed, 97 insertions(+), 241 deletions(-) diff --git a/Include/internal/pycore_atexit.h b/Include/internal/pycore_atexit.h index 3966df70e2616f..4dcda8f517c787 100644 --- a/Include/internal/pycore_atexit.h +++ b/Include/internal/pycore_atexit.h @@ -1,5 +1,8 @@ #ifndef Py_INTERNAL_ATEXIT_H #define Py_INTERNAL_ATEXIT_H + +#include "pycore_lock.h" // PyMutex + #ifdef __cplusplus extern "C" { #endif @@ -15,7 +18,7 @@ extern "C" { typedef void (*atexit_callbackfunc)(void); struct _atexit_runtime_state { - PyThread_type_lock mutex; + PyMutex mutex; #define NEXITFUNCS 32 atexit_callbackfunc callbacks[NEXITFUNCS]; int ncallbacks; diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index 3f7ac922bdf451..64fb4034669e19 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -41,8 +41,7 @@ PyAPI_FUNC(int) _PyEval_MakePendingCalls(PyThreadState *); #endif extern void _Py_FinishPendingCalls(PyThreadState *tstate); -extern void _PyEval_InitState(PyInterpreterState *, PyThread_type_lock); -extern void _PyEval_FiniState(struct _ceval_state *ceval); +extern void _PyEval_InitState(PyInterpreterState *); extern void _PyEval_SignalReceived(PyInterpreterState *interp); // bitwise flags: diff --git a/Include/internal/pycore_ceval_state.h b/Include/internal/pycore_ceval_state.h index 072bbcda0c3c82..28738980eb49be 100644 --- a/Include/internal/pycore_ceval_state.h +++ b/Include/internal/pycore_ceval_state.h @@ -8,6 +8,7 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif +#include "pycore_lock.h" // PyMutex #include "pycore_gil.h" // struct _gil_runtime_state @@ -15,7 +16,7 @@ typedef int (*_Py_pending_call_func)(void *); struct _pending_calls { int busy; - PyThread_type_lock lock; + PyMutex mutex; /* Request for running pending calls. */ int32_t calls_to_do; #define NPENDINGCALLS 32 diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h index ec9dac96292f35..2e6d09a49f95d3 100644 --- a/Include/internal/pycore_crossinterp.h +++ b/Include/internal/pycore_crossinterp.h @@ -8,6 +8,7 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif +#include "pycore_lock.h" // PyMutex #include "pycore_pyerrors.h" @@ -128,7 +129,7 @@ struct _xidregitem { struct _xidregistry { int global; /* builtin types or heap types */ int initialized; - PyThread_type_lock mutex; + PyMutex mutex; struct _xidregitem *head; }; diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index 117e46bb86285d..c84f87a831bf38 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -9,6 +9,7 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif +#include "pycore_lock.h" // PyMutex #include "pycore_hashtable.h" // _Py_hashtable_t #include "pycore_time.h" // _PyTime_t @@ -47,7 +48,7 @@ struct _import_runtime_state { Py_ssize_t last_module_index; struct { /* A lock to guard the cache. */ - PyThread_type_lock mutex; + PyMutex mutex; /* The actual cache of (filename, name, PyModuleDef) for modules. Only legacy (single-phase init) extension modules are added and only if they support multiple initialization (m_size >- 0) diff --git a/Include/internal/pycore_lock.h b/Include/internal/pycore_lock.h index f135cbbc3754fb..03ad1c9fd584b5 100644 --- a/Include/internal/pycore_lock.h +++ b/Include/internal/pycore_lock.h @@ -92,6 +92,13 @@ PyMutex_IsLocked(PyMutex *m) return (_Py_atomic_load_uint8(&m->v) & _Py_LOCKED) != 0; } +// Re-initializes the mutex after a fork to the unlocked state. +static inline void +_PyMutex_at_fork_reinit(PyMutex *m) +{ + memset(m, 0, sizeof(*m)); +} + typedef enum _PyLockFlags { // Do not detach/release the GIL when waiting on the lock. _Py_LOCK_DONT_DETACH = 0, @@ -108,6 +115,16 @@ typedef enum _PyLockFlags { extern PyLockStatus _PyMutex_LockTimed(PyMutex *m, _PyTime_t timeout_ns, _PyLockFlags flags); +// Lock a mutex with aditional options. See _PyLockFlags for details. +static inline void +PyMutex_LockFlags(PyMutex *m, _PyLockFlags flags) +{ + uint8_t expected = _Py_UNLOCKED; + if (!_Py_atomic_compare_exchange_uint8(&m->v, &expected, _Py_LOCKED)) { + _PyMutex_LockTimed(m, -1, flags); + } +} + // Unlock a mutex, returns 0 if the mutex is not locked (used for improved // error messages). extern int _PyMutex_TryUnlock(PyMutex *m); diff --git a/Include/internal/pycore_pymem.h b/Include/internal/pycore_pymem.h index 6b5113714dbeb2..8631ca34a5e616 100644 --- a/Include/internal/pycore_pymem.h +++ b/Include/internal/pycore_pymem.h @@ -1,5 +1,8 @@ #ifndef Py_INTERNAL_PYMEM_H #define Py_INTERNAL_PYMEM_H + +#include "pycore_lock.h" // PyMutex + #ifdef __cplusplus extern "C" { #endif @@ -30,7 +33,7 @@ typedef struct { } debug_alloc_api_t; struct _pymem_allocators { - PyThread_type_lock mutex; + PyMutex mutex; struct { PyMemAllocatorEx raw; PyMemAllocatorEx mem; diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 7fa952e371d7b4..c031a38cd6bfa3 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -220,9 +220,9 @@ PyAPI_FUNC(int) _PyState_AddModule( extern int _PyOS_InterruptOccurred(PyThreadState *tstate); #define HEAD_LOCK(runtime) \ - PyThread_acquire_lock((runtime)->interpreters.mutex, WAIT_LOCK) + PyMutex_LockFlags(&(runtime)->interpreters.mutex, _Py_LOCK_DONT_DETACH) #define HEAD_UNLOCK(runtime) \ - PyThread_release_lock((runtime)->interpreters.mutex) + PyMutex_Unlock(&(runtime)->interpreters.mutex) // Get the configuration of the current interpreter. // The caller must hold the GIL. diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 36743723f8afd8..e3348296ea61b7 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -173,7 +173,7 @@ typedef struct pyruntimestate { unsigned long _finalizing_id; struct pyinterpreters { - PyThread_type_lock mutex; + PyMutex mutex; /* The linked list of interpreters, newest first. */ PyInterpreterState *head; /* The runtime's initial interpreter, which has a special role @@ -234,7 +234,7 @@ typedef struct pyruntimestate { Py_OpenCodeHookFunction open_code_hook; void *open_code_userdata; struct { - PyThread_type_lock mutex; + PyMutex mutex; _Py_AuditHookEntry *head; } audit_hooks; diff --git a/Include/internal/pycore_unicodeobject.h b/Include/internal/pycore_unicodeobject.h index a0d00af92e0f5d..7ee540154b23d8 100644 --- a/Include/internal/pycore_unicodeobject.h +++ b/Include/internal/pycore_unicodeobject.h @@ -8,6 +8,7 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif +#include "pycore_lock.h" // PyMutex #include "pycore_fileutils.h" // _Py_error_handler #include "pycore_identifier.h" // _Py_Identifier #include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI @@ -277,7 +278,7 @@ extern PyTypeObject _PyUnicodeASCIIIter_Type; /* --- Other API ---------------------------------------------------------- */ struct _Py_unicode_runtime_ids { - PyThread_type_lock lock; + PyMutex mutex; // next_index value must be preserved when Py_Initialize()/Py_Finalize() // is called multiple times: see _PyUnicode_FromId() implementation. Py_ssize_t next_index; diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index 2761c774209786..b737c030957564 100644 --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -329,13 +329,9 @@ int _PyMem_SetDefaultAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *old_alloc) { - if (ALLOCATORS_MUTEX == NULL) { - /* The runtime must be initializing. */ - return set_default_allocator_unlocked(domain, pydebug, old_alloc); - } - PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK); + PyMutex_Lock(&ALLOCATORS_MUTEX); int res = set_default_allocator_unlocked(domain, pydebug, old_alloc); - PyThread_release_lock(ALLOCATORS_MUTEX); + PyMutex_Unlock(&ALLOCATORS_MUTEX); return res; } @@ -467,9 +463,9 @@ set_up_allocators_unlocked(PyMemAllocatorName allocator) int _PyMem_SetupAllocators(PyMemAllocatorName allocator) { - PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK); + PyMutex_Lock(&ALLOCATORS_MUTEX); int res = set_up_allocators_unlocked(allocator); - PyThread_release_lock(ALLOCATORS_MUTEX); + PyMutex_Unlock(&ALLOCATORS_MUTEX); return res; } @@ -554,9 +550,9 @@ get_current_allocator_name_unlocked(void) const char* _PyMem_GetCurrentAllocatorName(void) { - PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK); + PyMutex_Lock(&ALLOCATORS_MUTEX); const char *name = get_current_allocator_name_unlocked(); - PyThread_release_lock(ALLOCATORS_MUTEX); + PyMutex_Unlock(&ALLOCATORS_MUTEX); return name; } @@ -653,14 +649,9 @@ set_up_debug_hooks_unlocked(void) void PyMem_SetupDebugHooks(void) { - if (ALLOCATORS_MUTEX == NULL) { - /* The runtime must not be completely initialized yet. */ - set_up_debug_hooks_unlocked(); - return; - } - PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK); + PyMutex_Lock(&ALLOCATORS_MUTEX); set_up_debug_hooks_unlocked(); - PyThread_release_lock(ALLOCATORS_MUTEX); + PyMutex_Unlock(&ALLOCATORS_MUTEX); } static void @@ -696,53 +687,33 @@ set_allocator_unlocked(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator) void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator) { - if (ALLOCATORS_MUTEX == NULL) { - /* The runtime must not be completely initialized yet. */ - get_allocator_unlocked(domain, allocator); - return; - } - PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK); + PyMutex_Lock(&ALLOCATORS_MUTEX); get_allocator_unlocked(domain, allocator); - PyThread_release_lock(ALLOCATORS_MUTEX); + PyMutex_Unlock(&ALLOCATORS_MUTEX); } void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator) { - if (ALLOCATORS_MUTEX == NULL) { - /* The runtime must not be completely initialized yet. */ - set_allocator_unlocked(domain, allocator); - return; - } - PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK); + PyMutex_Lock(&ALLOCATORS_MUTEX); set_allocator_unlocked(domain, allocator); - PyThread_release_lock(ALLOCATORS_MUTEX); + PyMutex_Unlock(&ALLOCATORS_MUTEX); } void PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator) { - if (ALLOCATORS_MUTEX == NULL) { - /* The runtime must not be completely initialized yet. */ - *allocator = _PyObject_Arena; - return; - } - PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK); + PyMutex_Lock(&ALLOCATORS_MUTEX); *allocator = _PyObject_Arena; - PyThread_release_lock(ALLOCATORS_MUTEX); + PyMutex_Unlock(&ALLOCATORS_MUTEX); } void PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator) { - if (ALLOCATORS_MUTEX == NULL) { - /* The runtime must not be completely initialized yet. */ - _PyObject_Arena = *allocator; - return; - } - PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK); + PyMutex_Lock(&ALLOCATORS_MUTEX); _PyObject_Arena = *allocator; - PyThread_release_lock(ALLOCATORS_MUTEX); + PyMutex_Unlock(&ALLOCATORS_MUTEX); } diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 10022e23c04abf..836e14fd5d5dea 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -1904,7 +1904,7 @@ _PyUnicode_FromId(_Py_Identifier *id) if (index < 0) { struct _Py_unicode_runtime_ids *rt_ids = &interp->runtime->unicode_state.ids; - PyThread_acquire_lock(rt_ids->lock, WAIT_LOCK); + PyMutex_Lock(&rt_ids->mutex); // Check again to detect concurrent access. Another thread can have // initialized the index while this thread waited for the lock. index = _Py_atomic_load_ssize(&id->index); @@ -1914,7 +1914,7 @@ _PyUnicode_FromId(_Py_Identifier *id) rt_ids->next_index++; _Py_atomic_store_ssize(&id->index, index); } - PyThread_release_lock(rt_ids->lock); + PyMutex_Unlock(&rt_ids->mutex); } assert(index >= 0); diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c index 92c4b2fee9f863..636e4db898f2d9 100644 --- a/Python/ceval_gil.c +++ b/Python/ceval_gil.c @@ -589,9 +589,7 @@ _PyEval_ReInitThreads(PyThreadState *tstate) take_gil(tstate); struct _pending_calls *pending = &tstate->interp->ceval.pending; - if (_PyThread_at_fork_reinit(&pending->lock) < 0) { - return _PyStatus_ERR("Can't reinitialize pending calls lock"); - } + _PyMutex_at_fork_reinit(&pending->mutex); /* Destroy all threads except the current one */ _PyThreadState_DeleteExcept(tstate); @@ -720,13 +718,10 @@ _PyEval_AddPendingCall(PyInterpreterState *interp, assert(_Py_IsMainInterpreter(interp)); pending = &_PyRuntime.ceval.pending_mainthread; } - /* Ensure that _PyEval_InitState() was called - and that _PyEval_FiniState() is not called yet. */ - assert(pending->lock != NULL); - PyThread_acquire_lock(pending->lock, WAIT_LOCK); + PyMutex_Lock(&pending->mutex); int result = _push_pending_call(pending, func, arg, flags); - PyThread_release_lock(pending->lock); + PyMutex_Unlock(&pending->mutex); /* signal main loop */ SIGNAL_PENDING_CALLS(interp); @@ -768,9 +763,9 @@ _make_pending_calls(struct _pending_calls *pending) int flags = 0; /* pop one item off the queue while holding the lock */ - PyThread_acquire_lock(pending->lock, WAIT_LOCK); + PyMutex_Lock(&pending->mutex); _pop_pending_call(pending, &func, &arg, &flags); - PyThread_release_lock(pending->lock); + PyMutex_Unlock(&pending->mutex); /* having released the lock, perform the callback */ if (func == NULL) { @@ -795,7 +790,7 @@ make_pending_calls(PyInterpreterState *interp) /* Only one thread (per interpreter) may run the pending calls at once. In the same way, we don't do recursive pending calls. */ - PyThread_acquire_lock(pending->lock, WAIT_LOCK); + PyMutex_Lock(&pending->mutex); if (pending->busy) { /* A pending call was added after another thread was already handling the pending calls (and had already "unsignaled"). @@ -807,11 +802,11 @@ make_pending_calls(PyInterpreterState *interp) care of any remaining pending calls. Until then, though, all the interpreter's threads will be tripping the eval breaker every time it's checked. */ - PyThread_release_lock(pending->lock); + PyMutex_Unlock(&pending->mutex); return 0; } pending->busy = 1; - PyThread_release_lock(pending->lock); + PyMutex_Unlock(&pending->mutex); /* unsignal before starting to call callbacks, so that any callback added in-between re-signals */ @@ -892,23 +887,9 @@ Py_MakePendingCalls(void) } void -_PyEval_InitState(PyInterpreterState *interp, PyThread_type_lock pending_lock) +_PyEval_InitState(PyInterpreterState *interp) { _gil_initialize(&interp->_gil); - - struct _pending_calls *pending = &interp->ceval.pending; - assert(pending->lock == NULL); - pending->lock = pending_lock; -} - -void -_PyEval_FiniState(struct _ceval_state *ceval) -{ - struct _pending_calls *pending = &ceval->pending; - if (pending->lock != NULL) { - PyThread_free_lock(pending->lock); - pending->lock = NULL; - } } diff --git a/Python/crossinterp.c b/Python/crossinterp.c index 21b96ef05ed799..f74fee38648266 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -456,16 +456,17 @@ _xidregistry_clear(struct _xidregistry *xidregistry) static void _xidregistry_lock(struct _xidregistry *registry) { - if (registry->mutex != NULL) { - PyThread_acquire_lock(registry->mutex, WAIT_LOCK); + if (registry->global) { + PyMutex_Lock(®istry->mutex); } + // else: Within an interpreter we rely on the GIL instead of a separate lock. } static void _xidregistry_unlock(struct _xidregistry *registry) { - if (registry->mutex != NULL) { - PyThread_release_lock(registry->mutex); + if (registry->global) { + PyMutex_Unlock(®istry->mutex); } } @@ -874,19 +875,10 @@ _xidregistry_init(struct _xidregistry *registry) registry->initialized = 1; if (registry->global) { - // We manage the mutex lifecycle in pystate.c. - assert(registry->mutex != NULL); - // Registering the builtins is cheap so we don't bother doing it lazily. assert(registry->head == NULL); _register_builtins_for_crossinterpreter_data(registry); } - else { - // Within an interpreter we rely on the GIL instead of a separate lock. - assert(registry->mutex == NULL); - - // There's nothing else to initialize. - } } static void @@ -898,17 +890,6 @@ _xidregistry_fini(struct _xidregistry *registry) registry->initialized = 0; _xidregistry_clear(registry); - - if (registry->global) { - // We manage the mutex lifecycle in pystate.c. - assert(registry->mutex != NULL); - } - else { - // There's nothing else to finalize. - - // Within an interpreter we rely on the GIL instead of a separate lock. - assert(registry->mutex == NULL); - } } diff --git a/Python/import.c b/Python/import.c index ef81f46a4d65c1..2dd95d8364a0be 100644 --- a/Python/import.c +++ b/Python/import.c @@ -418,11 +418,7 @@ remove_module(PyThreadState *tstate, PyObject *name) Py_ssize_t _PyImport_GetNextModuleIndex(void) { - PyThread_acquire_lock(EXTENSIONS.mutex, WAIT_LOCK); - LAST_MODULE_INDEX++; - Py_ssize_t index = LAST_MODULE_INDEX; - PyThread_release_lock(EXTENSIONS.mutex); - return index; + return _Py_atomic_add_ssize(&LAST_MODULE_INDEX, 1) + 1; } static const char * @@ -882,13 +878,13 @@ gets even messier. static inline void extensions_lock_acquire(void) { - PyThread_acquire_lock(_PyRuntime.imports.extensions.mutex, WAIT_LOCK); + PyMutex_Lock(&_PyRuntime.imports.extensions.mutex); } static inline void extensions_lock_release(void) { - PyThread_release_lock(_PyRuntime.imports.extensions.mutex); + PyMutex_Unlock(&_PyRuntime.imports.extensions.mutex); } /* Magic for extension modules (built-in as well as dynamically diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 20bfe1a0b75b29..45a119fcca7f2c 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -3056,13 +3056,13 @@ wait_for_thread_shutdown(PyThreadState *tstate) int Py_AtExit(void (*func)(void)) { struct _atexit_runtime_state *state = &_PyRuntime.atexit; - PyThread_acquire_lock(state->mutex, WAIT_LOCK); + PyMutex_Lock(&state->mutex); if (state->ncallbacks >= NEXITFUNCS) { - PyThread_release_lock(state->mutex); + PyMutex_Unlock(&state->mutex); return -1; } state->callbacks[state->ncallbacks++] = func; - PyThread_release_lock(state->mutex); + PyMutex_Unlock(&state->mutex); return 0; } @@ -3072,18 +3072,18 @@ call_ll_exitfuncs(_PyRuntimeState *runtime) atexit_callbackfunc exitfunc; struct _atexit_runtime_state *state = &runtime->atexit; - PyThread_acquire_lock(state->mutex, WAIT_LOCK); + PyMutex_Lock(&state->mutex); while (state->ncallbacks > 0) { /* pop last function from the list */ state->ncallbacks--; exitfunc = state->callbacks[state->ncallbacks]; state->callbacks[state->ncallbacks] = NULL; - PyThread_release_lock(state->mutex); + PyMutex_Unlock(&state->mutex); exitfunc(); - PyThread_acquire_lock(state->mutex, WAIT_LOCK); + PyMutex_Lock(&state->mutex); } - PyThread_release_lock(state->mutex); + PyMutex_Unlock(&state->mutex); fflush(stdout); fflush(stderr); diff --git a/Python/pystate.c b/Python/pystate.c index c75991667869cf..1a7c0c968504d1 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -379,49 +379,23 @@ _Py_COMP_DIAG_IGNORE_DEPR_DECLS static const _PyRuntimeState initial = _PyRuntimeState_INIT(_PyRuntime); _Py_COMP_DIAG_POP -#define NUMLOCKS 8 #define LOCKS_INIT(runtime) \ { \ &(runtime)->interpreters.mutex, \ &(runtime)->xi.registry.mutex, \ - &(runtime)->unicode_state.ids.lock, \ + &(runtime)->unicode_state.ids.mutex, \ &(runtime)->imports.extensions.mutex, \ - &(runtime)->ceval.pending_mainthread.lock, \ + &(runtime)->ceval.pending_mainthread.mutex, \ &(runtime)->atexit.mutex, \ &(runtime)->audit_hooks.mutex, \ &(runtime)->allocators.mutex, \ } -static int -alloc_for_runtime(PyThread_type_lock locks[NUMLOCKS]) -{ - /* Force default allocator, since _PyRuntimeState_Fini() must - use the same allocator than this function. */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - for (int i = 0; i < NUMLOCKS; i++) { - PyThread_type_lock lock = PyThread_allocate_lock(); - if (lock == NULL) { - for (int j = 0; j < i; j++) { - PyThread_free_lock(locks[j]); - locks[j] = NULL; - } - break; - } - locks[i] = lock; - } - - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - return 0; -} - static void init_runtime(_PyRuntimeState *runtime, void *open_code_hook, void *open_code_userdata, _Py_AuditHookEntry *audit_hook_head, - Py_ssize_t unicode_next_index, - PyThread_type_lock locks[NUMLOCKS]) + Py_ssize_t unicode_next_index) { assert(!runtime->preinitializing); assert(!runtime->preinitialized); @@ -435,12 +409,6 @@ init_runtime(_PyRuntimeState *runtime, PyPreConfig_InitPythonConfig(&runtime->preconfig); - PyThread_type_lock *lockptrs[NUMLOCKS] = LOCKS_INIT(runtime); - for (int i = 0; i < NUMLOCKS; i++) { - assert(locks[i] != NULL); - *lockptrs[i] = locks[i]; - } - // Set it to the ID of the main thread of the main interpreter. runtime->main_thread = PyThread_get_thread_ident(); @@ -466,11 +434,6 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime) // is called multiple times. Py_ssize_t unicode_next_index = runtime->unicode_state.ids.next_index; - PyThread_type_lock locks[NUMLOCKS]; - if (alloc_for_runtime(locks) != 0) { - return _PyStatus_NO_MEMORY(); - } - if (runtime->_initialized) { // Py_Initialize() must be running again. // Reset to _PyRuntimeState_INIT. @@ -489,7 +452,7 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime) } init_runtime(runtime, open_code_hook, open_code_userdata, audit_hook_head, - unicode_next_index, locks); + unicode_next_index); return _PyStatus_OK(); } @@ -509,23 +472,6 @@ _PyRuntimeState_Fini(_PyRuntimeState *runtime) if (PyThread_tss_is_created(&runtime->trashTSSkey)) { PyThread_tss_delete(&runtime->trashTSSkey); } - - /* Force the allocator used by _PyRuntimeState_Init(). */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); -#define FREE_LOCK(LOCK) \ - if (LOCK != NULL) { \ - PyThread_free_lock(LOCK); \ - LOCK = NULL; \ - } - - PyThread_type_lock *lockptrs[NUMLOCKS] = LOCKS_INIT(runtime); - for (int i = 0; i < NUMLOCKS; i++) { - FREE_LOCK(*lockptrs[i]); - } - -#undef FREE_LOCK - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); } #ifdef HAVE_FORK @@ -537,28 +483,19 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime) // This was initially set in _PyRuntimeState_Init(). runtime->main_thread = PyThread_get_thread_ident(); - /* Force default allocator, since _PyRuntimeState_Fini() must - use the same allocator than this function. */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - PyThread_type_lock *lockptrs[NUMLOCKS] = LOCKS_INIT(runtime); - int reinit_err = 0; - for (int i = 0; i < NUMLOCKS; i++) { - reinit_err += _PyThread_at_fork_reinit(lockptrs[i]); - } - - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - // Clears the parking lot. Any waiting threads are dead. This must be // called before releasing any locks that use the parking lot. _PyParkingLot_AfterFork(); + // Re-initialize global locks + PyMutex *locks[] = LOCKS_INIT(runtime); + for (size_t i = 0; i < Py_ARRAY_LENGTH(locks); i++) { + _PyMutex_at_fork_reinit(locks[i]); + } + /* bpo-42540: id_mutex is freed by _PyInterpreterState_Delete, which does * not force the default allocator. */ - reinit_err += _PyThread_at_fork_reinit(&runtime->interpreters.main->id_mutex); - - if (reinit_err < 0) { + if (_PyThread_at_fork_reinit(&runtime->interpreters.main->id_mutex) < 0) { return _PyStatus_ERR("Failed to reinitialize runtime locks"); } @@ -594,24 +531,6 @@ _PyInterpreterState_Enable(_PyRuntimeState *runtime) { struct pyinterpreters *interpreters = &runtime->interpreters; interpreters->next_id = 0; - - /* Py_Finalize() calls _PyRuntimeState_Fini() which clears the mutex. - Create a new mutex if needed. */ - if (interpreters->mutex == NULL) { - /* Force default allocator, since _PyRuntimeState_Fini() must - use the same allocator than this function. */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - interpreters->mutex = PyThread_allocate_lock(); - - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - if (interpreters->mutex == NULL) { - return _PyStatus_ERR("Can't initialize threads for interpreter"); - } - } - return _PyStatus_OK(); } @@ -654,8 +573,7 @@ free_interpreter(PyInterpreterState *interp) static PyStatus init_interpreter(PyInterpreterState *interp, _PyRuntimeState *runtime, int64_t id, - PyInterpreterState *next, - PyThread_type_lock pending_lock) + PyInterpreterState *next) { if (interp->_initialized) { return _PyStatus_ERR("interpreter already initialized"); @@ -684,7 +602,7 @@ init_interpreter(PyInterpreterState *interp, return status; } - _PyEval_InitState(interp, pending_lock); + _PyEval_InitState(interp); _PyGC_InitState(&interp->gc); PyConfig_InitPythonConfig(&interp->config); _PyType_InitCache(interp); @@ -730,11 +648,6 @@ _PyInterpreterState_New(PyThreadState *tstate, PyInterpreterState **pinterp) } } - PyThread_type_lock pending_lock = PyThread_allocate_lock(); - if (pending_lock == NULL) { - return _PyStatus_NO_MEMORY(); - } - /* We completely serialize creation of multiple interpreters, since it simplifies things here and blocking concurrent calls isn't a problem. Regardless, we must fully block subinterpreter creation until @@ -781,11 +694,10 @@ _PyInterpreterState_New(PyThreadState *tstate, PyInterpreterState **pinterp) interpreters->head = interp; status = init_interpreter(interp, runtime, - id, old_head, pending_lock); + id, old_head); if (_PyStatus_EXCEPTION(status)) { goto error; } - pending_lock = NULL; HEAD_UNLOCK(runtime); @@ -796,9 +708,6 @@ _PyInterpreterState_New(PyThreadState *tstate, PyInterpreterState **pinterp) error: HEAD_UNLOCK(runtime); - if (pending_lock != NULL) { - PyThread_free_lock(pending_lock); - } if (interp != NULL) { free_interpreter(interp); } @@ -1003,8 +912,6 @@ PyInterpreterState_Delete(PyInterpreterState *interp) zapthreads(interp); - _PyEval_FiniState(&interp->ceval); - // XXX These two calls should be done at the end of clear_interpreter(), // but currently some objects get decref'ed after that. #ifdef Py_REF_DEBUG diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 46878c7c9687f5..57dc4a1226ce75 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -451,15 +451,9 @@ PySys_AddAuditHook(Py_AuditHookFunction hook, void *userData) e->hookCFunction = (Py_AuditHookFunction)hook; e->userData = userData; - if (runtime->audit_hooks.mutex == NULL) { - /* The runtime must not be initialized yet. */ - add_audit_hook_entry_unlocked(runtime, e); - } - else { - PyThread_acquire_lock(runtime->audit_hooks.mutex, WAIT_LOCK); - add_audit_hook_entry_unlocked(runtime, e); - PyThread_release_lock(runtime->audit_hooks.mutex); - } + PyMutex_Lock(&runtime->audit_hooks.mutex); + add_audit_hook_entry_unlocked(runtime, e); + PyMutex_Unlock(&runtime->audit_hooks.mutex); return 0; } From 64d8b4c7099a6097a7f7340c575679c5622fcd5c Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 7 Dec 2023 13:22:15 -0700 Subject: [PATCH 220/228] gh-112826: Add a "What's New" Entry About _thread._is_main_interpreter (gh-112853) As of gh-112661, the threading module expects the _thread module to have a _is_main_interpreter(), which is used in the internal threading._shutdown(). This change causes a problem for anyone that replaces the _thread module with a custom one (only if they don't provide _is_main_interpreter()). They need to be sure to add it for 3.13+, thus this PR is adding a note in "What's New". This also forward-ports the "What's New" entry from 3.12 (gh-112850). Note that we do not also forward-port the fix in that PR. The fix is there only due to a regression from 3.12.0. There is no regression in 3.13+. --- Doc/whatsnew/3.12.rst | 9 +++++++++ Doc/whatsnew/3.13.rst | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 07d22a4a5fb773..8551b35438e2c3 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -1895,6 +1895,15 @@ Changes in the Python API * Mixing tabs and spaces as indentation in the same file is not supported anymore and will raise a :exc:`TabError`. +* The :mod:`threading` module now expects the :mod:`!_thread` module to have + an ``_is_main_interpreter`` attribute. It is a function with no + arguments that returns ``True`` if the current interpreter is the + main interpreter. + + Any library or application that provides a custom ``_thread`` module + should provide ``_is_main_interpreter()``. + (See :gh:`112826`.) + Build Changes ============= diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 9adf7a3893bd70..4401deb0768c11 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1082,6 +1082,16 @@ Changes in the Python API retrieve a username, instead of :exc:`ImportError` on non-Unix platforms or :exc:`KeyError` on Unix platforms where the password database is empty. +* The :mod:`threading` module now expects the :mod:`!_thread` module to have + an ``_is_main_interpreter`` attribute. It is a function with no + arguments that returns ``True`` if the current interpreter is the + main interpreter. + + Any library or application that provides a custom ``_thread`` module + must provide ``_is_main_interpreter()``, just like the module's + other "private" attributes. + (See :gh:`112826`.) + Build Changes ============= From 2c3906bc4b7ee62bf9d122a6fdd98b6ae330643f Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 7 Dec 2023 20:57:30 +0000 Subject: [PATCH 221/228] gh-101100: Silence Sphinx warnings when `ntpath` or `posixpath` are referenced (#112833) --- Doc/conf.py | 4 ++++ Doc/library/pathlib.rst | 2 +- Doc/tools/.nitignore | 1 - Doc/whatsnew/3.7.rst | 6 +++--- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Doc/conf.py b/Doc/conf.py index 323d443588ceb6..f2d36fdc70430c 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -166,6 +166,10 @@ # Deprecated function that was never documented: ('py:func', 'getargspec'), ('py:func', 'inspect.getargspec'), + # Undocumented modules that users shouldn't have to worry about + # (implementation details of `os.path`): + ('py:mod', 'ntpath'), + ('py:mod', 'posixpath'), ] # Temporary undocumented names. diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 62d4ed5e3f46b9..43200e269f56f4 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -306,7 +306,7 @@ Pure paths provide the following methods and properties: .. attribute:: PurePath.pathmod The implementation of the :mod:`os.path` module used for low-level path - operations: either ``posixpath`` or ``ntpath``. + operations: either :mod:`posixpath` or :mod:`ntpath`. .. versionadded:: 3.13 diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index ada1fc5fafc9c9..8a033f019372f7 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -72,7 +72,6 @@ Doc/library/multiprocessing.rst Doc/library/multiprocessing.shared_memory.rst Doc/library/numbers.rst Doc/library/optparse.rst -Doc/library/os.path.rst Doc/library/os.rst Doc/library/pickle.rst Doc/library/pickletools.rst diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 99f280af84ab01..7a74f9c1685c31 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -2144,9 +2144,9 @@ The following features and APIs have been removed from Python 3.7: * Removed support of the *exclude* argument in :meth:`tarfile.TarFile.add`. It was deprecated in Python 2.7 and 3.2. Use the *filter* argument instead. -* The ``splitunc()`` function in the :mod:`ntpath` module was deprecated in - Python 3.1, and has now been removed. Use the :func:`~os.path.splitdrive` - function instead. +* The :func:`!ntpath.splitunc` function was deprecated in + Python 3.1, and has now been removed. Use :func:`~os.path.splitdrive` + instead. * :func:`collections.namedtuple` no longer supports the *verbose* parameter or ``_source`` attribute which showed the generated source code for the From 28b2b7407c25d448ff5d8836efabbe7c02316568 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Thu, 7 Dec 2023 21:45:40 +0000 Subject: [PATCH 222/228] GH-112675: Move path joining tests into `test_posixpath` and `test_ntpath` (#112676) In `test_pathlib`, the `check_drive_root_parts` test methods evaluated both joining and parsing/normalisation of paths. This dates from a time when pathlib implemented both functions itself, but nowadays path joining is done with `posixpath.join()` and `ntpath.join()`. This commit moves the joining-related test cases into `test_posixpath` and `test_ntpath`. --- Lib/test/test_ntpath.py | 11 +++ Lib/test/test_pathlib.py | 159 +++++++++++++++---------------------- Lib/test/test_posixpath.py | 32 +++++--- 3 files changed, 96 insertions(+), 106 deletions(-) diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 3e710d1c6dabe4..bf990ed36fbcae 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -256,6 +256,7 @@ def test_join(self): tester('ntpath.join("a", "b", "c")', 'a\\b\\c') tester('ntpath.join("a\\", "b", "c")', 'a\\b\\c') tester('ntpath.join("a", "b\\", "c")', 'a\\b\\c') + tester('ntpath.join("a", "b", "c\\")', 'a\\b\\c\\') tester('ntpath.join("a", "b", "\\c")', '\\c') tester('ntpath.join("d:\\", "\\pleep")', 'd:\\pleep') tester('ntpath.join("d:\\", "a", "b")', 'd:\\a\\b') @@ -313,6 +314,16 @@ def test_join(self): tester("ntpath.join('\\\\computer\\', 'share')", '\\\\computer\\share') tester("ntpath.join('\\\\computer\\share\\', 'a')", '\\\\computer\\share\\a') tester("ntpath.join('\\\\computer\\share\\a\\', 'b')", '\\\\computer\\share\\a\\b') + # Second part is anchored, so that the first part is ignored. + tester("ntpath.join('a', 'Z:b', 'c')", 'Z:b\\c') + tester("ntpath.join('a', 'Z:\\b', 'c')", 'Z:\\b\\c') + tester("ntpath.join('a', '\\\\b\\c', 'd')", '\\\\b\\c\\d') + # Second part has a root but not drive. + tester("ntpath.join('a', '\\b', 'c')", '\\b\\c') + tester("ntpath.join('Z:/a', '/b', 'c')", 'Z:\\b\\c') + tester("ntpath.join('//?/Z:/a', '/b', 'c')", '\\\\?\\Z:\\b\\c') + tester("ntpath.join('D:a', './c:b')", 'D:a\\.\\c:b') + tester("ntpath.join('D:/a', './c:b')", 'D:\\a\\.\\c:b') def test_normpath(self): tester("ntpath.normpath('A//////././//.//B')", r'A\B') diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 1b10d6c2f0cb19..ea922143e36e48 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -160,45 +160,30 @@ def with_segments(self, *pathsegments): for parent in p.parents: self.assertEqual(42, parent.session_id) - def _get_drive_root_parts(self, parts): - path = self.cls(*parts) - return path.drive, path.root, path.parts - - def _check_drive_root_parts(self, arg, *expected): + def _check_parse_path(self, raw_path, *expected): sep = self.pathmod.sep - actual = self._get_drive_root_parts([x.replace('/', sep) for x in arg]) + actual = self.cls._parse_path(raw_path.replace('/', sep)) self.assertEqual(actual, expected) if altsep := self.pathmod.altsep: - actual = self._get_drive_root_parts([x.replace('/', altsep) for x in arg]) + actual = self.cls._parse_path(raw_path.replace('/', altsep)) self.assertEqual(actual, expected) - def test_drive_root_parts_common(self): - check = self._check_drive_root_parts + def test_parse_path_common(self): + check = self._check_parse_path sep = self.pathmod.sep - # Unanchored parts. - check((), '', '', ()) - check(('a',), '', '', ('a',)) - check(('a/',), '', '', ('a',)) - check(('a', 'b'), '', '', ('a', 'b')) - # Expansion. - check(('a/b',), '', '', ('a', 'b')) - check(('a/b/',), '', '', ('a', 'b')) - check(('a', 'b/c', 'd'), '', '', ('a', 'b', 'c', 'd')) - # Collapsing and stripping excess slashes. - check(('a', 'b//c', 'd'), '', '', ('a', 'b', 'c', 'd')) - check(('a', 'b/c/', 'd'), '', '', ('a', 'b', 'c', 'd')) - # Eliminating standalone dots. - check(('.',), '', '', ()) - check(('.', '.', 'b'), '', '', ('b',)) - check(('a', '.', 'b'), '', '', ('a', 'b')) - check(('a', '.', '.'), '', '', ('a',)) - # The first part is anchored. - check(('/a/b',), '', sep, (sep, 'a', 'b')) - check(('/a', 'b'), '', sep, (sep, 'a', 'b')) - check(('/a/', 'b'), '', sep, (sep, 'a', 'b')) - # Ignoring parts before an anchored part. - check(('a', '/b', 'c'), '', sep, (sep, 'b', 'c')) - check(('a', '/b', '/c'), '', sep, (sep, 'c')) + check('', '', '', []) + check('a', '', '', ['a']) + check('a/', '', '', ['a']) + check('a/b', '', '', ['a', 'b']) + check('a/b/', '', '', ['a', 'b']) + check('a/b/c/d', '', '', ['a', 'b', 'c', 'd']) + check('a/b//c/d', '', '', ['a', 'b', 'c', 'd']) + check('a/b/c/d', '', '', ['a', 'b', 'c', 'd']) + check('.', '', '', []) + check('././b', '', '', ['b']) + check('a/./b', '', '', ['a', 'b']) + check('a/./.', '', '', ['a']) + check('/a/b', '', sep, ['a', 'b']) def test_join_common(self): P = self.cls @@ -792,17 +777,17 @@ def test_repr_roundtrips(self): class PurePosixPathTest(PurePathTest): cls = pathlib.PurePosixPath - def test_drive_root_parts(self): - check = self._check_drive_root_parts + def test_parse_path(self): + check = self._check_parse_path # Collapsing of excess leading slashes, except for the double-slash # special case. - check(('//a', 'b'), '', '//', ('//', 'a', 'b')) - check(('///a', 'b'), '', '/', ('/', 'a', 'b')) - check(('////a', 'b'), '', '/', ('/', 'a', 'b')) + check('//a/b', '', '//', ['a', 'b']) + check('///a/b', '', '/', ['a', 'b']) + check('////a/b', '', '/', ['a', 'b']) # Paths which look like NT paths aren't treated specially. - check(('c:a',), '', '', ('c:a',)) - check(('c:\\a',), '', '', ('c:\\a',)) - check(('\\a',), '', '', ('\\a',)) + check('c:a', '', '', ['c:a',]) + check('c:\\a', '', '', ['c:\\a',]) + check('\\a', '', '', ['\\a',]) def test_root(self): P = self.cls @@ -900,67 +885,53 @@ class PureWindowsPathTest(PurePathTest): ], }) - def test_drive_root_parts(self): - check = self._check_drive_root_parts + def test_parse_path(self): + check = self._check_parse_path # First part is anchored. - check(('c:',), 'c:', '', ('c:',)) - check(('c:/',), 'c:', '\\', ('c:\\',)) - check(('/',), '', '\\', ('\\',)) - check(('c:a',), 'c:', '', ('c:', 'a')) - check(('c:/a',), 'c:', '\\', ('c:\\', 'a')) - check(('/a',), '', '\\', ('\\', 'a')) - # UNC paths. - check(('//',), '\\\\', '', ('\\\\',)) - check(('//a',), '\\\\a', '', ('\\\\a',)) - check(('//a/',), '\\\\a\\', '', ('\\\\a\\',)) - check(('//a/b',), '\\\\a\\b', '\\', ('\\\\a\\b\\',)) - check(('//a/b/',), '\\\\a\\b', '\\', ('\\\\a\\b\\',)) - check(('//a/b/c',), '\\\\a\\b', '\\', ('\\\\a\\b\\', 'c')) - # Second part is anchored, so that the first part is ignored. - check(('a', 'Z:b', 'c'), 'Z:', '', ('Z:', 'b', 'c')) - check(('a', 'Z:/b', 'c'), 'Z:', '\\', ('Z:\\', 'b', 'c')) + check('c:', 'c:', '', []) + check('c:/', 'c:', '\\', []) + check('/', '', '\\', []) + check('c:a', 'c:', '', ['a']) + check('c:/a', 'c:', '\\', ['a']) + check('/a', '', '\\', ['a']) # UNC paths. - check(('a', '//b/c', 'd'), '\\\\b\\c', '\\', ('\\\\b\\c\\', 'd')) + check('//', '\\\\', '', []) + check('//a', '\\\\a', '', []) + check('//a/', '\\\\a\\', '', []) + check('//a/b', '\\\\a\\b', '\\', []) + check('//a/b/', '\\\\a\\b', '\\', []) + check('//a/b/c', '\\\\a\\b', '\\', ['c']) # Collapsing and stripping excess slashes. - check(('a', 'Z://b//c/', 'd/'), 'Z:', '\\', ('Z:\\', 'b', 'c', 'd')) + check('Z://b//c/d/', 'Z:', '\\', ['b', 'c', 'd']) # UNC paths. - check(('a', '//b/c//', 'd'), '\\\\b\\c', '\\', ('\\\\b\\c\\', 'd')) + check('//b/c//d', '\\\\b\\c', '\\', ['d']) # Extended paths. - check(('//./c:',), '\\\\.\\c:', '', ('\\\\.\\c:',)) - check(('//?/c:/',), '\\\\?\\c:', '\\', ('\\\\?\\c:\\',)) - check(('//?/c:/a',), '\\\\?\\c:', '\\', ('\\\\?\\c:\\', 'a')) - check(('//?/c:/a', '/b'), '\\\\?\\c:', '\\', ('\\\\?\\c:\\', 'b')) + check('//./c:', '\\\\.\\c:', '', []) + check('//?/c:/', '\\\\?\\c:', '\\', []) + check('//?/c:/a', '\\\\?\\c:', '\\', ['a']) # Extended UNC paths (format is "\\?\UNC\server\share"). - check(('//?',), '\\\\?', '', ('\\\\?',)) - check(('//?/',), '\\\\?\\', '', ('\\\\?\\',)) - check(('//?/UNC',), '\\\\?\\UNC', '', ('\\\\?\\UNC',)) - check(('//?/UNC/',), '\\\\?\\UNC\\', '', ('\\\\?\\UNC\\',)) - check(('//?/UNC/b',), '\\\\?\\UNC\\b', '', ('\\\\?\\UNC\\b',)) - check(('//?/UNC/b/',), '\\\\?\\UNC\\b\\', '', ('\\\\?\\UNC\\b\\',)) - check(('//?/UNC/b/c',), '\\\\?\\UNC\\b\\c', '\\', ('\\\\?\\UNC\\b\\c\\',)) - check(('//?/UNC/b/c/',), '\\\\?\\UNC\\b\\c', '\\', ('\\\\?\\UNC\\b\\c\\',)) - check(('//?/UNC/b/c/d',), '\\\\?\\UNC\\b\\c', '\\', ('\\\\?\\UNC\\b\\c\\', 'd')) + check('//?', '\\\\?', '', []) + check('//?/', '\\\\?\\', '', []) + check('//?/UNC', '\\\\?\\UNC', '', []) + check('//?/UNC/', '\\\\?\\UNC\\', '', []) + check('//?/UNC/b', '\\\\?\\UNC\\b', '', []) + check('//?/UNC/b/', '\\\\?\\UNC\\b\\', '', []) + check('//?/UNC/b/c', '\\\\?\\UNC\\b\\c', '\\', []) + check('//?/UNC/b/c/', '\\\\?\\UNC\\b\\c', '\\', []) + check('//?/UNC/b/c/d', '\\\\?\\UNC\\b\\c', '\\', ['d']) # UNC device paths - check(('//./BootPartition/',), '\\\\.\\BootPartition', '\\', ('\\\\.\\BootPartition\\',)) - check(('//?/BootPartition/',), '\\\\?\\BootPartition', '\\', ('\\\\?\\BootPartition\\',)) - check(('//./PhysicalDrive0',), '\\\\.\\PhysicalDrive0', '', ('\\\\.\\PhysicalDrive0',)) - check(('//?/Volume{}/',), '\\\\?\\Volume{}', '\\', ('\\\\?\\Volume{}\\',)) - check(('//./nul',), '\\\\.\\nul', '', ('\\\\.\\nul',)) - # Second part has a root but not drive. - check(('a', '/b', 'c'), '', '\\', ('\\', 'b', 'c')) - check(('Z:/a', '/b', 'c'), 'Z:', '\\', ('Z:\\', 'b', 'c')) - check(('//?/Z:/a', '/b', 'c'), '\\\\?\\Z:', '\\', ('\\\\?\\Z:\\', 'b', 'c')) - # Joining with the same drive => the first path is appended to if - # the second path is relative. - check(('c:/a/b', 'c:x/y'), 'c:', '\\', ('c:\\', 'a', 'b', 'x', 'y')) - check(('c:/a/b', 'c:/x/y'), 'c:', '\\', ('c:\\', 'x', 'y')) + check('//./BootPartition/', '\\\\.\\BootPartition', '\\', []) + check('//?/BootPartition/', '\\\\?\\BootPartition', '\\', []) + check('//./PhysicalDrive0', '\\\\.\\PhysicalDrive0', '', []) + check('//?/Volume{}/', '\\\\?\\Volume{}', '\\', []) + check('//./nul', '\\\\.\\nul', '', []) # Paths to files with NTFS alternate data streams - check(('./c:s',), '', '', ('c:s',)) - check(('cc:s',), '', '', ('cc:s',)) - check(('C:c:s',), 'C:', '', ('C:', 'c:s')) - check(('C:/c:s',), 'C:', '\\', ('C:\\', 'c:s')) - check(('D:a', './c:b'), 'D:', '', ('D:', 'a', 'c:b')) - check(('D:/a', './c:b'), 'D:', '\\', ('D:\\', 'a', 'c:b')) + check('./c:s', '', '', ['c:s']) + check('cc:s', '', '', ['cc:s']) + check('C:c:s', 'C:', '', ['c:s']) + check('C:/c:s', 'C:', '\\', ['c:s']) + check('D:a/c:b', 'D:', '', ['a', 'c:b']) + check('D:/a/c:b', 'D:', '\\', ['a', 'c:b']) def test_str(self): p = self.cls('a/b/c') diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index 9be4640f970aef..86ce1b1d41ba61 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -47,18 +47,26 @@ def tearDown(self): safe_rmdir(os_helper.TESTFN + suffix) def test_join(self): - self.assertEqual(posixpath.join("/foo", "bar", "/bar", "baz"), - "/bar/baz") - self.assertEqual(posixpath.join("/foo", "bar", "baz"), "/foo/bar/baz") - self.assertEqual(posixpath.join("/foo/", "bar/", "baz/"), - "/foo/bar/baz/") - - self.assertEqual(posixpath.join(b"/foo", b"bar", b"/bar", b"baz"), - b"/bar/baz") - self.assertEqual(posixpath.join(b"/foo", b"bar", b"baz"), - b"/foo/bar/baz") - self.assertEqual(posixpath.join(b"/foo/", b"bar/", b"baz/"), - b"/foo/bar/baz/") + fn = posixpath.join + self.assertEqual(fn("/foo", "bar", "/bar", "baz"), "/bar/baz") + self.assertEqual(fn("/foo", "bar", "baz"), "/foo/bar/baz") + self.assertEqual(fn("/foo/", "bar/", "baz/"), "/foo/bar/baz/") + + self.assertEqual(fn(b"/foo", b"bar", b"/bar", b"baz"), b"/bar/baz") + self.assertEqual(fn(b"/foo", b"bar", b"baz"), b"/foo/bar/baz") + self.assertEqual(fn(b"/foo/", b"bar/", b"baz/"), b"/foo/bar/baz/") + + self.assertEqual(fn("a", "b"), "a/b") + self.assertEqual(fn("a", "b/"), "a/b/") + self.assertEqual(fn("a/", "b"), "a/b") + self.assertEqual(fn("a/", "b/"), "a/b/") + self.assertEqual(fn("a", "b/c", "d"), "a/b/c/d") + self.assertEqual(fn("a", "b//c", "d"), "a/b//c/d") + self.assertEqual(fn("a", "b/c/", "d"), "a/b/c/d") + self.assertEqual(fn("/a", "b"), "/a/b") + self.assertEqual(fn("/a/", "b"), "/a/b") + self.assertEqual(fn("a", "/b", "c"), "/b/c") + self.assertEqual(fn("a", "/b", "/c"), "/c") def test_split(self): self.assertEqual(posixpath.split("/foo/bar"), ("/foo", "bar")) From 4ac1e8fb25c5c0e1da61784281ab878db671761b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 8 Dec 2023 10:18:15 +0200 Subject: [PATCH 223/228] Add a versionchanged directive for gh-94692 (GH-112846) Co-authored-by: Alex Waygood --- Doc/library/shutil.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index d30d289710b129..f61ef8b0ecc7ba 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -346,6 +346,8 @@ Directory and files operations .. versionchanged:: 3.13 :func:`!rmtree` now ignores :exc:`FileNotFoundError` exceptions for all but the top-level path. + Exceptions other than :exc:`OSError` and subclasses of :exc:`!OSError` + are now always propagated to the caller. .. attribute:: rmtree.avoids_symlink_attacks From 15a80b15af9a0b0ebe6bd538a1919712ce7d4ef9 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Fri, 8 Dec 2023 10:09:34 +0100 Subject: [PATCH 224/228] gh-110820: Make sure processor specific defines are correct for Universal 2 build on macOS (#112828) * gh-110820: Make sure processor specific defines are correct for Universal 2 build on macOS A number of processor specific defines are different for x86-64 and arm64, and need to be adjusted in pymacconfig.h. * remove debug stuf --- Include/pymacconfig.h | 11 ++++++++++- .../2023-12-07-14-19-46.gh-issue-110820.DIxb_F.rst | 3 +++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/macOS/2023-12-07-14-19-46.gh-issue-110820.DIxb_F.rst diff --git a/Include/pymacconfig.h b/Include/pymacconfig.h index 806e41955efd7f..615abe103ca038 100644 --- a/Include/pymacconfig.h +++ b/Include/pymacconfig.h @@ -7,7 +7,9 @@ #define PY_MACCONFIG_H #ifdef __APPLE__ +#undef ALIGNOF_MAX_ALIGN_T #undef SIZEOF_LONG +#undef SIZEOF_LONG_DOUBLE #undef SIZEOF_PTHREAD_T #undef SIZEOF_SIZE_T #undef SIZEOF_TIME_T @@ -20,6 +22,7 @@ #undef DOUBLE_IS_BIG_ENDIAN_IEEE754 #undef DOUBLE_IS_LITTLE_ENDIAN_IEEE754 #undef HAVE_GCC_ASM_FOR_X87 +#undef HAVE_GCC_ASM_FOR_X64 #undef VA_LIST_IS_ARRAY #if defined(__LP64__) && defined(__x86_64__) @@ -74,8 +77,14 @@ # define DOUBLE_IS_LITTLE_ENDIAN_IEEE754 #endif -#ifdef __i386__ +#if defined(__i386__) || defined(__x86_64__) # define HAVE_GCC_ASM_FOR_X87 +# define ALIGNOF_MAX_ALIGN_T 16 +# define HAVE_GCC_ASM_FOR_X64 1 +# define SIZEOF_LONG_DOUBLE 16 +#else +# define ALIGNOF_MAX_ALIGN_T 8 +# define SIZEOF_LONG_DOUBLE 8 #endif #endif // __APPLE__ diff --git a/Misc/NEWS.d/next/macOS/2023-12-07-14-19-46.gh-issue-110820.DIxb_F.rst b/Misc/NEWS.d/next/macOS/2023-12-07-14-19-46.gh-issue-110820.DIxb_F.rst new file mode 100644 index 00000000000000..0badace7928745 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-12-07-14-19-46.gh-issue-110820.DIxb_F.rst @@ -0,0 +1,3 @@ +Make sure the preprocessor definitions for ``ALIGNOF_MAX_ALIGN_T``, +``SIZEOF_LONG_DOUBLE`` and ``HAVE_GCC_ASM_FOR_X64`` are correct for +Universal 2 builds on macOS. From aefdebdef16b8e9e6c1c2a0a54d6cb25bd8e28dc Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 8 Dec 2023 11:48:30 +0000 Subject: [PATCH 225/228] GH-111485: Factor out opcode ID generator from the main cases generator. (GH-112831) --- Include/opcode_ids.h | 9 +- Makefile.pre.in | 3 +- Tools/cases_generator/cwriter.py | 7 +- Tools/cases_generator/generate_cases.py | 47 +----- Tools/cases_generator/generators_common.py | 19 +++ Tools/cases_generator/opcode_id_generator.py | 153 +++++++++++++++++++ Tools/cases_generator/tier1_generator.py | 37 ++--- 7 files changed, 203 insertions(+), 72 deletions(-) create mode 100644 Tools/cases_generator/generators_common.py create mode 100644 Tools/cases_generator/opcode_id_generator.py diff --git a/Include/opcode_ids.h b/Include/opcode_ids.h index ba25bd459c1bcd..47f809e345f61c 100644 --- a/Include/opcode_ids.h +++ b/Include/opcode_ids.h @@ -1,6 +1,6 @@ -// This file is generated by Tools/cases_generator/generate_cases.py +// This file is generated by Tools/cases_generator/opcode_id_generator.py // from: -// Python/bytecodes.c +// ['./Python/bytecodes.c'] // Do not edit! #ifndef Py_OPCODE_IDS_H @@ -55,7 +55,6 @@ extern "C" { #define UNARY_NEGATIVE 42 #define UNARY_NOT 43 #define WITH_EXCEPT_START 44 -#define HAVE_ARGUMENT 45 #define BINARY_OP 45 #define BUILD_CONST_KEY_MAP 46 #define BUILD_LIST 47 @@ -200,7 +199,6 @@ extern "C" { #define UNPACK_SEQUENCE_LIST 216 #define UNPACK_SEQUENCE_TUPLE 217 #define UNPACK_SEQUENCE_TWO_TUPLE 218 -#define MIN_INSTRUMENTED_OPCODE 236 #define INSTRUMENTED_RESUME 236 #define INSTRUMENTED_END_FOR 237 #define INSTRUMENTED_END_SEND 238 @@ -233,6 +231,9 @@ extern "C" { #define SETUP_WITH 266 #define STORE_FAST_MAYBE_NULL 267 +#define HAVE_ARGUMENT 45 +#define MIN_INSTRUMENTED_OPCODE 236 + #ifdef __cplusplus } #endif diff --git a/Makefile.pre.in b/Makefile.pre.in index f57894a2118e74..6ca11f080dcc3f 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1587,13 +1587,14 @@ regen-cases: $(PYTHON_FOR_REGEN) \ $(srcdir)/Tools/cases_generator/generate_cases.py \ $(CASESFLAG) \ - -n $(srcdir)/Include/opcode_ids.h.new \ -t $(srcdir)/Python/opcode_targets.h.new \ -m $(srcdir)/Include/internal/pycore_opcode_metadata.h.new \ -e $(srcdir)/Python/executor_cases.c.h.new \ -p $(srcdir)/Lib/_opcode_metadata.py.new \ -a $(srcdir)/Python/abstract_interp_cases.c.h.new \ $(srcdir)/Python/bytecodes.c + $(PYTHON_FOR_REGEN) \ + $(srcdir)/Tools/cases_generator/opcode_id_generator.py -o $(srcdir)/Include/opcode_ids.h.new $(srcdir)/Python/bytecodes.c $(PYTHON_FOR_REGEN) \ $(srcdir)/Tools/cases_generator/tier1_generator.py -o $(srcdir)/Python/generated_cases.c.h.new $(srcdir)/Python/bytecodes.c $(UPDATE_FILE) $(srcdir)/Python/generated_cases.c.h $(srcdir)/Python/generated_cases.c.h.new diff --git a/Tools/cases_generator/cwriter.py b/Tools/cases_generator/cwriter.py index 0b7edd03fd9e47..34e39855a9b40a 100644 --- a/Tools/cases_generator/cwriter.py +++ b/Tools/cases_generator/cwriter.py @@ -48,8 +48,13 @@ def maybe_indent(self, txt: str) -> None: if offset <= self.indents[-1] or offset > 40: offset = self.indents[-1] + 4 self.indents.append(offset) - elif "{" in txt or is_label(txt): + if is_label(txt): self.indents.append(self.indents[-1] + 4) + elif "{" in txt: + if 'extern "C"' in txt: + self.indents.append(self.indents[-1]) + else: + self.indents.append(self.indents[-1] + 4) def emit_text(self, txt: str) -> None: self.out.write(txt) diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 4b7f028970bd0c..d0fdc4a0aeb7b0 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -101,13 +101,6 @@ arg_parser.add_argument( "-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT ) -arg_parser.add_argument( - "-n", - "--opcode_ids_h", - type=str, - help="Header file with opcode number definitions", - default=DEFAULT_OPCODE_IDS_H_OUTPUT, -) arg_parser.add_argument( "-t", "--opcode_targets_h", @@ -334,42 +327,8 @@ def map_op(op: int, name: str) -> None: self.opmap = opmap self.markers = markers - def write_opcode_ids( - self, opcode_ids_h_filename: str, opcode_targets_filename: str - ) -> None: - """Write header file that defined the opcode IDs""" - - with open(opcode_ids_h_filename, "w") as f: - # Create formatter - self.out = Formatter(f, 0) - - self.write_provenance_header() - - self.out.emit("") - self.out.emit("#ifndef Py_OPCODE_IDS_H") - self.out.emit("#define Py_OPCODE_IDS_H") - self.out.emit("#ifdef __cplusplus") - self.out.emit('extern "C" {') - self.out.emit("#endif") - self.out.emit("") - self.out.emit("/* Instruction opcodes for compiled code */") - - def define(name: str, opcode: int) -> None: - self.out.emit(f"#define {name:<38} {opcode:>3}") - - all_pairs: list[tuple[int, int, str]] = [] - # the second item in the tuple sorts the markers before the ops - all_pairs.extend((i, 1, name) for (name, i) in self.markers.items()) - all_pairs.extend((i, 2, name) for (name, i) in self.opmap.items()) - for i, _, name in sorted(all_pairs): - assert name is not None - define(name, i) - - self.out.emit("") - self.out.emit("#ifdef __cplusplus") - self.out.emit("}") - self.out.emit("#endif") - self.out.emit("#endif /* !Py_OPCODE_IDS_H */") + def write_opcode_targets(self, opcode_targets_filename: str) -> None: + """Write header file that defines the jump target table""" with open(opcode_targets_filename, "w") as f: # Create formatter @@ -885,7 +844,7 @@ def main() -> None: # These raise OSError if output can't be written a.assign_opcode_ids() - a.write_opcode_ids(args.opcode_ids_h, args.opcode_targets_h) + a.write_opcode_targets(args.opcode_targets_h) a.write_metadata(args.metadata, args.pymetadata) a.write_executor_instructions(args.executor_cases, args.emit_line_directives) a.write_abstract_interpreter_instructions( diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py new file mode 100644 index 00000000000000..76900d1efffd5d --- /dev/null +++ b/Tools/cases_generator/generators_common.py @@ -0,0 +1,19 @@ +from pathlib import Path +from typing import TextIO + +ROOT = Path(__file__).parent.parent.parent +DEFAULT_INPUT = (ROOT / "Python/bytecodes.c").absolute() + + +def root_relative_path(filename: str) -> str: + return Path(filename).relative_to(ROOT).as_posix() + + +def write_header(generator: str, source: str, outfile: TextIO) -> None: + outfile.write( + f"""// This file is generated by {root_relative_path(generator)} +// from: +// {source} +// Do not edit! +""" + ) diff --git a/Tools/cases_generator/opcode_id_generator.py b/Tools/cases_generator/opcode_id_generator.py new file mode 100644 index 00000000000000..a1f6f62156ebd3 --- /dev/null +++ b/Tools/cases_generator/opcode_id_generator.py @@ -0,0 +1,153 @@ +"""Generate the list of opcode IDs. +Reads the instruction definitions from bytecodes.c. +Writes the IDs to opcode._ids.h by default. +""" + +import argparse +import os.path +import sys + +from analyzer import ( + Analysis, + Instruction, + analyze_files, +) +from generators_common import ( + DEFAULT_INPUT, + ROOT, + write_header, +) +from cwriter import CWriter +from typing import TextIO + + +DEFAULT_OUTPUT = ROOT / "Include/opcode_ids.h" + + +def generate_opcode_header(filenames: str, analysis: Analysis, outfile: TextIO) -> None: + write_header(__file__, filenames, outfile) + out = CWriter(outfile, 0, False) + out.emit("\n") + instmap: dict[str, int] = {} + + # 0 is reserved for cache entries. This helps debugging. + instmap["CACHE"] = 0 + + # 17 is reserved as it is the initial value for the specializing counter. + # This helps catch cases where we attempt to execute a cache. + instmap["RESERVED"] = 17 + + # 149 is RESUME - it is hard coded as such in Tools/build/deepfreeze.py + instmap["RESUME"] = 149 + instmap["INSTRUMENTED_LINE"] = 254 + + instrumented = [ + name for name in analysis.instructions if name.startswith("INSTRUMENTED") + ] + + # Special case: this instruction is implemented in ceval.c + # rather than bytecodes.c, so we need to add it explicitly + # here (at least until we add something to bytecodes.c to + # declare external instructions). + instrumented.append("INSTRUMENTED_LINE") + + specialized: set[str] = set() + no_arg: list[str] = [] + has_arg: list[str] = [] + + for family in analysis.families.values(): + specialized.update(inst.name for inst in family.members) + + for inst in analysis.instructions.values(): + name = inst.name + if name in specialized: + continue + if name in instrumented: + continue + if inst.properties.oparg: + has_arg.append(name) + else: + no_arg.append(name) + + # Specialized ops appear in their own section + # Instrumented opcodes are at the end of the valid range + min_internal = 150 + min_instrumented = 254 - (len(instrumented) - 1) + assert min_internal + len(specialized) < min_instrumented + + next_opcode = 1 + + def add_instruction(name: str) -> None: + nonlocal next_opcode + if name in instmap: + return # Pre-defined name + while next_opcode in instmap.values(): + next_opcode += 1 + instmap[name] = next_opcode + next_opcode += 1 + + for name in sorted(no_arg): + add_instruction(name) + for name in sorted(has_arg): + add_instruction(name) + # For compatibility + next_opcode = min_internal + for name in sorted(specialized): + add_instruction(name) + next_opcode = min_instrumented + for name in instrumented: + add_instruction(name) + + for op, name in enumerate(sorted(analysis.pseudos), 256): + instmap[name] = op + + assert 255 not in instmap.values() + + out.emit( + """#ifndef Py_OPCODE_IDS_H +#define Py_OPCODE_IDS_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Instruction opcodes for compiled code */ +""" + ) + + def write_define(name: str, op: int) -> None: + out.emit(f"#define {name:<38} {op:>3}\n") + + for op, name in sorted([(op, name) for (name, op) in instmap.items()]): + write_define(name, op) + + out.emit("\n") + write_define("HAVE_ARGUMENT", len(no_arg)) + write_define("MIN_INSTRUMENTED_OPCODE", min_instrumented) + + out.emit("\n") + out.emit("#ifdef __cplusplus\n") + out.emit("}\n") + out.emit("#endif\n") + out.emit("#endif /* !Py_OPCODE_IDS_H */\n") + + +arg_parser = argparse.ArgumentParser( + description="Generate the header file with all opcode IDs.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, +) + +arg_parser.add_argument( + "-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT +) + +arg_parser.add_argument( + "input", nargs=argparse.REMAINDER, help="Instruction definition file(s)" +) + +if __name__ == "__main__": + args = arg_parser.parse_args() + if len(args.input) == 0: + args.input.append(DEFAULT_INPUT) + data = analyze_files(args.input) + with open(args.output, "w") as outfile: + generate_opcode_header(args.input, data, outfile) diff --git a/Tools/cases_generator/tier1_generator.py b/Tools/cases_generator/tier1_generator.py index eba926435d2415..9787403b3bbc47 100644 --- a/Tools/cases_generator/tier1_generator.py +++ b/Tools/cases_generator/tier1_generator.py @@ -17,33 +17,18 @@ StackItem, analysis_error, ) +from generators_common import ( + DEFAULT_INPUT, + ROOT, + write_header, +) from cwriter import CWriter from typing import TextIO, Iterator from lexer import Token from stack import StackOffset -HERE = os.path.dirname(__file__) -ROOT = os.path.join(HERE, "../..") -THIS = os.path.relpath(__file__, ROOT).replace(os.path.sep, "/") - -DEFAULT_INPUT = os.path.relpath(os.path.join(ROOT, "Python/bytecodes.c")) -DEFAULT_OUTPUT = os.path.relpath(os.path.join(ROOT, "Python/generated_cases.c.h")) - - -def write_header(filename: str, outfile: TextIO) -> None: - outfile.write( - f"""// This file is generated by {THIS} -// from: -// {filename} -// Do not edit! - -#ifdef TIER_TWO - #error "This file is for Tier 1 only" -#endif -#define TIER_ONE 1 -""" - ) +DEFAULT_OUTPUT = ROOT / "Python/generated_cases.c.h" FOOTER = "#undef TIER_ONE\n" @@ -351,7 +336,15 @@ def uses_this(inst: Instruction) -> bool: def generate_tier1( filenames: str, analysis: Analysis, outfile: TextIO, lines: bool ) -> None: - write_header(filenames, outfile) + write_header(__file__, filenames, outfile) + outfile.write( + """ +#ifdef TIER_TWO + #error "This file is for Tier 1 only" +#endif +#define TIER_ONE 1 +""" + ) out = CWriter(outfile, 2, lines) out.emit("\n") for name, inst in sorted(analysis.instructions.items()): From 3cdcc2edf81c7be4c88d4f273947ce29f916f49a Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 8 Dec 2023 12:31:11 +0000 Subject: [PATCH 226/228] gh-101100: Fix Sphinx nitpicks in `library/shelve.rst` (#112836) --- Doc/library/shelve.rst | 9 +++++---- Doc/tools/.nitignore | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/shelve.rst b/Doc/library/shelve.rst index 219219af6fd87f..88802d717d7383 100644 --- a/Doc/library/shelve.rst +++ b/Doc/library/shelve.rst @@ -149,13 +149,14 @@ Restrictions .. class:: BsdDbShelf(dict, protocol=None, writeback=False, keyencoding='utf-8') - A subclass of :class:`Shelf` which exposes :meth:`first`, :meth:`!next`, - :meth:`previous`, :meth:`last` and :meth:`set_location` which are available - in the third-party :mod:`bsddb` module from `pybsddb + A subclass of :class:`Shelf` which exposes :meth:`!first`, :meth:`!next`, + :meth:`!previous`, :meth:`!last` and :meth:`!set_location` methods. + These are available + in the third-party :mod:`!bsddb` module from `pybsddb `_ but not in other database modules. The *dict* object passed to the constructor must support those methods. This is generally accomplished by calling one of - :func:`bsddb.hashopen`, :func:`bsddb.btopen` or :func:`bsddb.rnopen`. The + :func:`!bsddb.hashopen`, :func:`!bsddb.btopen` or :func:`!bsddb.rnopen`. The optional *protocol*, *writeback*, and *keyencoding* parameters have the same interpretation as for the :class:`Shelf` class. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 8a033f019372f7..18dddb26867837 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -86,7 +86,6 @@ Doc/library/readline.rst Doc/library/resource.rst Doc/library/rlcompleter.rst Doc/library/select.rst -Doc/library/shelve.rst Doc/library/signal.rst Doc/library/smtplib.rst Doc/library/socket.rst From e4c087603397a1314253b861d35f8314fba8ae92 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 8 Dec 2023 13:17:57 +0000 Subject: [PATCH 227/228] gh-101100: Fix Sphinx nits in `library/contextlib.rst` (#112870) --- Doc/library/contextlib.rst | 14 +++++++------- Doc/tools/.nitignore | 1 - 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Doc/library/contextlib.rst b/Doc/library/contextlib.rst index f6ebbfacfba509..aab319cbe7405e 100644 --- a/Doc/library/contextlib.rst +++ b/Doc/library/contextlib.rst @@ -106,8 +106,8 @@ Functions and classes provided: This function is a :term:`decorator` that can be used to define a factory function for :keyword:`async with` statement asynchronous context managers, - without needing to create a class or separate :meth:`__aenter__` and - :meth:`__aexit__` methods. It must be applied to an :term:`asynchronous + without needing to create a class or separate :meth:`~object.__aenter__` and + :meth:`~object.__aexit__` methods. It must be applied to an :term:`asynchronous generator` function. A simple example:: @@ -616,12 +616,12 @@ Functions and classes provided: asynchronous context managers, as well as having coroutines for cleanup logic. - The :meth:`close` method is not implemented, :meth:`aclose` must be used + The :meth:`~ExitStack.close` method is not implemented; :meth:`aclose` must be used instead. .. coroutinemethod:: enter_async_context(cm) - Similar to :meth:`enter_context` but expects an asynchronous context + Similar to :meth:`ExitStack.enter_context` but expects an asynchronous context manager. .. versionchanged:: 3.11 @@ -630,16 +630,16 @@ Functions and classes provided: .. method:: push_async_exit(exit) - Similar to :meth:`push` but expects either an asynchronous context manager + Similar to :meth:`ExitStack.push` but expects either an asynchronous context manager or a coroutine function. .. method:: push_async_callback(callback, /, *args, **kwds) - Similar to :meth:`callback` but expects a coroutine function. + Similar to :meth:`ExitStack.callback` but expects a coroutine function. .. coroutinemethod:: aclose() - Similar to :meth:`close` but properly handles awaitables. + Similar to :meth:`ExitStack.close` but properly handles awaitables. Continuing the example for :func:`asynccontextmanager`:: diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 18dddb26867837..5ef68cc089d436 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -39,7 +39,6 @@ Doc/library/collections.abc.rst Doc/library/collections.rst Doc/library/concurrent.futures.rst Doc/library/configparser.rst -Doc/library/contextlib.rst Doc/library/csv.rst Doc/library/datetime.rst Doc/library/dbm.rst From ed21d0c1f4bd17b392e24bfd83e652723dad4ddf Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 8 Dec 2023 13:18:53 +0000 Subject: [PATCH 228/228] gh-101100: Improve documentation for attributes on instance methods (#112832) --- Doc/library/inspect.rst | 2 +- Doc/library/stdtypes.rst | 23 ++++++----- Doc/reference/datamodel.rst | 77 +++++++++++++++++++++++++------------ Doc/tutorial/classes.rst | 6 ++- Doc/whatsnew/2.6.rst | 4 +- Doc/whatsnew/2.7.rst | 5 ++- 6 files changed, 76 insertions(+), 41 deletions(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 0138557f5fd84c..8381e508139fbd 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -492,7 +492,7 @@ attributes (see :ref:`import-mod-attrs` for module attributes): Methods implemented via descriptors that also pass one of the other tests return ``False`` from the :func:`ismethoddescriptor` test, simply because the other tests promise more -- you can, e.g., count on having the - :ref:`__func__ ` attribute (etc) when an object passes + :attr:`~method.__func__` attribute (etc) when an object passes :func:`ismethod`. diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 44c13bd9474ea1..1265b5b12e492d 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -5328,25 +5328,30 @@ Methods .. index:: pair: object; method Methods are functions that are called using the attribute notation. There are -two flavors: built-in methods (such as :meth:`append` on lists) and class -instance methods. Built-in methods are described with the types that support -them. +two flavors: :ref:`built-in methods ` (such as :meth:`append` +on lists) and :ref:`class instance method `. +Built-in methods are described with the types that support them. If you access a method (a function defined in a class namespace) through an instance, you get a special object: a :dfn:`bound method` (also called -:dfn:`instance method`) object. When called, it will add the ``self`` argument +:ref:`instance method `) object. When called, it will add +the ``self`` argument to the argument list. Bound methods have two special read-only attributes: -``m.__self__`` is the object on which the method operates, and ``m.__func__`` is +:attr:`m.__self__ ` is the object on which the method +operates, and :attr:`m.__func__ ` is the function implementing the method. Calling ``m(arg-1, arg-2, ..., arg-n)`` is completely equivalent to calling ``m.__func__(m.__self__, arg-1, arg-2, ..., arg-n)``. -Like function objects, bound method objects support getting arbitrary +Like :ref:`function objects `, bound method objects support +getting arbitrary attributes. However, since method attributes are actually stored on the -underlying function object (``meth.__func__``), setting method attributes on +underlying function object (:attr:`method.__func__`), setting method attributes on bound methods is disallowed. Attempting to set an attribute on a method results in an :exc:`AttributeError` being raised. In order to set a method -attribute, you need to explicitly set it on the underlying function object:: +attribute, you need to explicitly set it on the underlying function object: + +.. doctest:: >>> class C: ... def method(self): @@ -5361,7 +5366,7 @@ attribute, you need to explicitly set it on the underlying function object:: >>> c.method.whoami 'my name is method' -See :ref:`types` for more information. +See :ref:`instance-methods` for more information. .. index:: object; code, code object diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 3bcc170faa087a..27d379a8b70f31 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -519,6 +519,8 @@ These are the types to which the function call operation (see section :ref:`calls`) can be applied: +.. _user-defined-funcs: + User-defined functions ^^^^^^^^^^^^^^^^^^^^^^ @@ -654,43 +656,64 @@ callable object (normally a user-defined function). single: __name__ (method attribute) single: __module__ (method attribute) -Special read-only attributes: :attr:`__self__` is the class instance object, -:attr:`__func__` is the function object; :attr:`__doc__` is the method's -documentation (same as ``__func__.__doc__``); :attr:`~definition.__name__` is the -method name (same as ``__func__.__name__``); :attr:`__module__` is the -name of the module the method was defined in, or ``None`` if unavailable. +Special read-only attributes: + +.. list-table:: + + * - .. attribute:: method.__self__ + - Refers to the class instance object to which the method is + :ref:`bound ` + + * - .. attribute:: method.__func__ + - Refers to the original function object + + * - .. attribute:: method.__doc__ + - The method's documentation (same as :attr:`!method.__func__.__doc__`). + A :class:`string ` if the original function had a docstring, else + ``None``. + + * - .. attribute:: method.__name__ + - The name of the method (same as :attr:`!method.__func__.__name__`) + + * - .. attribute:: method.__module__ + - The name of the module the method was defined in, or ``None`` if + unavailable. Methods also support accessing (but not setting) the arbitrary function -attributes on the underlying function object. +attributes on the underlying :ref:`function object `. User-defined method objects may be created when getting an attribute of a class (perhaps via an instance of that class), if that attribute is a -user-defined function object or a class method object. +user-defined :ref:`function object ` or a +:class:`classmethod` object. + +.. _method-binding: When an instance method object is created by retrieving a user-defined -function object from a class via one of its instances, its -:attr:`__self__` attribute is the instance, and the method object is said -to be bound. The new method's :attr:`__func__` attribute is the original -function object. - -When an instance method object is created by retrieving a class method -object from a class or instance, its :attr:`__self__` attribute is the -class itself, and its :attr:`__func__` attribute is the function object +:ref:`function object ` from a class via one of its +instances, its :attr:`~method.__self__` attribute is the instance, and the +method object is said to be *bound*. The new method's :attr:`~method.__func__` +attribute is the original function object. + +When an instance method object is created by retrieving a :class:`classmethod` +object from a class or instance, its :attr:`~method.__self__` attribute is the +class itself, and its :attr:`~method.__func__` attribute is the function object underlying the class method. When an instance method object is called, the underlying function -(:attr:`__func__`) is called, inserting the class instance -(:attr:`__self__`) in front of the argument list. For instance, when +(:attr:`~method.__func__`) is called, inserting the class instance +(:attr:`~method.__self__`) in front of the argument list. For instance, when :class:`!C` is a class which contains a definition for a function :meth:`!f`, and ``x`` is an instance of :class:`!C`, calling ``x.f(1)`` is equivalent to calling ``C.f(x, 1)``. -When an instance method object is derived from a class method object, the -"class instance" stored in :attr:`__self__` will actually be the class +When an instance method object is derived from a :class:`classmethod` object, the +"class instance" stored in :attr:`~method.__self__` will actually be the class itself, so that calling either ``x.f(1)`` or ``C.f(1)`` is equivalent to calling ``f(C,1)`` where ``f`` is the underlying function. -Note that the transformation from function object to instance method +Note that the transformation from :ref:`function object ` +to instance method object happens each time the attribute is retrieved from the instance. In some cases, a fruitful optimization is to assign the attribute to a local variable and call that local variable. Also notice that this @@ -774,6 +797,8 @@ set to ``None`` (but see the next item); :attr:`__module__` is the name of the module the function was defined in or ``None`` if unavailable. +.. _builtin-methods: + Built-in methods ^^^^^^^^^^^^^^^^ @@ -785,8 +810,9 @@ Built-in methods This is really a different disguise of a built-in function, this time containing an object passed to the C function as an implicit extra argument. An example of a built-in method is ``alist.append()``, assuming *alist* is a list object. In -this case, the special read-only attribute :attr:`__self__` is set to the object -denoted by *alist*. +this case, the special read-only attribute :attr:`!__self__` is set to the object +denoted by *alist*. (The attribute has the same semantics as it does with +:attr:`other instance methods `.) Classes @@ -901,8 +927,9 @@ https://www.python.org/download/releases/2.3/mro/. When a class attribute reference (for class :class:`!C`, say) would yield a class method object, it is transformed into an instance method object whose -:attr:`__self__` attribute is :class:`!C`. When it would yield a static -method object, it is transformed into the object wrapped by the static method +:attr:`~method.__self__` attribute is :class:`!C`. +When it would yield a :class:`staticmethod` object, +it is transformed into the object wrapped by the static method object. See section :ref:`descriptors` for another way in which attributes retrieved from a class may differ from those actually contained in its :attr:`~object.__dict__`. @@ -970,7 +997,7 @@ in which attribute references are searched. When an attribute is not found there, and the instance's class has an attribute by that name, the search continues with the class attributes. If a class attribute is found that is a user-defined function object, it is transformed into an instance method -object whose :attr:`__self__` attribute is the instance. Static method and +object whose :attr:`~method.__self__` attribute is the instance. Static method and class method objects are also transformed; see above under "Classes". See section :ref:`descriptors` for another way in which attributes of a class retrieved via its instances may differ from the objects actually stored in diff --git a/Doc/tutorial/classes.rst b/Doc/tutorial/classes.rst index 7b92e1a51b6e67..3bf138ca225ee5 100644 --- a/Doc/tutorial/classes.rst +++ b/Doc/tutorial/classes.rst @@ -769,8 +769,10 @@ data from a string buffer instead, and pass it as an argument. or arithmetic operators, and assigning such a "pseudo-file" to sys.stdin will not cause the interpreter to read further input from it.) -Instance method objects have attributes, too: ``m.__self__`` is the instance -object with the method :meth:`!m`, and ``m.__func__`` is the function object +:ref:`Instance method objects ` have attributes, too: +:attr:`m.__self__ ` is the instance +object with the method :meth:`!m`, and :attr:`m.__func__ ` is +the :ref:`function object ` corresponding to the method. diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index 8bdbb0fa352ed1..e8c1709c42abac 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -1678,8 +1678,8 @@ Some smaller changes made to the core Python language are: * Instance method objects have new attributes for the object and function comprising the method; the new synonym for :attr:`!im_self` is - :ref:`__self__ `, and :attr:`!im_func` is also available as - :ref:`__func__ `. + :attr:`~method.__self__`, and :attr:`!im_func` is also available as + :attr:`~method.__func__`. The old names are still supported in Python 2.6, but are gone in 3.0. * An obscure change: when you use the :func:`locals` function inside a diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index 162dd74637479a..cf6d26859bb6a2 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -858,9 +858,10 @@ Some smaller changes made to the core Python language are: .. XXX bytearray doesn't seem to be documented -* When using ``@classmethod`` and ``@staticmethod`` to wrap +* When using :class:`@classmethod ` and + :class:`@staticmethod ` to wrap methods as class or static methods, the wrapper object now - exposes the wrapped function as their :ref:`__func__ ` + exposes the wrapped function as their :attr:`~method.__func__` attribute. (Contributed by Amaury Forgeot d'Arc, after a suggestion by George Sakkis; :issue:`5982`.)
  • O3=@lHik4kW(~$h5zRB&Vr@ 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